🎉 Add background blur (#10034)

* 🎉 Add background blur

* 🎉 Add test

* 🎉 Add background blur info to plugins API

* 🎉 Suport in wasm for both layer and background blur

* 🐛 Fix failing test

* ♻️ Fix comments

---------

Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
This commit is contained in:
Eva Marco 2026-06-16 19:46:03 +02:00 committed by GitHub
parent b391a4c8d3
commit 2a098e5b16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 840 additions and 368 deletions

View File

@ -38,13 +38,10 @@
(d/concat-vec
[{:id "BackgroundImageFix" :type :image-fix}]
;; Background blur won't work in current SVG specification
;; We can revisit this in the future
#_(->> shape :blur (into []) (blur-filters :background-blur))
(->> shape :shadow (apply-filters :style :drop-shadow))
[{:id "shape" :type :blend-filters}]
(->> shape :shadow (apply-filters :style :inner-shadow))
(->> shape :blur list (apply-filters :type :layer-blur))))
(defn- calculate-filter-bounds
@ -100,17 +97,13 @@
;; shapes because selrect stores the unrotated rectangle, not the screen-space bbox.
(and (empty? (-> shape :shadow))
(or (nil? (:blur shape))
(not= :layer-blur (-> shape :blur :type))
(zero? (-> shape :blur :value (or 0)))))
(-> (dm/get-prop shape :points)
(grc/points->rect))
:else
(let [filters (shape->filters shape)
blur-value (case (-> shape :blur :type)
:layer-blur (or (-> shape :blur :value) 0)
:background-blur 0
0)
blur-value (or (-> shape :blur :value) 0)
srect (-> (dm/get-prop shape :points)
(grc/points->rect))]
(get-rect-filter-bounds srect filters blur-value ignore-shadow-margin?)))))
@ -242,10 +235,7 @@
(not (cfh/frame-shape? shape)) (or (:children-bounds shape)))
filters (shape->filters shape)
blur-value (case (-> shape :blur :type)
:layer-blur (or (-> shape :blur :value) 0)
:background-blur 0
0)]
blur-value (or (-> shape :blur :value) 0)]
(get-rect-filter-bounds children-bounds filters blur-value ignore-shadow-margin?))))

View File

@ -24,3 +24,7 @@
[shape scale]
(update-in shape [:blur :value] * scale))
(defn update-background-blur-scale
[shape scale]
(update-in shape [:background-blur :value] * scale))

View File

@ -91,6 +91,7 @@
:blend-mode :layer-effects-group
:shadow :shadow-group
:blur :blur-group
:background-blur :blur-group
:masked-group :mask-group
:constraints-h :constraints-group
:constraints-v :constraints-group

View File

@ -692,6 +692,9 @@
(some? (:blur shape))
(gse/update-blur-scale value)
(some? (:background-blur shape))
(gse/update-background-blur-scale value)
(ctl/flex-layout? shape)
(ctl/update-flex-scale value)

View File

@ -25,7 +25,7 @@
fills)))
(def group-style-properties
#{:shadow :blur})
#{:shadow :blur :background-blur})
;; FIXME: revisit
(def style-properties

View File

@ -24,6 +24,7 @@
[app.common.types.path :as path]
[app.common.types.plugins :as ctpg]
[app.common.types.shape.attrs :refer [default-color]]
[app.common.types.shape.background-blur :as ctsbb]
[app.common.types.shape.blur :as ctsb]
[app.common.types.shape.export :as ctse]
[app.common.types.shape.interactions :as ctsi]
@ -222,6 +223,7 @@
[:shadow {:optional true}
[:vector {:gen/max 1} ctss/schema:shadow]]
[:blur {:optional true} ctsb/schema:blur]
[:background-blur {:optional true} ctsbb/schema:background-blur]
[:grow-type {:optional true}
[::sm/one-of grow-types]]
[:applied-tokens {:optional true} cto/schema:applied-tokens]
@ -415,7 +417,7 @@
:remote-synced :shape-ref :touched :blocked :collapsed :locked
:hidden :masked-group :fills :proportion :proportion-lock :constraints-h
:constraints-v :fixed-scroll :r1 :r2 :r3 :r4 :rotation :opacity :grids :exports
:strokes :blend-mode :interactions :shadow :blur :grow-type :applied-tokens
:strokes :blend-mode :interactions :shadow :blur :background-blur :grow-type :applied-tokens
:plugin-data})
(def ^:private allowed-shape-geom-attrs #{:x :y :width :height})
@ -657,6 +659,7 @@
;; - Contraints
;; - Shadow
;; - Blur
;; - Background blur
;; - Border radius
(def ^:private basic-extract-props
#{:fills
@ -681,6 +684,7 @@
:shadow
:blur
:background-blur
;; Radius
:r1

View File

@ -32,6 +32,7 @@
:shadow
:blur
:background-blur
:fills
:fill-color
@ -108,6 +109,7 @@
:shadow
:blur
:background-blur
:exports
@ -166,6 +168,7 @@
:shadow
:blur
:background-blur
:exports
@ -223,6 +226,7 @@
:shadow
:blur
:background-blur
:exports
@ -280,6 +284,7 @@
:shadow
:blur
:background-blur
:exports
@ -336,6 +341,7 @@
:shadow
:blur
:background-blur
:typography-ref-id
:typography-ref-file
@ -398,6 +404,7 @@
:shadow
:blur
:background-blur
:exports
@ -456,6 +463,7 @@
:shadow
:blur
:background-blur
:exports
@ -513,6 +521,7 @@
:shadow
:blur
:background-blur
:exports

View File

@ -0,0 +1,16 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC Sucursal en España SL
(ns app.common.types.shape.background-blur
(:require
[app.common.schema :as sm]))
(def schema:background-blur
[:map {:title "BackgroundBlur"}
[:id ::sm/uuid]
[:type [:enum :background-blur]]
[:value ::sm/safe-number]
[:hidden :boolean]])

View File

@ -8,12 +8,9 @@
(:require
[app.common.schema :as sm]))
(def schema:blur-type
[:enum :layer-blur :background-blur])
(def schema:blur
[:map {:title "Blur"}
[:id ::sm/uuid]
[:type schema:blur-type]
[:type [:enum :layer-blur]]
[:value ::sm/safe-number]
[:hidden :boolean]])

View File

@ -123,12 +123,12 @@
"~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]]",
"~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,\"~:background-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-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,\"~:background-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]]",
"~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,\"~:background-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\"]]]",
@ -140,11 +140,11 @@
"~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-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,\"~:background-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]]"
"~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,\"~:background-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,\"~:background-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",

View File

@ -257,12 +257,26 @@ test.describe("Background blur", () => {
// Click the first Rectangle (which has background-blur type)
await workspace.clickLeafLayer("Rectangle");
const blurSection = workspace.page.getByRole("region", {
name: "Blur effects",
});
await expect(blurSection).toBeVisible();
// The blur type select should show "Background blur" as the current value
const blurTypeSelect = workspace.page
.getByTestId("blur-info")
.getByRole("combobox");
const blurTypeSelect = workspace.page.getByRole("combobox", {
name: "Blur type select",
});
await expect(blurTypeSelect).toBeVisible();
await expect(blurTypeSelect).toContainText("Background blur");
// Select first group layer, which has not blur effect
await workspace.layers.getByTestId("layer-row").nth(5).click();
await expect(blurTypeSelect).not.toBeVisible();
await blurSection.getByRole("button", { name: "Add blur" }).click();
await expect(blurTypeSelect).toBeVisible();
await expect(blurTypeSelect).toContainText("Layer blur");
});
test("Shows both layer-blur and background-blur options in the blur type dropdown", async ({
@ -279,25 +293,66 @@ test.describe("Background blur", () => {
});
await workspace.clickLeafLayer("Rectangle");
const blurSection = workspace.page.getByRole("region", {
name: "Blur effects",
});
await expect(blurSection).toBeVisible();
// Open the blur type dropdown
const blurTypeSelect = workspace.page
.getByTestId("blur-info")
.getByRole("combobox");
const blurTypeSelect = blurSection.getByRole("combobox", {
name: "Blur type select",
});
await expect(blurTypeSelect).toBeVisible();
await blurTypeSelect.click();
// Both options should be visible
const layerBlurOption = workspace.page.getByRole("option", {
const layerBlurOption = blurSection.getByRole("option", {
name: "Layer blur",
});
const backgroundBlurOption = workspace.page.getByRole("option", {
const backgroundBlurOption = blurSection.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 ({
test("Shape can have both layer blur and background blur effects at the same time", async ({
page,
}) => {
const workspace = new WasmWorkspacePage(page);
await workspace.mockConfigFlags(["enable-background-blur", tokenInputFlag]);
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");
const blurSection = workspace.page.getByRole("region", {
name: "Blur effects",
});
await expect(blurSection).toBeVisible();
const addBlurButton = blurSection.getByRole("button", { name: "Add blur" });
await expect(addBlurButton).toBeVisible();
await addBlurButton.click();
const blurTypeSelect = blurSection.getByRole("combobox", {
name: "Blur type select",
});
await expect(blurTypeSelect).toHaveCount(2);
const backgroundBlurLabel = blurSection.getByText("Background blur");
await expect(backgroundBlurLabel).toBeVisible();
const layerBlurLabel = blurSection.getByText("Layer blur");
await expect(layerBlurLabel).toBeVisible();
});
test("Show background blur disabled when flag is not active", async ({
page,
}) => {
const workspace = new WasmWorkspacePage(page);
@ -312,15 +367,42 @@ test.describe("Background blur", () => {
await workspace.clickLeafLayer("Rectangle");
// When there is no background blur flag the section has the old name "blur" instead of "blur effects"
const blurSection = workspace.page.getByRole("region", {
name: "Blur",
});
await expect(blurSection).toBeVisible();
// 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");
// Instead, a plain "Background blur" label is shown and more option button is disabled.
const blurTypeSelect = blurSection.getByRole("combobox", {
name: "Blur type select",
});
await expect(blurTypeSelect).not.toBeVisible();
const blurLabel = workspace.page.getByTestId("blur-info").getByText("Blur");
await expect(blurLabel).toBeVisible();
const backgroundBlurLabel = blurSection.getByText("Background blur");
await expect(backgroundBlurLabel).toBeVisible();
const showMoreOptionsButton = blurSection.getByRole("button", {
name: "Show/hide more options",
});
await expect(showMoreOptionsButton).toBeDisabled();
const showAndHideButton = blurSection.getByRole("button", {
name: "Toggle blur",
});
await expect(showAndHideButton).toBeDisabled();
const addBlurButton = blurSection.getByRole("button", { name: "Add blur" });
// We can add a layer blur, but not a background blur, and the type select should not appear
await expect(addBlurButton).toBeVisible();
await addBlurButton.click();
await expect(blurTypeSelect).not.toBeVisible();
await expect(backgroundBlurLabel).toBeVisible();
const blurLabel = blurSection.getByText("Blur", { exact: true });
await expect(blurLabel).toHaveCount(2);
});
});

View File

@ -73,6 +73,7 @@
:r4
:shadow
:blur
:background-blur
:strokes
:width
:height

View File

@ -281,7 +281,7 @@
(grc/fix-aspect-ratio aspect-ratio))
;; Bounds without shadows/blur will be the bounds of the thumbnail
bounds2 (gsb/get-object-bounds objects (dissoc frame :shadow :blur))
bounds2 (gsb/get-object-bounds objects (dissoc frame :shadow :blur :background-blur))
delta-bounds (gpt/point (:x bounds) (:y bounds))
vector (gpt/negate delta-bounds)

View File

@ -14,33 +14,32 @@
(mf/defc title-bar*
[{:keys [class collapsable collapsed title children
btn-icon btn-title add-icon-gap
title-class on-collapsed on-btn-click]}]
[:div {:class [(stl/css :title-bar)
class]}
title-class on-collapsed on-btn-click] :rest props}]
(let [props (mf/spread-props props {:class (stl/css :icon-text-btn)
:on-click on-collapsed})]
(if ^boolean collapsable
[:div {:class [(stl/css :title-wrapper) title-class]}
[:div {:class [(stl/css :title-bar) class]}
(if ^boolean collapsable
[:div {:class [(stl/css :title-wrapper) title-class]}
(let [icon-id (if collapsed "arrow-right" "arrow-down")]
[:> :button props
[:> icon* {:icon-id icon-id
:size "s"
:class (stl/css :icon)}]
[:div {:class (stl/css :title)} title]])]
(let [icon-id (if collapsed "arrow-right" "arrow-down")]
[:button {:class (stl/css :icon-text-btn)
:on-click on-collapsed}
[:> icon* {:icon-id icon-id
:size "s"
:class (stl/css :icon)}]
[:div {:class (stl/css :title)} title]])]
[:div {:class [(stl/css-case :title-only true
:title-only-icon-gap add-icon-gap)
title-class]}
title])
[:div {:class [(stl/css-case :title-only true
:title-only-icon-gap add-icon-gap)
title-class]}
title])
children
children
(when (some? on-btn-click)
[:> icon-button* {:variant "ghost"
:aria-label btn-title
:on-click on-btn-click
:icon btn-icon}])])
(when (some? on-btn-click)
[:> icon-button* {:variant "ghost"
:aria-label btn-title
:on-click on-btn-click
:icon btn-icon}])]))
(mf/defc inspect-title-bar*

View File

@ -25,6 +25,7 @@
display: grid;
align-items: center;
justify-content: flex-start;
text-align: start;
grid-auto-flow: column;
height: 100%;
min-height: deprecated.$s-32;

View File

@ -8,6 +8,8 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.config :as cf]
[app.main.features :as features]
[app.main.ui.components.copy-button :refer [copy-button*]]
[app.main.ui.components.title-bar :refer [inspect-title-bar*]]
[app.util.code-gen.style-css :as css]
@ -15,35 +17,53 @@
[rumext.v2 :as mf]))
(defn- has-blur? [shape]
(:blur shape))
(defn- blur-css-property [shape]
(if (= :background-blur (get-in shape [:blur :type]))
:backdrop-filter
:filter))
(or (:blur shape)
(:background-blur shape)))
(mf/defc blur-panel
[{:keys [objects shapes]}]
(let [shapes (->> shapes (filter has-blur?))]
(let [render-wasm? (features/use-feature "render-wasm/v1")
bg-blur? (and render-wasm?
(contains? cf/flags :background-blur))
shapes (->> shapes (filter #(has-blur? %)))
title (if bg-blur?
(tr "labels.blur-effects")
(tr "labels.blur"))]
(when (seq shapes)
[:div {:class (stl/css :attributes-block)}
[:> inspect-title-bar*
{:title (tr "inspect.attributes.blur")
{:title title
:class (stl/css :title-wrapper)
:title-class (stl/css :blur-attr-title)}
(when (= (count shapes) 1)
(let [prop (blur-css-property (first shapes))]
[:> copy-button* {:data (css/get-css-property objects (first shapes) prop)
:class (stl/css :copy-btn-title)}]))]
(let [background-blur (:background-blur (first shapes))
layer-blur (:blur (first shapes))]
(when background-blur
[:> copy-button* {:data (css/get-css-property objects (first shapes) :backdrop-filter)
:class (stl/css :copy-btn-title)}])
(when layer-blur
[:> copy-button* {:data (css/get-css-property objects (first shapes) :filter)
:class (stl/css :copy-btn-title)}])))]
[:div {:class (stl/css :attributes-content)}
(for [shape shapes]
(let [prop (blur-css-property shape)]
(let [background-blur (:background-blur (first shapes))
layer-blur (:blur (first shapes))]
[: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)]]]]))]])))
(when background-blur
[:div {:key (dm/str "block-" (:id shape) "-background-blur")}
[:div {:class (stl/css :global/attr-label)}
"Backdrop Filter"]
[:div {:class (stl/css :global/attr-value)}
[:> copy-button* {:data (css/get-css-property objects shape :backdrop-filter)}
[:div {:class (stl/css :button-children)}
(css/get-css-value objects shape :backdrop-filter)]]]])
(when layer-blur
[:div {:key (dm/str "block-" (:id shape) "-layer-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)]]]])]))]])))

View File

@ -51,7 +51,6 @@
:grid-column
:grid-row])
(def type->panel-group
{:multiple [:fill :stroke :text :shadow :blur :layout-element]
:frame [:visibility :geometry :fill :stroke :shadow :blur :layout :layout-element]
@ -72,7 +71,8 @@
(seq (:strokes shape)))
(defn- has-blur? [shape]
(:blur shape))
(or (:blur shape)
(:background-blur shape)))
(defn- has-text? [shape]
(:content shape))

View File

@ -18,12 +18,24 @@
[:div {:class (stl/css :blur-panel)}
(for [shape shapes]
[:div {:key (:id shape) :class (stl/css :blur-shape)}
(let [property :filter
value (css/get-css-value objects shape property)
property-name (cmm/get-css-rule-humanized property)
property-value (css/get-css-property objects shape property)]
[:> properties-row* {:key (dm/str "blur-property-" property)
:term property-name
:detail (dm/str value)
:property property-value
:copiable true}])])])
(let [blur-property :filter
blur-value (css/get-css-value objects shape blur-property)
background-blur-property :backdrop-filter
blur-property-name (cmm/get-css-rule-humanized blur-property)
blur-property-value (css/get-css-property objects shape blur-property)
background-blur-value (css/get-css-value objects shape background-blur-property)
background-blur-property-name (cmm/get-css-rule-humanized background-blur-property)
background-blur-property-value (css/get-css-property objects shape background-blur-property)]
[:div
(when blur-property-value
[:> properties-row* {:key (dm/str "blur-property-" blur-property)
:term blur-property-name
:detail (dm/str blur-value)
:property blur-property-value
:copiable true}])
(when background-blur-property-value
[:> properties-row* {:key (dm/str "blur-property-" background-blur-property)
:term background-blur-property-name
:detail (dm/str background-blur-value)
:property background-blur-property-value
:copiable true}])])])])

View File

@ -8,6 +8,8 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.config :as cf]
[app.main.features :as features]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
[app.util.clipboard :as clipboard]
@ -16,22 +18,27 @@
(defn- panel->title
[type]
(case type
:variant (tr "inspect.tabs.styles.variants-panel")
:token (tr "inspect.tabs.styles.token-panel")
:geometry (tr "inspect.attributes.size")
:fill (tr "labels.fill")
:stroke (tr "labels.stroke")
:text (tr "labels.text")
:blur (tr "labels.blur")
:shadow (tr "labels.shadow")
:layout (tr "labels.layout")
:flex-element "Flex Element"
:grid-element "Grid Element"
:layout-element "Layout Element"
:visibility (tr "labels.visibility")
:svg (tr "labels.svg")
nil))
(let [render-wasm? (features/use-feature "render-wasm/v1")
bg-blur? (and render-wasm?
(contains? cf/flags :background-blur))]
(case type
:variant (tr "inspect.tabs.styles.variants-panel")
:token (tr "inspect.tabs.styles.token-panel")
:geometry (tr "inspect.attributes.size")
:fill (tr "labels.fill")
:stroke (tr "labels.stroke")
:text (tr "labels.text")
:blur (if bg-blur?
(tr "labels.blur-effects")
(tr "labels.blur"))
:shadow (tr "labels.shadow")
:layout (tr "labels.layout")
:flex-element "Flex Element"
:grid-element "Grid Element"
:layout-element "Layout Element"
:visibility (tr "labels.visibility")
:svg (tr "labels.svg")
nil)))
(mf/defc style-box*
[{:keys [panel shorthand children]}]

View File

@ -183,6 +183,7 @@
([props shape position render-id]
(let [shape-fills (get shape :fills)
shape-shadow (get shape :shadow)
shape-blur (get shape :blur)
svg-attrs (get-svg-props shape render-id)

View File

@ -480,7 +480,8 @@
stroke-id (dm/str (dm/fmt "strokes-%-%" prefix shape-id))
shape-blur (get shape :blur)
shape-blur (get shape :blur)
shape-fills (get shape :fills)
shape-shadow (get shape :shadow)
shape-strokes (not-empty strokes)
@ -498,6 +499,7 @@
open-path? (and ^boolean (cfh/path-shape? shape)
^boolean (path/shape-with-open-path? shape))]
(when-not ^boolean (cfh/frame-shape? shape)
(when (and (some? shape-blur)
(not ^boolean (:hidden shape-blur)))

View File

@ -67,6 +67,7 @@
filter-id-blur (dm/fmt "filter-blur-%" render-id)
filter-id-shadows (dm/fmt "filter-shadow-%" render-id)
filter-str-blur (filters/filter-str filter-id-blur (dissoc shape :shadow))
filter-str-shadows (filters/filter-str filter-id-shadows (dissoc shape :blur))

View File

@ -14,146 +14,305 @@
[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.controls.numeric-input :refer [numeric-input*]]
[app.main.ui.ds.controls.select :refer [select*]]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.icons :as deprecated-icon]
[app.main.ui.ds.tooltip.tooltip :refer [tooltip*]]
[app.util.i18n :as i18n :refer [tr]]
[rumext.v2 :as mf]))
(def blur-attrs [:blur])
(def blur-attrs [:blur :background-blur])
(defn create-blur []
(defn create-blur [type]
(let [id (uuid/next)]
{:id id
:type :layer-blur
:type type
:value 4
:hidden false}))
(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))
(mf/defc blur-menu-content*
[{:keys [blur-key value change-fn blur-values]}]
(let [render-wasm? (features/use-feature "render-wasm/v1")
bg-blur? (and render-wasm?
(contains? cf/flags :background-blur))
is-hidden (get value :hidden)
show-more-options* (mf/use-state false)
show-more-options (deref show-more-options*)
toggle-more-options (mf/use-fn #(swap! show-more-options* not))
state* (mf/use-state {:show-content true
:show-more-options false})
handle-delete
(mf/use-fn
(mf/deps change-fn blur-key)
(fn []
(change-fn #(dissoc % blur-key))))
handle-toggle-visibility
(mf/use-fn
(mf/deps change-fn blur-key)
(fn []
(change-fn #(update-in % [blur-key :hidden] not))))
handle-change
(mf/use-fn
(mf/deps change-fn blur-key)
(fn [value]
(change-fn #(assoc-in % [blur-key :value] value))))
handle-type-change
(mf/use-fn
(mf/deps change-fn value blur-key)
(fn [type]
(let [type-kw (keyword type)
target-key (if (= type-kw :layer-blur) :blur :background-blur)]
(change-fn
(fn [shape]
(cond
;; mismo tipo
(= blur-key target-key)
shape
;; ya existe un blur del tipo destino
(contains? shape target-key)
shape
;; blur origen no existe
(not (contains? shape blur-key))
shape
:else
(let [blur (get shape blur-key)]
(-> shape
(dissoc blur-key)
(assoc target-key
(assoc blur :type type-kw))))))))))
bb-disabled? (and (= 2 (count blur-values))
(not= blur-key :background-blur))
lb-disabled? (and (= 2 (count blur-values))
(not= blur-key :blur))
label-ref (mf/use-ref nil)
type-options
[{:value "layer-blur" :disabled lb-disabled? :id "layer-blur" :label (tr "workspace.options.blur-options.layer-blur")}
{:value "background-blur" :disabled bb-disabled? :id "background-blur" :label (tr "workspace.options.blur-options.background-blur")}]
background-blur-disabled?
(and (= blur-key :background-blur)
(not bg-blur?))
label-text
(cond
(= blur-key :background-blur)
(tr "workspace.options.blur-options.background-blur")
bg-blur?
(tr "workspace.options.blur-options.layer-blur")
:else
(tr "labels.blur"))
label
(mf/html [:span {:aria-labelledby "background-blur-disabled-label"
:ref label-ref
:class (stl/css-case :label true
:disabled-label background-blur-disabled?)}
label-text])]
[:*
[:div {:class (stl/css-case :first-row true
:hidden is-hidden)}
[:div {:class (stl/css :blur-info)
:data-testid "blur-info"}
[:> icon-button* {:class (stl/css-case :show-more true
:selected show-more-options)
:on-click toggle-more-options
:selected show-more-options
:variant "ghost"
:disabled (or
is-hidden
(and (= blur-key :background-blur)
(= false bg-blur?)))
:aria-label (tr "workspace.options.blur-options.toggle-more-options")
:icon i/menu}]
(cond bg-blur?
[:> select*
{:class (stl/css :blur-type-select)
:default-selected (d/name (:type value))
:aria-label (tr "workspace.options.blur-options.blur-type-select")
:options type-options
:disabled is-hidden
:on-change handle-type-change}]
background-blur-disabled?
[:> tooltip*
{:trigger-ref label-ref
:id "background-blur-disabled-label"
:class (stl/css :disabled-label-tooltip)
:content (tr "workspace.options.blur-options.disabled-blur-label")}
label]
:else
label)]
[:div {:class (stl/css :actions)}
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.options.blur-options.toggle-blur")
:on-click handle-toggle-visibility
:disabled (and (= blur-key :background-blur)
(= false bg-blur?))
:tooltip-placement "top-left"
:icon (if (or is-hidden
(and (= blur-key :background-blur)
(= false bg-blur?))) i/hide i/shown)}]
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.options.blur-options.remove-blur")
:on-click handle-delete
:tooltip-placement "top-left"
:icon i/remove}]]]
(when show-more-options
[:div {:class (stl/css :second-row)}
[:> numeric-input*
{:class (stl/css :numeric-input)
:placeholder "--"
:min 0
:text-icon "value"
:on-change handle-change
:name "blur-value"
:value (:value value)}]])]))
(defn get-blurs [values]
(cond-> []
(:blur values)
(conj {:key :blur
:value (:blur values)})
(:background-blur values)
(conj {:key :background-blur
:value (:background-blur values)})))
(defn- check-props
"A blur-menu specific memoize check function that only checks if
specific values are changed on provided props. This allows pass the
whole shape as values without adding additional rerenders when other
shape properties changes."
[n-props o-props]
(and (identical? (unchecked-get n-props "ids")
(unchecked-get o-props "ids"))
(let [o-vals (unchecked-get o-props "values")
n-vals (unchecked-get n-props "values")
o-blur-values (get o-vals :blur)
n-blur-values (get n-vals :blur)
o-background-blur-values (get o-vals :background-blur)
n-background-blur-values (get n-vals :background-blur)]
(and (identical? o-blur-values n-blur-values)
(identical? o-background-blur-values n-background-blur-values)))))
(mf/defc blur-menu*
{::mf/wrap [#(mf/memo' % check-props)]}
[{:keys [ids type values]}]
(let [render-wasm? (features/use-feature "render-wasm/v1")
bg-blur? (and render-wasm?
(contains? cf/flags :background-blur))
blur-values (get-blurs values)
mixed-state (and (or (= :group type)
(= :multiple type))
(boolean
(some #(= :multiple (:value %)) blur-values)))
state* (mf/use-state {:show-content true})
state (deref state*)
open? (:show-content state)
more-options? (:show-more-options state)
toggle-content (mf/use-fn #(swap! state* update :show-content not))
toggle-more-options (mf/use-fn #(swap! state* update :show-more-options not))
hidden? (:hidden blur)
change!
(mf/use-fn
(mf/deps ids)
(fn [update-fn]
(st/emit! (dwsh/update-shapes ids update-fn))))
(st/emit! (dwsh/update-shapes ids update-fn)
(udw/trigger-bounding-box-cloaking ids))))
handle-delete-all
(mf/use-fn
(mf/deps change!)
(fn []
(change! #(dissoc % :blur :background-blur))))
handle-add
(mf/use-fn
(mf/deps change! ids)
(mf/deps change! blur-values)
(fn []
(st/emit! (udw/trigger-bounding-box-cloaking ids))
(change! #(assoc % :blur (create-blur)))))
(cond
(= 1 (count blur-values))
(let [existing-key (:key (first blur-values))
new-key (if (= existing-key :blur)
:background-blur
:blur)]
(change! #(assoc % new-key (create-blur (if (= :blur new-key)
:layer-blur
:background-blur)))))
(= 0 (count blur-values))
(change! #(assoc % :blur (create-blur :layer-blur))))
:else
blur-values))]
handle-delete
(mf/use-fn
(mf/deps change! ids)
(fn []
(st/emit! (udw/trigger-bounding-box-cloaking ids))
(change! #(dissoc % :blur))))
handle-change
(mf/use-fn
(mf/deps change! ids)
(fn [value]
(st/emit! (udw/trigger-bounding-box-cloaking ids))
(change! #(cond-> %
(not (contains? % :blur))
(assoc :blur (create-blur))
: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))))
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)}
[:section {:class (stl/css :element-set)
:hidden (not open?)
:aria-label (if bg-blur?
(tr "labels.blur-effects")
(tr "labels.blur"))}
[:div {:class (stl/css :element-title)}
[:> title-bar* {:collapsable has-value?
[:> title-bar* {:collapsable (seq blur-values)
:collapsed (not open?)
:on-collapsed toggle-content
:title (case type
:multiple (tr "workspace.options.blur-options.title.multiple")
:group (tr "workspace.options.blur-options.title.group")
(tr "workspace.options.blur-options.title"))
:class (stl/css-case :title-spacing-blur (not has-value?))}
(when-not has-value?
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.options.blur-options.add-blur")
:on-click handle-add
:icon i/add
:data-testid "add-blur"}])]]
(when (and open? has-value?)
[:div {:class (stl/css :element-set-content)}
[:div {:class (stl/css-case :first-row true
:hidden hidden?)}
[: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]
(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")
:on-click handle-toggle-visibility
:icon (if hidden? i/hide i/shown)}]
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.options.blur-options.remove-blur")
:on-click handle-delete
:icon i/remove}]]]
(when more-options?
[:div {:class (stl/css :second-row)}
[:label {:class (stl/css :label)
:for "blur-input-sidebar"}
(tr "inspect.attributes.blur.value")]
[:> numeric-input*
{:className (stl/css :numeric-input)
:placeholder "--"
:id "blur-input-sidebar"
:min "0"
:on-change handle-change
:value (:value blur)}]])])]))
:aria-expanded open?
:aria-controls "blur-content"
:title (if bg-blur?
(cond
(= type :multiple) (tr "workspace.options.blur-effects-options.title.multiple")
(= type :group) (tr "workspace.options.blur-effects-options.title.group")
:else (tr "labels.blur-effects"))
(cond
(= type :multiple) (tr "workspace.options.blur-options.title.multiple")
(= type :group) (tr "workspace.options.blur-options.title.group")
:else (tr "labels.blur")))
:class (stl/css-case :title-spacing-blur (not (seq blur-values))
:long-title true)}
(when (and (not mixed-state)
(if bg-blur?
(< (count blur-values) 2)
(nil? (:blur values))))
[:> icon-button*
{:variant "ghost"
:aria-label (tr "workspace.options.blur-options.add-blur")
:on-click handle-add
:icon i/add
:tooltip-placement "top-left"
:data-testid "add-blur"}])]]
(when (and open? (seq blur-values))
[:div {:class (stl/css :element-set-content)
:hidden (not open?)
:id "blur-content"}
(if mixed-state
[:div {:class (stl/css :first-row)}
[:span {:class (stl/css :mixed-label)}
(tr "labels.mixed-values")]
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.options.blur-options.remove-blur")
:on-click handle-delete-all
:tooltip-placement "top-left"
:icon i/remove}]]
(for [{:keys [key value]} blur-values]
[:> blur-menu-content*
{:key key
:blur-key key
:value value
:blur-values blur-values
:change-fn change!}]))])]))

View File

@ -4,114 +4,130 @@
//
// Copyright (c) KALEIDOS INC Sucursal en España SL
@use "refactor/common-refactor.scss" as deprecated;
@use "ds/_borders.scss" as *;
@use "ds/typography.scss" as t;
@use "ds/_sizes.scss" as *;
@use "ds/_utils.scss" as *;
@use "../../../sidebar/common/sidebar.scss" as sidebar;
.element-set {
@include sidebar.option-grid-structure;
}
.element-title {
grid-column: span 8;
}
.title-spacing-blur {
padding-left: deprecated.$s-2;
padding-left: var(--sp-xxs);
margin: 0;
}
.element-set-content {
@include deprecated.flex-column;
.long-title {
height: auto;
}
margin-bottom: deprecated.$s-8;
.element-set-content {
display: flex;
flex-direction: column;
gap: var(--sp-xs);
margin-bottom: var(--sp-s);
}
.first-row {
@include sidebar.option-grid-structure;
}
.blur-info {
grid-column: span 6;
display: flex;
align-items: center;
gap: deprecated.$s-1;
flex-grow: 1;
border-radius: deprecated.$br-8;
background-color: var(--input-details-color);
.show-more {
background-color: var(--color-background-tertiary);
border: $b-1 solid var(--color-background-tertiary);
border-radius: $br-8 0 0 $br-8;
.show-more {
@extend %button-secondary;
height: deprecated.$s-32;
width: deprecated.$s-28;
border-radius: deprecated.$br-8 0 0 deprecated.$br-8;
box-sizing: border-box;
border: deprecated.$s-1 solid var(--button-secondary-background-color-rest);
svg {
@extend %button-icon;
}
&.selected {
background-color: var(--button-radio-background-color-active);
svg {
stroke: var(--button-radio-foreground-color-active);
}
}
}
.label {
@include deprecated.body-small-typography;
flex-grow: 1;
display: flex;
align-items: center;
height: deprecated.$s-32;
padding: 0 deprecated.$s-8;
border-radius: 0 deprecated.$br-8 deprecated.$br-8 0;
background-color: var(--input-background-color);
color: var(--menu-foreground-color);
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;
}
&:hover {
background-color: var(--color-background-quaternary);
border: $b-1 solid var(--color-background-quaternary);
}
.actions {
@include deprecated.flex-row;
&:disabled {
background-color: var(--color-background-primary);
border-block-start: $b-1 solid var(--color-background-quaternary);
border-block-end: $b-1 solid var(--color-background-quaternary);
border-inline-start: $b-1 solid var(--color-background-quaternary);
}
}
&.hidden {
.blur-info {
@include deprecated.hidden-element;
.blur-info {
grid-column: span 6;
display: flex;
align-items: center;
flex-grow: 1;
gap: 1px;
border-radius: $br-8;
background-color: var(--color-background-primary);
}
.show-more {
@include deprecated.hidden-element;
.mixed-label {
@include t.use-typography("body-small");
border: deprecated.$s-1 solid var(--input-border-color-disabled);
}
grid-column: span 7;
gap: 1px;
border-radius: $br-8;
display: flex;
align-items: center;
flex-grow: 1;
height: $sz-32;
padding: 0 var(--sp-s);
background-color: var(--color-background-tertiary);
border: $b-1 solid var(--color-background-tertiary);
color: var(--color-foreground-primary);
box-sizing: border-box;
}
.label {
@include deprecated.hidden-element;
.disabled-label-tooltip {
flex-grow: 1;
}
border: deprecated.$s-1 solid var(--input-border-color-disabled);
}
}
}
.label {
@include t.use-typography("body-small");
display: flex;
align-items: center;
flex-grow: 1;
height: $sz-32;
padding: 0 var(--sp-s);
border-radius: 0 $br-8 $br-8 0;
background-color: var(--color-background-tertiary);
border: $b-1 solid var(--color-background-tertiary);
color: var(--color-foreground-primary);
box-sizing: border-box;
}
.blur-type-select {
flex-grow: 1;
border-radius: 0 $br-8 $br-8 0;
}
.actions {
display: flex;
align-items: center;
gap: var(--sp-xs);
}
.hidden .blur-info,
.hidden .blur-info .label {
cursor: default;
pointer-events: none;
box-sizing: border-box;
color: var(--color-foreground-secondary);
stroke: var(--color-foreground-secondary);
background-color: transparent;
}
.hidden .blur-info .label {
border: $b-1 solid var(--color-background-quaternary);
}
.second-row {
@extend %input-element;
@include deprecated.body-small-typography;
width: deprecated.$s-92;
.label {
padding-left: deprecated.$s-8;
width: deprecated.$s-60;
}
width: var(--three-columns-width);
}
.disabled-label {
color: var(--color-foreground-secondary);
background-color: var(--color-background-primary);
border-top: $b-1 solid var(--color-background-quaternary);
border-bottom: $b-1 solid var(--color-background-quaternary);
border-right: $b-1 solid var(--color-background-quaternary);
border-left: $b-1 solid transparent;
}

View File

@ -451,5 +451,5 @@
[:> icon-button* {:variant "ghost"
:aria-label (tr "settings.select-this-color")
:on-click handle-select
:tooltip-position "top-left"
:tooltip-placement "top-left"
:icon i/move}])]))

View File

@ -135,7 +135,7 @@
[:> shadow-menu* {:ids ids :values (get shape :shadow)}]
[:> blur-menu* {:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> exports-menu* {:type type
:ids ids

View File

@ -132,7 +132,7 @@
:applied-tokens applied-tokens}]
[:> shadow-menu* {:ids ids :values (get shape :shadow)}]
[:> blur-menu* {:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> svg-attrs-menu* {:ids ids
:values (select-keys shape [:svg-attrs])}]
[:> exports-menu* {:type type

View File

@ -159,7 +159,7 @@
:libraries libraries}]
[:> shadow-menu* {:ids ids :values (get shape :shadow)}]
[:> blur-menu* {:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> frame-grid* {:shape shape}]
[:> exports-menu* {:type shape-type
:ids ids

View File

@ -48,6 +48,7 @@
:fill :shape
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :children
:exports :shape
@ -61,6 +62,7 @@
:fill :children
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :children
:text :children
:exports :shape
@ -74,6 +76,7 @@
:fill :shape
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :ignore
:exports :shape
@ -87,6 +90,7 @@
:fill :text
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :text
:exports :shape
@ -100,6 +104,7 @@
:fill :ignore
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :ignore
:text :ignore
:exports :shape
@ -113,6 +118,7 @@
:fill :shape
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :ignore
:exports :shape
@ -126,6 +132,7 @@
:fill :shape
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :ignore
:exports :shape
@ -139,6 +146,7 @@
:fill :shape
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :ignore
:exports :shape
@ -152,6 +160,7 @@
:fill :shape
:shadow :shape
:blur :shape
:background-blur :shape
:stroke :shape
:text :ignore
:exports :shape

View File

@ -133,7 +133,7 @@
:applied-tokens applied-tokens}]
[:> shadow-menu* {:ids ids :values (get shape :shadow)}]
[:> blur-menu* {:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> svg-attrs-menu* {:ids ids
:values (select-keys shape [:svg-attrs])}]
[:> exports-menu* {:type type

View File

@ -134,7 +134,7 @@
[:> shadow-menu* {:ids ids :values (get shape :shadow)}]
[:> blur-menu* {:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> svg-attrs-menu* {:ids ids
:values (select-keys shape [:svg-attrs])}]

View File

@ -201,7 +201,7 @@
[:> shadow-menu* {:ids ids :values (get shape :shadow)}]
[:> blur-menu* {:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> svg-attrs-menu* {:ids ids
:values (select-keys shape [:svg-attrs])}]

View File

@ -211,7 +211,7 @@
[:> blur-menu*
{:ids ids
:values (select-keys shape [:blur])}]
:values (select-keys shape [:blur :background-blur])}]
[:> exports-menu* {:type type
:ids ids

View File

@ -232,16 +232,14 @@
;; export interface Blur {
;; id?: string;
;; type?: 'layer-blur';
;; value?: number;
;; hidden?: boolean;
;; }
(defn format-blur
[{:keys [id type value hidden] :as blur}]
[{:keys [id value hidden] :as blur}]
(when (some? blur)
(obj/without-empty
#js {:id (format-id id)
:type (format-key type)
:value value
:hidden hidden})))

View File

@ -237,7 +237,6 @@
;; export interface Blur {
;; id?: string;
;; type?: 'layer-blur';
;; value?: number;
;; hidden?: boolean;
;; }
@ -246,7 +245,6 @@
(when (some? blur)
(d/without-nils
{:id (-> (obj/get blur "id") parse-id)
:type (-> (obj/get blur "type") parse-keyword)
:value (obj/get blur "value")
:hidden (obj/get blur "hidden")})))

View File

@ -24,6 +24,7 @@
[app.common.types.grid :as ctg]
[app.common.types.path :as path]
[app.common.types.shape :as cts]
[app.common.types.shape.background-blur :as ctsbb]
[app.common.types.shape.blur :as ctsb]
[app.common.types.shape.export :as ctse]
[app.common.types.shape.interactions :as ctsi]
@ -176,6 +177,15 @@
:hidden false}
blur))
(defn- background-blur-defaults
[blur]
(d/patch-object
{:id (uuid/next)
:type :background-blur
:value 4
:hidden false}
blur))
(defn commit-fills!
[plugin-id ^js self value]
(let [shape (u/proxy->shape self)
@ -499,7 +509,8 @@
(if (nil? value)
(st/emit! (dwsh/update-shapes [id] #(dissoc % :blur)))
(let [id (obj/get self "$id")
value (blur-defaults (parser/parse-blur value))]
value (blur-defaults (parser/parse-blur value))
value (assoc value :type :layer-blur)]
(cond
(not (sm/validate ctsb/schema:blur value))
(u/not-valid plugin-id :blur value)
@ -510,6 +521,26 @@
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :blur value)))))))}
:background-blur
{:this true
:get #(-> % u/proxy->shape :background-blur format/format-blur)
:set
(fn [self value]
(if (nil? value)
(st/emit! (dwsh/update-shapes [id] #(dissoc % :background-blur)))
(let [id (obj/get self "$id")
value (background-blur-defaults (parser/parse-blur value))
value (assoc value :type :background-blur)]
(cond
(not (sm/validate ctsbb/schema:background-blur value))
(u/not-valid plugin-id :background-blur value)
(not (r/check-permission plugin-id "content:write"))
(u/not-valid plugin-id :background-blur "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dwsh/update-shapes [id] #(assoc % :background-blur value)))))))}
:exports
{:this true
:get #(-> % u/proxy->shape :exports format/format-exports)

View File

@ -959,12 +959,21 @@
(defn set-shape-blur
[blur]
(if (some? blur)
(let [type (-> blur :type sr/translate-blur-type)
hidden (:hidden blur)
value (:value blur)]
(h/call wasm/internal-module "_set_shape_blur" type hidden value))
(h/call wasm/internal-module "_clear_shape_blur")))
(let [type (sr/translate-blur-type :layer-blur)]
(if (some? blur)
(let [hidden (:hidden blur)
value (:value blur)]
(h/call wasm/internal-module "_set_shape_blur" type hidden value))
(h/call wasm/internal-module "_clear_shape_blur" type))))
(defn set-shape-background-blur
[background-blur]
(let [type (sr/translate-blur-type :background-blur)]
(if (some? background-blur)
(let [hidden (:hidden background-blur)
value (:value background-blur)]
(h/call wasm/internal-module "_set_shape_blur" type hidden value))
(h/call wasm/internal-module "_clear_shape_blur" type))))
(defn set-shape-corners
[corners]
@ -1346,6 +1355,7 @@
bool-type (get shape :bool-type)
grow-type (get shape :grow-type)
blur (get shape :blur)
background-blur (get shape :background-blur)
svg-attrs (get shape :svg-attrs)
shadows (get shape :shadow)]
@ -1354,6 +1364,7 @@
;; Remaining properties that need separate calls (variable-length or conditional)
(set-shape-children children)
(set-shape-blur blur)
(set-shape-background-blur background-blur)
(when (= type :group)
(set-masked (boolean masked)))
(when (= type :bool)

View File

@ -187,6 +187,9 @@
:blur
(api/set-shape-blur v)
:background-blur
(api/set-shape-background-blur v)
:shadow
(api/set-shape-shadows v)
@ -230,6 +233,7 @@
;; Always update fills/blur/shadow to clear previous state if filters disappear
(api/set-shape-fills id (:fills shape) false)
(api/set-shape-blur (:blur shape))
(api/set-shape-background-blur (:background-blur shape))
(api/set-shape-shadows (:shadow shape)))
:masked-group

View File

@ -45,7 +45,6 @@
(set! context-initialized? false)
(reset! context-lost? false))
(defonce serializers
#js {:blur-type shared/RawBlurType
:blend-mode shared/RawBlendMode

View File

@ -38,7 +38,7 @@
:border-color :border-color
:box-shadow :shadows
:filter :blur
:backdrop-filter :blur
:backdrop-filter :background-blur
:gap :size-array
:row-gap :size-array
:column-gap :size-array
@ -202,5 +202,6 @@
:tracks (format-tracks value)
:shadows (format-shadow value options)
:blur (format-blur value)
:background-blur (format-blur value)
:matrix (format-matrix value)
(if (keyword? value) (d/name value) value))))

View File

@ -256,14 +256,12 @@
(defn- get-filter
[shape]
(when-not (cgc/svg-markup? shape)
(when (= :layer-blur (get-in shape [:blur :type]))
(get-in shape [:blur :value]))))
(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]))))
(get-in shape [:background-blur :value])))
(defn- get-display
[shape]

View File

@ -190,7 +190,6 @@
(t/testing " - blur"
(set! (.-blur shape) #js {:value 10})
(t/is (= (-> (. shape -blur) (aget "type")) "layer-blur"))
(t/is (= (-> (. shape -blur) (aget "value")) 10))
(t/is (= (-> (. shape -blur) (aget "hidden")) false))
(let [id (-> (. shape -blur) (aget "id") uuid/uuid)]

View File

@ -2755,6 +2755,10 @@ msgstr "Bad Gateway"
msgid "labels.blur"
msgstr "Blur"
#: src/app/main/ui/inspect/styles/style_box.cljs
msgid "labels.blur-effects"
msgstr "Blur effects"
#: src/app/main/data/common.cljs:119, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:543, src/app/main/ui/dashboard/team.cljs:866, src/app/main/ui/dashboard/team.cljs:1345, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:167, src/app/main/ui/settings/integrations.cljs:228, src/app/main/ui/viewer/share_link.cljs:204, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:242
msgid "labels.cancel"
msgstr "Cancel"
@ -7177,10 +7181,30 @@ msgstr "Layer blur"
msgid "workspace.options.blur-options.remove-blur"
msgstr "Remove blur"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-options.toggle-more-options"
msgstr "Show/hide more options"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-options.disabled-blur-label"
msgstr "Background blur is only supported in the new render. Switch to the new render in the Preferences menu to use this effect"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:113, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:138
msgid "workspace.options.blur-options.title"
msgstr "Blur"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-effects-options.title"
msgstr "Blur effects"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-effects-options.title.group"
msgstr "Group blur effects"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-effects-options.title.multiple"
msgstr "Selection blur effects"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:112
msgid "workspace.options.blur-options.title.group"
msgstr "Group blur"
@ -7193,6 +7217,10 @@ msgstr "Selection blur"
msgid "workspace.options.blur-options.toggle-blur"
msgstr "Toggle blur"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-options.blur-type-select"
msgstr "Blur type select"
#: src/app/main/ui/workspace/sidebar/options/page.cljs:60, src/app/main/ui/workspace/sidebar/options/page.cljs:68
msgid "workspace.options.canvas-background"
msgstr "Canvas background"

View File

@ -2685,6 +2685,10 @@ msgstr "Error del servidor (Bad Gateway)"
msgid "labels.blur"
msgstr "Desenfoque"
#: src/app/main/ui/inspect/styles/style_box.cljs
msgid "labels.blur-effects"
msgstr "Efectos de desenfoque"
#: src/app/main/data/common.cljs:119, src/app/main/ui/dashboard/change_owner.cljs:67, src/app/main/ui/dashboard/change_owner.cljs:174, src/app/main/ui/dashboard/import.cljs:543, src/app/main/ui/dashboard/team.cljs:866, src/app/main/ui/dashboard/team.cljs:1345, src/app/main/ui/delete_shared.cljs:36, src/app/main/ui/exports/assets.cljs:163, src/app/main/ui/exports/files.cljs:167, src/app/main/ui/settings/integrations.cljs:228, src/app/main/ui/viewer/share_link.cljs:204, src/app/main/ui/workspace/sidebar/assets/groups.cljs:178, src/app/main/ui/workspace/tokens/export/modal.cljs:43, src/app/main/ui/workspace/tokens/import/modal.cljs:268, src/app/main/ui/workspace/tokens/import_from_library.cljs:88, src/app/main/ui/workspace/tokens/management/forms/generic_form.cljs:364, src/app/main/ui/workspace/tokens/management/forms/rename_node_modal.cljs:73, src/app/main/ui/workspace/tokens/settings/menu.cljs:104, src/app/main/ui/workspace/tokens/themes/create_modal.cljs:242
msgid "labels.cancel"
msgstr "Cancelar"
@ -7004,10 +7008,30 @@ msgstr "Desenfoque de capa"
msgid "workspace.options.blur-options.remove-blur"
msgstr "Eliminar desenfoque"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-options.toggle-more-options"
msgstr "Mostrar/ocultar más opciones"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-options.disabled-blur-label"
msgstr "El desenfoque de fondo (Background Blur) solo está disponible en el nuevo motor de renderizado. Actívalo desde el menú de Preferencias para utilizar este efecto."
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:113, src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:138
msgid "workspace.options.blur-options.title"
msgstr "Desenfoque"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-effects-options.title"
msgstr "Efectos de desenfoque"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-effects-options.title.group"
msgstr "Efectos de desenfoque del grupo"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-effects-options.title.multiple"
msgstr "Efectos de desenfoque de la selección"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs:112
msgid "workspace.options.blur-options.title.group"
msgstr "Desenfoque del grupo"
@ -7020,6 +7044,10 @@ msgstr "Desenfoque de la selección"
msgid "workspace.options.blur-options.toggle-blur"
msgstr "Mostrar/ocultar desenfoque"
#: src/app/main/ui/workspace/sidebar/options/menus/blur.cljs
msgid "workspace.options.blur-options.blur-type-select"
msgstr "Selector de tipo de desenfoque"
#: src/app/main/ui/workspace/sidebar/options/page.cljs:60, src/app/main/ui/workspace/sidebar/options/page.cljs:68
msgid "workspace.options.canvas-background"
msgstr "Color de fondo"

View File

@ -194,11 +194,6 @@ export interface Blur {
* The optional unique identifier for the blur effect.
*/
id?: string;
/**
* The optional type of the blur effect.
* Currently, only 'layer-blur' is supported.
*/
type?: 'layer-blur';
/**
* The optional intensity value of the blur effect.
*/
@ -3717,6 +3712,13 @@ export interface ShapeBase extends PluginData {
*/
blur?: Blur;
/**
* The background blur effect applied to the shape.
* Background blur creates a blur effect on the content behind the shape,
* rather than on the shape's own content.
*/
backgroundBlur?: Blur;
/**
* The export settings of the shape.
*/

View File

@ -649,10 +649,7 @@ impl RenderState {
{
return;
}
let blur = match shape
.blur
.filter(|b| !b.hidden && b.blur_type == BlurType::BackgroundBlur)
{
let blur = match shape.visible_background_blur() {
Some(blur) => blur,
None => return,
};
@ -1208,6 +1205,7 @@ impl RenderState {
&& parent_shadows.is_none()
&& !shape.needs_layer()
&& shape.blur.is_none()
&& shape.background_blur.is_none()
&& !has_inherited_blur
&& shape.shadows.is_empty()
&& shape.transform.is_identity()
@ -1317,20 +1315,9 @@ 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 !skip_effects
&& 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);
}
// Background blur is stored separately (shape.background_blur) and is
// rendered before the save_layer in render_background_blur(), so here
// shape.blur only ever holds a layer blur.
let frame_has_blur = Self::frame_clip_layer_blur(&shape).is_some();
let shape_has_blur = shape.blur.is_some();
@ -2838,6 +2825,7 @@ impl RenderState {
plain_shape_mut.clear_shadows();
plain_shape_mut.blur = None;
plain_shape_mut.background_blur = None;
// Shadow rendering uses a single render_shape call with no render_shape_exit,
// so strokes must be drawn here. Disable clip_content to avoid skip_strokes
@ -3641,10 +3629,8 @@ impl RenderState {
// 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)
})
tree.get(id)
.is_some_and(|s| s.visible_background_blur().is_some())
});
// We only need first level shapes, in the same order as the parent node.

View File

@ -188,6 +188,7 @@ pub struct Shape {
pub blend_mode: BlendMode,
pub vertical_align: VerticalAlign,
pub blur: Option<Blur>,
pub background_blur: Option<Blur>,
pub opacity: f32,
pub hidden: bool,
pub svg: Option<skia::svg::Dom>,
@ -291,6 +292,7 @@ impl Shape {
opacity: 1.,
hidden: false,
blur: None,
background_blur: None,
svg: None,
svg_attrs: None,
shadows: Vec::with_capacity(1),
@ -314,6 +316,10 @@ impl Shape {
blur.scale_content(value);
}
if let Some(background_blur) = self.background_blur.as_mut() {
background_blur.scale_content(value);
}
self.layout_item
.iter_mut()
.for_each(|i| i.scale_content(value));
@ -631,6 +637,15 @@ impl Shape {
self.blur = blur;
}
pub fn set_background_blur(&mut self, blur: Option<Blur>) {
self.invalidate_extrect();
self.background_blur = blur;
}
pub fn visible_background_blur(&self) -> Option<Blur> {
self.background_blur.filter(|blur| !blur.hidden)
}
pub fn add_child(&mut self, id: Uuid) {
self.children.push(id);
}
@ -1421,6 +1436,7 @@ impl Shape {
}
self.blur.is_none()
&& self.background_blur.is_none()
&& self.shadows.is_empty()
&& (self.opacity - 1.0).abs() <= 1e-4
&& self.blend_mode().0 == skia::BlendMode::SrcOver
@ -1665,7 +1681,7 @@ impl Shape {
return false;
}
if self.blur.is_some() {
if self.blur.is_some() || self.background_blur.is_some() {
return false;
}
@ -1805,6 +1821,38 @@ mod tests {
)
}
#[test]
fn layer_blur_and_background_blur_can_coexist() {
let mut shape = any_shape();
let layer_blur = Blur::new(BlurType::LayerBlur, false, 4.0);
let background_blur = Blur::new(BlurType::BackgroundBlur, false, 8.0);
shape.set_blur(Some(layer_blur));
shape.set_background_blur(Some(background_blur));
assert_eq!(shape.blur, Some(layer_blur));
assert_eq!(shape.background_blur, Some(background_blur));
assert_eq!(shape.visible_background_blur(), Some(background_blur));
// Clearing one type must not affect the other.
shape.set_blur(None);
assert_eq!(shape.blur, None);
assert_eq!(shape.background_blur, Some(background_blur));
shape.set_blur(Some(layer_blur));
shape.set_background_blur(None);
assert_eq!(shape.blur, Some(layer_blur));
assert_eq!(shape.background_blur, None);
}
#[test]
fn hidden_background_blur_is_not_visible() {
let mut shape = any_shape();
shape.set_background_blur(Some(Blur::new(BlurType::BackgroundBlur, true, 8.0)));
assert_eq!(shape.visible_background_blur(), None);
}
#[test]
fn test_set_corners() {
let mut shape = any_shape();

View File

@ -29,14 +29,21 @@ impl From<RawBlurType> for BlurType {
#[no_mangle]
pub extern "C" fn set_shape_blur(blur_type: u8, hidden: bool, value: f32) {
with_current_shape_mut!(state, |shape: &mut Shape| {
let blur_type = RawBlurType::from(blur_type);
shape.set_blur(Some(Blur::new(blur_type.into(), hidden, value)));
let blur_type: BlurType = RawBlurType::from(blur_type).into();
let blur = Some(Blur::new(blur_type, hidden, value));
match blur_type {
BlurType::LayerBlur => shape.set_blur(blur),
BlurType::BackgroundBlur => shape.set_background_blur(blur),
}
});
}
#[no_mangle]
pub extern "C" fn clear_shape_blur() {
pub extern "C" fn clear_shape_blur(blur_type: u8) {
with_current_shape_mut!(state, |shape: &mut Shape| {
shape.set_blur(None);
match RawBlurType::from(blur_type).into() {
BlurType::LayerBlur => shape.set_blur(None),
BlurType::BackgroundBlur => shape.set_background_blur(None),
}
});
}