diff --git a/frontend/playwright/data/render-wasm/get-file-inner-stroke-overlap-seam.json b/frontend/playwright/data/render-wasm/get-file-inner-stroke-overlap-seam.json new file mode 100644 index 0000000000..e499f027dc --- /dev/null +++ b/frontend/playwright/data/render-wasm/get-file-inner-stroke-overlap-seam.json @@ -0,0 +1,769 @@ +{ + "~:features": { + "~#set": [ + "fdata/path-data", + "plugins/runtime", + "design-tokens/v1", + "variants/v1", + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "render-wasm/v1", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:team-id": "~uaaa00001-0001-0001-8007-000000000003", + "~:permissions": { + "~:type": "~:membership", + "~:is-owner": true, + "~:is-admin": true, + "~:can-edit": true, + "~:can-read": true, + "~:is-logged": true + }, + "~:has-media-trimmed": false, + "~:comment-thread-seqn": 0, + "~:name": "inner-stroke-overlap-seam", + "~:revn": 1, + "~:modified-at": "~m1771855365377", + "~:vern": 0, + "~:id": "~uaaa00001-0001-0001-8007-000000000001", + "~:is-shared": false, + "~:migrations": { + "~#ordered-set": [ + "legacy-2", + "legacy-3", + "legacy-5", + "legacy-6", + "legacy-7", + "legacy-8", + "legacy-9", + "legacy-10", + "legacy-11", + "legacy-12", + "legacy-13", + "legacy-14", + "legacy-16", + "legacy-17", + "legacy-18", + "legacy-19", + "legacy-25", + "legacy-26", + "legacy-27", + "legacy-28", + "legacy-29", + "legacy-31", + "legacy-32", + "legacy-33", + "legacy-34", + "legacy-36", + "legacy-37", + "legacy-38", + "legacy-39", + "legacy-40", + "legacy-41", + "legacy-42", + "legacy-43", + "legacy-44", + "legacy-45", + "legacy-46", + "legacy-47", + "legacy-48", + "legacy-49", + "legacy-50", + "legacy-51", + "legacy-52", + "legacy-53", + "legacy-54", + "legacy-55", + "legacy-56", + "legacy-57", + "legacy-59", + "legacy-62", + "legacy-65", + "legacy-66", + "legacy-67", + "0001-remove-tokens-from-groups", + "0002-normalize-bool-content-v2", + "0002-clean-shape-interactions", + "0003-fix-root-shape", + "0003-convert-path-content-v2", + "0005-deprecate-image-type", + "0006-fix-old-texts-fills", + "0008-fix-library-colors-v4", + "0009-clean-library-colors", + "0009-add-partial-text-touched-flags", + "0010-fix-swap-slots-pointing-non-existent-shapes", + "0011-fix-invalid-text-touched-flags", + "0012-fix-position-data", + "0013-fix-component-path", + "0013-clear-invalid-strokes-and-fills", + "0014-fix-tokens-lib-duplicate-ids", + "0014-clear-components-nil-objects", + "0015-fix-text-attrs-blank-strings", + "0015-clean-shadow-color", + "0016-copy-fills-from-position-data-to-text-node" + ] + }, + "~:version": 67, + "~:project-id": "~uaaa00001-0001-0001-8007-000000000004", + "~:created-at": "~m1771591980210", + "~:backend": "legacy-db", + "~:data": { + "~:pages": [ + "~uaaa00001-0001-0001-8007-000000000002" + ], + "~:pages-index": { + "~uaaa00001-0001-0001-8007-000000000002": { + "~:objects": { + "~u00000000-0000-0000-0000-000000000000": { + "~#shape": { + "~:y": 0, + "~:hide-fill-on-export": false, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "Root Frame", + "~:width": 0.01, + "~:type": "~:frame", + "~:points": [ + {"~#point": {"~:x": 0, "~:y": 0}}, + {"~#point": {"~:x": 0.01, "~:y": 0}}, + {"~#point": {"~:x": 0.01, "~:y": 0.01}}, + {"~#point": {"~:x": 0, "~:y": 0.01}} + ], + "~:r2": 0, + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 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, + "~:r4": 0, + "~:selrect": {"~#rect": {"~:x": 0, "~:y": 0, "~:width": 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, + "~:height": 0.01, + "~:flip-y": null, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146992e4ab5"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146992e4ab5": { + "~#shape": { + "~:y": -237, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg", + "~:width": 86.215, + "~:type": "~:group", + "~:svg-attrs": {"~:width": "86.215", "~:height": "50"}, + "~:points": [ + {"~#point": {"~:x": 1079, "~:y": -237}}, + {"~#point": {"~:x": 1165.215, "~:y": -237}}, + {"~#point": {"~:x": 1165.215, "~:y": -187}}, + {"~#point": {"~:x": 1079, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146992e4ab5", + "~:parent-id": "~u00000000-0000-0000-0000-000000000000", + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1079, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1079, "~:y": -237, "~:width": 86.215, "~:height": 50, "~:x1": 1079, "~:y1": -237, "~:x2": 1165.215, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": false, + "~:height": 50, + "~:flip-y": false, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146992f0a36"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146992f0a36": { + "~#shape": { + "~:y": -237, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg-g", + "~:width": 86.215, + "~:type": "~:group", + "~:svg-attrs": {}, + "~:points": [ + {"~#point": {"~:x": 1079, "~:y": -237}}, + {"~#point": {"~:x": 1165.215, "~:y": -237}}, + {"~#point": {"~:x": 1165.215, "~:y": -187}}, + {"~#point": {"~:x": 1079, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992e4ab5", + "~:svg-viewbox": {"~#rect": {"~:x": -0.000034146222333220067, "~:y": -0.000003814697265625, "~:width": 86.21503414622225, "~:height": 50.000003814697266, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1079, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1079, "~:y": -237, "~:width": 86.215, "~:height": 50, "~:x1": 1079, "~:y1": -237, "~:x2": 1165.215, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": 50, + "~:flip-y": null, + "~:shapes": [ + "~u8f89d4e8-0efd-804d-8007-b146992fbb39", + "~u8f89d4e8-0efd-804d-8007-b146992fde2e", + "~u8f89d4e8-0efd-804d-8007-b14699303fd3", + "~u8f89d4e8-0efd-804d-8007-b1469930583b", + "~u8f89d4e8-0efd-804d-8007-b146993083e1", + "~u8f89d4e8-0efd-804d-8007-b1469930dc39", + "~u8f89d4e8-0efd-804d-8007-b146993141e4", + "~u8f89d4e8-0efd-804d-8007-b146993141e5", + "~u8f89d4e8-0efd-804d-8007-b1469931a9d0", + "~u8f89d4e8-0efd-804d-8007-b1469931a9d1" + ] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146992fbb39": { + "~#shape": { + "~:y": null, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAADGGIhEq6p9wxpukESrqn3DcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:className": "fills"}, + "~:points": [ + {"~#point": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727}}, + {"~#point": {"~:x": 1131.7299204883832, "~:y": -237.00000381469727}}, + {"~#point": {"~:x": 1131.7299204883832, "~:y": -187}}, + {"~#point": {"~:x": 1112.4849232616168, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146992fbb39", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 33.484923261616885, "~:y": -0.000003814697265625, "~:width": 19.24499722676625, "~:height": 50.000003814697266, "~:x1": 33.484923261616885, "~:y1": -0.000003814697265625, "~:x2": 52.72992048838314, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:inner", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727, "~:width": 19.24499722676625, "~:height": 50.000003814697266, "~:x1": 1112.4849232616168, "~:y1": -237.00000381469727, "~:x2": 1131.729920488383, "~:y2": -187}}, + "~:fills": [{"~:fill-color": "#70d19d", "~:fill-opacity": 1}], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b146992fde2e": { + "~#shape": { + "~:y": -237, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg-g", + "~:width": 19.244997226766372, + "~:type": "~:group", + "~:svg-attrs": {"~:className": "strokes"}, + "~:points": [ + {"~#point": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727}}, + {"~#point": {"~:x": 1131.7299204883832, "~:y": -237.00000381469727}}, + {"~#point": {"~:x": 1131.7299204883832, "~:y": -187}}, + {"~#point": {"~:x": 1112.4849232616168, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146992fde2e", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 33.484923261616814, "~:y": -0.000003814697265625, "~:width": 19.244997226766372, "~:height": 50.000003814697266, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1112.4849232616168, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727, "~:width": 19.244997226766372, "~:height": 50.000003814697266, "~:x1": 1112.4849232616168, "~:y1": -237.00000381469727, "~:x2": 1131.7299204883832, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": 50, + "~:flip-y": null, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146992fde2f"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146992fde2f": { + "~#shape": { + "~:y": null, + "~:stroke-cap-start": "~:round", + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAADGGIhEq6p9wxpukESrqn3DcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"}, + "~:points": [ + {"~#point": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727}}, + {"~#point": {"~:x": 1131.7299204883832, "~:y": -237.00000381469727}}, + {"~#point": {"~:x": 1131.7299204883832, "~:y": -187}}, + {"~#point": {"~:x": 1112.4849232616168, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:stroke-cap-end": "~:round", + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:stroke-linecap": "~:round", + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146992fde2f", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992fde2e", + "~:svg-viewbox": {"~#rect": {"~:x": 33.484923261616885, "~:y": -0.000003814697265625, "~:width": 19.24499722676625, "~:height": 50.000003814697266, "~:x1": 33.484923261616885, "~:y1": -0.000003814697265625, "~:x2": 52.72992048838314, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:inner", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727, "~:width": 19.24499722676625, "~:height": 50.000003814697266, "~:x1": 1112.4849232616168, "~:y1": -237.00000381469727, "~:x2": 1131.729920488383, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b14699303fd3": { + "~#shape": { + "~:y": null, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAABBBpFEthF7w+aGlUSc+ELDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:className": "fills"}, + "~:points": [ + {"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1165.2148778962223, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1165.2148778962223, "~:y": -187}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b14699303fd3", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 19.66923798963697, "~:width": 43.107456021222276, "~:height": 30.33076201036303, "~:x1": 43.107421875, "~:y1": 19.66923798963697, "~:x2": 86.21487789622228, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:inner", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -217.33076201036303, "~:width": 43.107456021222276, "~:height": 30.33076201036303, "~:x1": 1122.107421875, "~:y1": -217.33076201036303, "~:x2": 1165.2148778962223, "~:y2": -187}}, + "~:fills": [{"~:fill-color": "#3c49ff", "~:fill-opacity": 1}], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b1469930583b": { + "~#shape": { + "~:y": -217.33076201036303, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg-g", + "~:width": 43.10745602122233, + "~:type": "~:group", + "~:svg-attrs": {"~:className": "strokes"}, + "~:points": [ + {"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1165.2148778962223, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1165.2148778962223, "~:y": -187}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b1469930583b", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 19.66923798963697, "~:width": 43.10745602122233, "~:height": 30.33076201036303, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1122.107421875, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -217.33076201036303, "~:width": 43.10745602122233, "~:height": 30.33076201036303, "~:x1": 1122.107421875, "~:y1": -217.33076201036303, "~:x2": 1165.2148778962223, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": 30.33076201036303, + "~:flip-y": null, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146993083e0"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146993083e0": { + "~#shape": { + "~:y": null, + "~:stroke-cap-start": "~:round", + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAABBBpFEthF7w+aGlUSc+ELDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"}, + "~:points": [ + {"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1165.2148778962223, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1165.2148778962223, "~:y": -187}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:stroke-cap-end": "~:round", + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:stroke-linecap": "~:round", + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146993083e0", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b1469930583b", + "~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 19.66923798963697, "~:width": 43.107456021222276, "~:height": 30.33076201036303, "~:x1": 43.107421875, "~:y1": 19.66923798963697, "~:x2": 86.21487789622228, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:inner", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -217.33076201036303, "~:width": 43.107456021222276, "~:height": 30.33076201036303, "~:x1": 1122.107421875, "~:y1": -217.33076201036303, "~:x2": 1165.2148778962223, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b146993083e1": { + "~#shape": { + "~:y": null, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAAD6/4JEnPhCw5+Ah0S2EXvDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:className": "fills"}, + "~:points": [ + {"~#point": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}}, + {"~#point": {"~:x": 1078.9999658537777, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146993083e1", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": -0.00003414622229014341, "~:y": 19.669237989636976, "~:width": 43.10745602122229, "~:height": 30.330762010363024, "~:x1": -0.00003414622229014341, "~:y1": 19.669237989636976, "~:x2": 43.107421875, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:inner", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303, "~:width": 43.10745602122229, "~:height": 30.330762010363024, "~:x1": 1078.9999658537777, "~:y1": -217.33076201036303, "~:x2": 1122.107421875, "~:y2": -187}}, + "~:fills": [{"~:fill-color": "#3c49ff", "~:fill-opacity": 1}], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b1469930dc39": { + "~#shape": { + "~:y": -217.33076201036303, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg-g", + "~:width": 43.10745602122233, + "~:type": "~:group", + "~:svg-attrs": {"~:className": "strokes"}, + "~:points": [ + {"~#point": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}}, + {"~#point": {"~:x": 1078.9999658537777, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b1469930dc39", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": -0.000034146222333220067, "~:y": 19.66923798963697, "~:width": 43.10745602122233, "~:height": 30.33076201036303, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1079, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303, "~:width": 43.10745602122233, "~:height": 30.33076201036303, "~:x1": 1078.9999658537777, "~:y1": -217.33076201036303, "~:x2": 1122.107421875, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": 30.33076201036303, + "~:flip-y": null, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146993110ec"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146993110ec": { + "~#shape": { + "~:y": null, + "~:stroke-cap-start": "~:round", + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAAD6/4JEnPhCw5+Ah0S2EXvDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"}, + "~:points": [ + {"~#point": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}}, + {"~#point": {"~:x": 1078.9999658537777, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:stroke-cap-end": "~:round", + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:stroke-linecap": "~:round", + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146993110ec", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b1469930dc39", + "~:svg-viewbox": {"~#rect": {"~:x": -0.00003414622229014341, "~:y": 19.669237989636976, "~:width": 43.10745602122229, "~:height": 30.330762010363024, "~:x1": -0.00003414622229014341, "~:y1": 19.669237989636976, "~:x2": 43.107421875, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:inner", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303, "~:width": 43.10745602122229, "~:height": 30.330762010363024, "~:x1": 1078.9999658537777, "~:y1": -217.33076201036303, "~:x2": 1122.107421875, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b146993141e4": { + "~#shape": { + "~:y": null, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAAAgmoxEQb6Cw03qk0RQhmXDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:className": "fills"}, + "~:points": [ + {"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1149.9259006903171, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1149.9259006903171, "~:y": -187}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146993141e4", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 5.330620727377042, "~:width": 27.818478815317206, "~:height": 44.66937927262296, "~:x1": 43.107421875, "~:y1": 5.330620727377042, "~:x2": 70.9259006903172, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:inner", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -231.66937927262296, "~:width": 27.818478815317206, "~:height": 44.66937927262296, "~:x1": 1122.107421875, "~:y1": -231.66937927262296, "~:x2": 1149.9259006903171, "~:y2": -187}}, + "~:fills": [{"~:fill-color": "#434cc2", "~:fill-opacity": 1}], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b146993141e5": { + "~#shape": { + "~:y": -231.66937927262296, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg-g", + "~:width": 27.81847881531712, + "~:type": "~:group", + "~:svg-attrs": {"~:className": "strokes"}, + "~:points": [ + {"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1149.9259006903171, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1149.9259006903171, "~:y": -187}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146993141e5", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 5.330620727377038, "~:width": 27.81847881531712, "~:height": 44.66937927262296, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1122.107421875, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -231.66937927262296, "~:width": 27.81847881531712, "~:height": 44.66937927262296, "~:x1": 1122.107421875, "~:y1": -231.66937927262296, "~:x2": 1149.9259006903171, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": 44.66937927262296, + "~:flip-y": null, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146993141e6"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146993141e6": { + "~#shape": { + "~:y": null, + "~:stroke-cap-start": "~:round", + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAAAgmoxEQb6Cw03qk0RQhmXDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"}, + "~:points": [ + {"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1149.9259006903171, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1149.9259006903171, "~:y": -187}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:stroke-cap-end": "~:round", + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:stroke-linecap": "~:round", + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146993141e6", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146993141e5", + "~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 5.330620727377042, "~:width": 27.818478815317206, "~:height": 44.66937927262296, "~:x1": 43.107421875, "~:y1": 5.330620727377042, "~:x2": 70.9259006903172, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:inner", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -231.66937927262296, "~:width": 27.818478815317206, "~:height": 44.66937927262296, "~:x1": 1122.107421875, "~:y1": -231.66937927262296, "~:x2": 1149.9259006903171, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b1469931a9d0": { + "~#shape": { + "~:y": null, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAACTnIREUIZlw8Dsi0RBvoLDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:className": "fills"}, + "~:points": [ + {"~#point": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}}, + {"~#point": {"~:x": 1094.2889430596829, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b1469931a9d0", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 15.28894305968281, "~:y": 5.330620727377026, "~:width": 27.818478815317192, "~:height": 44.669379272622976, "~:x1": 15.28894305968281, "~:y1": 5.330620727377026, "~:x2": 43.107421875, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:inner", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296, "~:width": 27.818478815317192, "~:height": 44.669379272622976, "~:x1": 1094.2889430596829, "~:y1": -231.66937927262296, "~:x2": 1122.107421875, "~:y2": -187}}, + "~:fills": [{"~:fill-color": "#434cc2", "~:fill-opacity": 1}], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b1469931a9d1": { + "~#shape": { + "~:y": -231.66937927262296, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg-g", + "~:width": 27.81847881531712, + "~:type": "~:group", + "~:svg-attrs": {"~:className": "strokes"}, + "~:points": [ + {"~#point": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}}, + {"~#point": {"~:x": 1094.2889430596829, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b1469931a9d1", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 15.288943059682879, "~:y": 5.330620727377038, "~:width": 27.81847881531712, "~:height": 44.66937927262296, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1094.2889430596829, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296, "~:width": 27.81847881531712, "~:height": 44.66937927262296, "~:x1": 1094.2889430596829, "~:y1": -231.66937927262296, "~:x2": 1122.107421875, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": 44.66937927262296, + "~:flip-y": null, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b1469931e08c"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b1469931e08c": { + "~#shape": { + "~:y": null, + "~:stroke-cap-start": "~:round", + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAACTnIREUIZlw8Dsi0RBvoLDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"}, + "~:points": [ + {"~#point": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}}, + {"~#point": {"~:x": 1094.2889430596829, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:stroke-cap-end": "~:round", + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:stroke-linecap": "~:round", + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b1469931e08c", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b1469931a9d1", + "~:svg-viewbox": {"~#rect": {"~:x": 15.28894305968281, "~:y": 5.330620727377026, "~:width": 27.818478815317192, "~:height": 44.669379272622976, "~:x1": 15.28894305968281, "~:y1": 5.330620727377026, "~:x2": 43.107421875, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:inner", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296, "~:width": 27.818478815317192, "~:height": 44.669379272622976, "~:x1": 1094.2889430596829, "~:y1": -231.66937927262296, "~:x2": 1122.107421875, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + } + }, + "~:id": "~uaaa00001-0001-0001-8007-000000000002", + "~:name": "Page 1" + } + }, + "~:id": "~uaaa00001-0001-0001-8007-000000000001", + "~:options": { + "~:components-v2": true, + "~:base-font-size": "16px" + } + } +} diff --git a/frontend/playwright/data/render-wasm/get-file-outer-stroke-overlap-seam.json b/frontend/playwright/data/render-wasm/get-file-outer-stroke-overlap-seam.json new file mode 100644 index 0000000000..2f2904618a --- /dev/null +++ b/frontend/playwright/data/render-wasm/get-file-outer-stroke-overlap-seam.json @@ -0,0 +1,769 @@ +{ + "~:features": { + "~#set": [ + "fdata/path-data", + "plugins/runtime", + "design-tokens/v1", + "variants/v1", + "layout/grid", + "styles/v2", + "fdata/pointer-map", + "fdata/objects-map", + "render-wasm/v1", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:team-id": "~uaaa00001-0001-0001-8007-000000000003", + "~:permissions": { + "~:type": "~:membership", + "~:is-owner": true, + "~:is-admin": true, + "~:can-edit": true, + "~:can-read": true, + "~:is-logged": true + }, + "~:has-media-trimmed": false, + "~:comment-thread-seqn": 0, + "~:name": "outer-stroke-overlap-seam", + "~:revn": 1, + "~:modified-at": "~m1771855365377", + "~:vern": 0, + "~:id": "~uaaa00001-0001-0001-8007-000000000001", + "~:is-shared": false, + "~:migrations": { + "~#ordered-set": [ + "legacy-2", + "legacy-3", + "legacy-5", + "legacy-6", + "legacy-7", + "legacy-8", + "legacy-9", + "legacy-10", + "legacy-11", + "legacy-12", + "legacy-13", + "legacy-14", + "legacy-16", + "legacy-17", + "legacy-18", + "legacy-19", + "legacy-25", + "legacy-26", + "legacy-27", + "legacy-28", + "legacy-29", + "legacy-31", + "legacy-32", + "legacy-33", + "legacy-34", + "legacy-36", + "legacy-37", + "legacy-38", + "legacy-39", + "legacy-40", + "legacy-41", + "legacy-42", + "legacy-43", + "legacy-44", + "legacy-45", + "legacy-46", + "legacy-47", + "legacy-48", + "legacy-49", + "legacy-50", + "legacy-51", + "legacy-52", + "legacy-53", + "legacy-54", + "legacy-55", + "legacy-56", + "legacy-57", + "legacy-59", + "legacy-62", + "legacy-65", + "legacy-66", + "legacy-67", + "0001-remove-tokens-from-groups", + "0002-normalize-bool-content-v2", + "0002-clean-shape-interactions", + "0003-fix-root-shape", + "0003-convert-path-content-v2", + "0005-deprecate-image-type", + "0006-fix-old-texts-fills", + "0008-fix-library-colors-v4", + "0009-clean-library-colors", + "0009-add-partial-text-touched-flags", + "0010-fix-swap-slots-pointing-non-existent-shapes", + "0011-fix-invalid-text-touched-flags", + "0012-fix-position-data", + "0013-fix-component-path", + "0013-clear-invalid-strokes-and-fills", + "0014-fix-tokens-lib-duplicate-ids", + "0014-clear-components-nil-objects", + "0015-fix-text-attrs-blank-strings", + "0015-clean-shadow-color", + "0016-copy-fills-from-position-data-to-text-node" + ] + }, + "~:version": 67, + "~:project-id": "~uaaa00001-0001-0001-8007-000000000004", + "~:created-at": "~m1771591980210", + "~:backend": "legacy-db", + "~:data": { + "~:pages": [ + "~uaaa00001-0001-0001-8007-000000000002" + ], + "~:pages-index": { + "~uaaa00001-0001-0001-8007-000000000002": { + "~:objects": { + "~u00000000-0000-0000-0000-000000000000": { + "~#shape": { + "~:y": 0, + "~:hide-fill-on-export": false, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "Root Frame", + "~:width": 0.01, + "~:type": "~:frame", + "~:points": [ + {"~#point": {"~:x": 0, "~:y": 0}}, + {"~#point": {"~:x": 0.01, "~:y": 0}}, + {"~#point": {"~:x": 0.01, "~:y": 0.01}}, + {"~#point": {"~:x": 0, "~:y": 0.01}} + ], + "~:r2": 0, + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 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, + "~:r4": 0, + "~:selrect": {"~#rect": {"~:x": 0, "~:y": 0, "~:width": 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, + "~:height": 0.01, + "~:flip-y": null, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146992e4ab5"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146992e4ab5": { + "~#shape": { + "~:y": -237, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg", + "~:width": 86.215, + "~:type": "~:group", + "~:svg-attrs": {"~:width": "86.215", "~:height": "50"}, + "~:points": [ + {"~#point": {"~:x": 1079, "~:y": -237}}, + {"~#point": {"~:x": 1165.215, "~:y": -237}}, + {"~#point": {"~:x": 1165.215, "~:y": -187}}, + {"~#point": {"~:x": 1079, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146992e4ab5", + "~:parent-id": "~u00000000-0000-0000-0000-000000000000", + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1079, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1079, "~:y": -237, "~:width": 86.215, "~:height": 50, "~:x1": 1079, "~:y1": -237, "~:x2": 1165.215, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": false, + "~:height": 50, + "~:flip-y": false, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146992f0a36"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146992f0a36": { + "~#shape": { + "~:y": -237, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg-g", + "~:width": 86.215, + "~:type": "~:group", + "~:svg-attrs": {}, + "~:points": [ + {"~#point": {"~:x": 1079, "~:y": -237}}, + {"~#point": {"~:x": 1165.215, "~:y": -237}}, + {"~#point": {"~:x": 1165.215, "~:y": -187}}, + {"~#point": {"~:x": 1079, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992e4ab5", + "~:svg-viewbox": {"~#rect": {"~:x": -0.000034146222333220067, "~:y": -0.000003814697265625, "~:width": 86.21503414622225, "~:height": 50.000003814697266, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1079, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1079, "~:y": -237, "~:width": 86.215, "~:height": 50, "~:x1": 1079, "~:y1": -237, "~:x2": 1165.215, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": 50, + "~:flip-y": null, + "~:shapes": [ + "~u8f89d4e8-0efd-804d-8007-b146992fbb39", + "~u8f89d4e8-0efd-804d-8007-b146992fde2e", + "~u8f89d4e8-0efd-804d-8007-b14699303fd3", + "~u8f89d4e8-0efd-804d-8007-b1469930583b", + "~u8f89d4e8-0efd-804d-8007-b146993083e1", + "~u8f89d4e8-0efd-804d-8007-b1469930dc39", + "~u8f89d4e8-0efd-804d-8007-b146993141e4", + "~u8f89d4e8-0efd-804d-8007-b146993141e5", + "~u8f89d4e8-0efd-804d-8007-b1469931a9d0", + "~u8f89d4e8-0efd-804d-8007-b1469931a9d1" + ] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146992fbb39": { + "~#shape": { + "~:y": null, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAADGGIhEq6p9wxpukESrqn3DcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:className": "fills"}, + "~:points": [ + {"~#point": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727}}, + {"~#point": {"~:x": 1131.7299204883832, "~:y": -237.00000381469727}}, + {"~#point": {"~:x": 1131.7299204883832, "~:y": -187}}, + {"~#point": {"~:x": 1112.4849232616168, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146992fbb39", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 33.484923261616885, "~:y": -0.000003814697265625, "~:width": 19.24499722676625, "~:height": 50.000003814697266, "~:x1": 33.484923261616885, "~:y1": -0.000003814697265625, "~:x2": 52.72992048838314, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727, "~:width": 19.24499722676625, "~:height": 50.000003814697266, "~:x1": 1112.4849232616168, "~:y1": -237.00000381469727, "~:x2": 1131.729920488383, "~:y2": -187}}, + "~:fills": [{"~:fill-color": "#70d19d", "~:fill-opacity": 1}], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b146992fde2e": { + "~#shape": { + "~:y": -237, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg-g", + "~:width": 19.244997226766372, + "~:type": "~:group", + "~:svg-attrs": {"~:className": "strokes"}, + "~:points": [ + {"~#point": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727}}, + {"~#point": {"~:x": 1131.7299204883832, "~:y": -237.00000381469727}}, + {"~#point": {"~:x": 1131.7299204883832, "~:y": -187}}, + {"~#point": {"~:x": 1112.4849232616168, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146992fde2e", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 33.484923261616814, "~:y": -0.000003814697265625, "~:width": 19.244997226766372, "~:height": 50.000003814697266, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1112.4849232616168, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727, "~:width": 19.244997226766372, "~:height": 50.000003814697266, "~:x1": 1112.4849232616168, "~:y1": -237.00000381469727, "~:x2": 1131.7299204883832, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": 50, + "~:flip-y": null, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146992fde2f"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146992fde2f": { + "~#shape": { + "~:y": null, + "~:stroke-cap-start": "~:round", + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAADGGIhEq6p9wxpukESrqn3DcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"}, + "~:points": [ + {"~#point": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727}}, + {"~#point": {"~:x": 1131.7299204883832, "~:y": -237.00000381469727}}, + {"~#point": {"~:x": 1131.7299204883832, "~:y": -187}}, + {"~#point": {"~:x": 1112.4849232616168, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:stroke-cap-end": "~:round", + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:stroke-linecap": "~:round", + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146992fde2f", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992fde2e", + "~:svg-viewbox": {"~#rect": {"~:x": 33.484923261616885, "~:y": -0.000003814697265625, "~:width": 19.24499722676625, "~:height": 50.000003814697266, "~:x1": 33.484923261616885, "~:y1": -0.000003814697265625, "~:x2": 52.72992048838314, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1112.4849232616168, "~:y": -237.00000381469727, "~:width": 19.24499722676625, "~:height": 50.000003814697266, "~:x1": 1112.4849232616168, "~:y1": -237.00000381469727, "~:x2": 1131.729920488383, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b14699303fd3": { + "~#shape": { + "~:y": null, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAABBBpFEthF7w+aGlUSc+ELDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:className": "fills"}, + "~:points": [ + {"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1165.2148778962223, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1165.2148778962223, "~:y": -187}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b14699303fd3", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 19.66923798963697, "~:width": 43.107456021222276, "~:height": 30.33076201036303, "~:x1": 43.107421875, "~:y1": 19.66923798963697, "~:x2": 86.21487789622228, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -217.33076201036303, "~:width": 43.107456021222276, "~:height": 30.33076201036303, "~:x1": 1122.107421875, "~:y1": -217.33076201036303, "~:x2": 1165.2148778962223, "~:y2": -187}}, + "~:fills": [{"~:fill-color": "#3c49ff", "~:fill-opacity": 1}], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b1469930583b": { + "~#shape": { + "~:y": -217.33076201036303, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg-g", + "~:width": 43.10745602122233, + "~:type": "~:group", + "~:svg-attrs": {"~:className": "strokes"}, + "~:points": [ + {"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1165.2148778962223, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1165.2148778962223, "~:y": -187}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b1469930583b", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 19.66923798963697, "~:width": 43.10745602122233, "~:height": 30.33076201036303, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1122.107421875, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -217.33076201036303, "~:width": 43.10745602122233, "~:height": 30.33076201036303, "~:x1": 1122.107421875, "~:y1": -217.33076201036303, "~:x2": 1165.2148778962223, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": 30.33076201036303, + "~:flip-y": null, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146993083e0"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146993083e0": { + "~#shape": { + "~:y": null, + "~:stroke-cap-start": "~:round", + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAABBBpFEthF7w+aGlUSc+ELDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"}, + "~:points": [ + {"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1165.2148778962223, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1165.2148778962223, "~:y": -187}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:stroke-cap-end": "~:round", + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:stroke-linecap": "~:round", + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146993083e0", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b1469930583b", + "~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 19.66923798963697, "~:width": 43.107456021222276, "~:height": 30.33076201036303, "~:x1": 43.107421875, "~:y1": 19.66923798963697, "~:x2": 86.21487789622228, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -217.33076201036303, "~:width": 43.107456021222276, "~:height": 30.33076201036303, "~:x1": 1122.107421875, "~:y1": -217.33076201036303, "~:x2": 1165.2148778962223, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b146993083e1": { + "~#shape": { + "~:y": null, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAAD6/4JEnPhCw5+Ah0S2EXvDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:className": "fills"}, + "~:points": [ + {"~#point": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}}, + {"~#point": {"~:x": 1078.9999658537777, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146993083e1", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": -0.00003414622229014341, "~:y": 19.669237989636976, "~:width": 43.10745602122229, "~:height": 30.330762010363024, "~:x1": -0.00003414622229014341, "~:y1": 19.669237989636976, "~:x2": 43.107421875, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303, "~:width": 43.10745602122229, "~:height": 30.330762010363024, "~:x1": 1078.9999658537777, "~:y1": -217.33076201036303, "~:x2": 1122.107421875, "~:y2": -187}}, + "~:fills": [{"~:fill-color": "#3c49ff", "~:fill-opacity": 1}], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b1469930dc39": { + "~#shape": { + "~:y": -217.33076201036303, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg-g", + "~:width": 43.10745602122233, + "~:type": "~:group", + "~:svg-attrs": {"~:className": "strokes"}, + "~:points": [ + {"~#point": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}}, + {"~#point": {"~:x": 1078.9999658537777, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b1469930dc39", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": -0.000034146222333220067, "~:y": 19.66923798963697, "~:width": 43.10745602122233, "~:height": 30.33076201036303, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1079, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303, "~:width": 43.10745602122233, "~:height": 30.33076201036303, "~:x1": 1078.9999658537777, "~:y1": -217.33076201036303, "~:x2": 1122.107421875, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": 30.33076201036303, + "~:flip-y": null, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146993110ec"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146993110ec": { + "~#shape": { + "~:y": null, + "~:stroke-cap-start": "~:round", + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAAD6/4JEnPhCw5+Ah0S2EXvDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"}, + "~:points": [ + {"~#point": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -217.33076201036303}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}}, + {"~#point": {"~:x": 1078.9999658537777, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:stroke-cap-end": "~:round", + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:stroke-linecap": "~:round", + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146993110ec", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b1469930dc39", + "~:svg-viewbox": {"~#rect": {"~:x": -0.00003414622229014341, "~:y": 19.669237989636976, "~:width": 43.10745602122229, "~:height": 30.330762010363024, "~:x1": -0.00003414622229014341, "~:y1": 19.669237989636976, "~:x2": 43.107421875, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1078.9999658537777, "~:y": -217.33076201036303, "~:width": 43.10745602122229, "~:height": 30.330762010363024, "~:x1": 1078.9999658537777, "~:y1": -217.33076201036303, "~:x2": 1122.107421875, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b146993141e4": { + "~#shape": { + "~:y": null, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAAAgmoxEQb6Cw03qk0RQhmXDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:className": "fills"}, + "~:points": [ + {"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1149.9259006903171, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1149.9259006903171, "~:y": -187}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146993141e4", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 5.330620727377042, "~:width": 27.818478815317206, "~:height": 44.66937927262296, "~:x1": 43.107421875, "~:y1": 5.330620727377042, "~:x2": 70.9259006903172, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -231.66937927262296, "~:width": 27.818478815317206, "~:height": 44.66937927262296, "~:x1": 1122.107421875, "~:y1": -231.66937927262296, "~:x2": 1149.9259006903171, "~:y2": -187}}, + "~:fills": [{"~:fill-color": "#434cc2", "~:fill-opacity": 1}], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b146993141e5": { + "~#shape": { + "~:y": -231.66937927262296, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg-g", + "~:width": 27.81847881531712, + "~:type": "~:group", + "~:svg-attrs": {"~:className": "strokes"}, + "~:points": [ + {"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1149.9259006903171, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1149.9259006903171, "~:y": -187}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146993141e5", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 5.330620727377038, "~:width": 27.81847881531712, "~:height": 44.66937927262296, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1122.107421875, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -231.66937927262296, "~:width": 27.81847881531712, "~:height": 44.66937927262296, "~:x1": 1122.107421875, "~:y1": -231.66937927262296, "~:x2": 1149.9259006903171, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": 44.66937927262296, + "~:flip-y": null, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b146993141e6"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b146993141e6": { + "~#shape": { + "~:y": null, + "~:stroke-cap-start": "~:round", + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAAAgmoxEQb6Cw03qk0RQhmXDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"}, + "~:points": [ + {"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1149.9259006903171, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1149.9259006903171, "~:y": -187}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:stroke-cap-end": "~:round", + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:stroke-linecap": "~:round", + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b146993141e6", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146993141e5", + "~:svg-viewbox": {"~#rect": {"~:x": 43.107421875, "~:y": 5.330620727377042, "~:width": 27.818478815317206, "~:height": 44.66937927262296, "~:x1": 43.107421875, "~:y1": 5.330620727377042, "~:x2": 70.9259006903172, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1122.107421875, "~:y": -231.66937927262296, "~:width": 27.818478815317206, "~:height": 44.66937927262296, "~:x1": 1122.107421875, "~:y1": -231.66937927262296, "~:x2": 1149.9259006903171, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b1469931a9d0": { + "~#shape": { + "~:y": null, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAACTnIREUIZlw8Dsi0RBvoLDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:className": "fills"}, + "~:points": [ + {"~#point": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}}, + {"~#point": {"~:x": 1094.2889430596829, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b1469931a9d0", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 15.28894305968281, "~:y": 5.330620727377026, "~:width": 27.818478815317192, "~:height": 44.669379272622976, "~:x1": 15.28894305968281, "~:y1": 5.330620727377026, "~:x2": 43.107421875, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296, "~:width": 27.818478815317192, "~:height": 44.669379272622976, "~:x1": 1094.2889430596829, "~:y1": -231.66937927262296, "~:x2": 1122.107421875, "~:y2": -187}}, + "~:fills": [{"~:fill-color": "#434cc2", "~:fill-opacity": 1}], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + }, + "~u8f89d4e8-0efd-804d-8007-b1469931a9d1": { + "~#shape": { + "~:y": -231.66937927262296, + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:name": "svg-g", + "~:width": 27.81847881531712, + "~:type": "~:group", + "~:svg-attrs": {"~:className": "strokes"}, + "~:points": [ + {"~#point": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}}, + {"~#point": {"~:x": 1094.2889430596829, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b1469931a9d1", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b146992f0a36", + "~:svg-viewbox": {"~#rect": {"~:x": 15.288943059682879, "~:y": 5.330620727377038, "~:width": 27.81847881531712, "~:height": 44.66937927262296, "~:x1": 0, "~:y1": 0, "~:x2": 86.215, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [], + "~:x": 1094.2889430596829, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296, "~:width": 27.81847881531712, "~:height": 44.66937927262296, "~:x1": 1094.2889430596829, "~:y1": -231.66937927262296, "~:x2": 1122.107421875, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": 44.66937927262296, + "~:flip-y": null, + "~:shapes": ["~u8f89d4e8-0efd-804d-8007-b1469931e08c"] + } + }, + "~u8f89d4e8-0efd-804d-8007-b1469931e08c": { + "~#shape": { + "~:y": null, + "~:stroke-cap-start": "~:round", + "~:transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:rotation": 0, + "~:content": {"~#penpot/path-data": "~bAQAAAAAAAAAAAAAAAAAAAAAAAABwQ4xEAAA7wwMAAACTnIREUIZlw8Dsi0RBvoLDcEOMRAAAO8M="}, + "~:name": "svg-path", + "~:width": null, + "~:type": "~:path", + "~:svg-attrs": {"~:fill": "none", "~:style": {"~:fill": "none"}, "~:strokeLinecap": "round", "~:strokeMiterlimit": "10", "~:className": "stroke-shape"}, + "~:points": [ + {"~#point": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -231.66937927262296}}, + {"~#point": {"~:x": 1122.107421875, "~:y": -187}}, + {"~#point": {"~:x": 1094.2889430596829, "~:y": -187}} + ], + "~:proportion-lock": false, + "~:stroke-cap-end": "~:round", + "~:transform-inverse": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:stroke-linecap": "~:round", + "~:svg-transform": {"~#matrix": {"~:a": 1, "~:b": 0, "~:c": 0, "~:d": 1, "~:e": 0, "~:f": 0}}, + "~:id": "~u8f89d4e8-0efd-804d-8007-b1469931e08c", + "~:parent-id": "~u8f89d4e8-0efd-804d-8007-b1469931a9d1", + "~:svg-viewbox": {"~#rect": {"~:x": 15.28894305968281, "~:y": 5.330620727377026, "~:width": 27.818478815317192, "~:height": 44.669379272622976, "~:x1": 15.28894305968281, "~:y1": 5.330620727377026, "~:x2": 43.107421875, "~:y2": 50}}, + "~:svg-defs": {}, + "~:frame-id": "~u00000000-0000-0000-0000-000000000000", + "~:strokes": [{"~:stroke-alignment": "~:outer", "~:stroke-style": "~:solid", "~:stroke-color": "#000000", "~:stroke-opacity": 1, "~:stroke-width": 1}], + "~:x": null, + "~:proportion": 1, + "~:selrect": {"~#rect": {"~:x": 1094.2889430596829, "~:y": -231.66937927262296, "~:width": 27.818478815317192, "~:height": 44.669379272622976, "~:x1": 1094.2889430596829, "~:y1": -231.66937927262296, "~:x2": 1122.107421875, "~:y2": -187}}, + "~:fills": [], + "~:flip-x": null, + "~:height": null, + "~:flip-y": null + } + } + }, + "~:id": "~uaaa00001-0001-0001-8007-000000000002", + "~:name": "Page 1" + } + }, + "~:id": "~uaaa00001-0001-0001-8007-000000000001", + "~:options": { + "~:components-v2": true, + "~:base-font-size": "16px" + } + } +} diff --git a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js index 63e16a3966..4d0597b9aa 100644 --- a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js +++ b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js @@ -457,6 +457,62 @@ test("Check inner stroke artifacts", async ({ }); }); +test("No white seam at intersections of overlapping shapes with inner strokes", async ({ + page, +}) => { + const workspace = new WasmWorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockGetFile("render-wasm/get-file-inner-stroke-overlap-seam.json"); + + await workspace.goToWorkspace({ + id: "aaa00001-0001-0001-8007-000000000001", + pageId: "aaa00001-0001-0001-8007-000000000002", + }); + await workspace.waitForFirstRender(); + + await workspace.viewport.click(); + await page.keyboard.press("ControlOrMeta+A"); + const previousRenderCount = await workspace.getRenderCount(); + await page.keyboard.press("f"); + await workspace.waitForNextRender(previousRenderCount); + + await workspace.hideUI(); + + // Stricter comparison: white seam artifacts are very subtle + await expect(workspace.canvas).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0.1, + }); +}); + +test("Correct stroke closing at self-intersection of overlapping shapes with outer strokes", async ({ + page, +}) => { + const workspace = new WasmWorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockGetFile("render-wasm/get-file-outer-stroke-overlap-seam.json"); + + await workspace.goToWorkspace({ + id: "aaa00001-0001-0001-8007-000000000001", + pageId: "aaa00001-0001-0001-8007-000000000002", + }); + await workspace.waitForFirstRender(); + + await workspace.viewport.click(); + await page.keyboard.press("ControlOrMeta+A"); + const previousRenderCount = await workspace.getRenderCount(); + await page.keyboard.press("f"); + await workspace.waitForNextRender(previousRenderCount); + + await workspace.hideUI(); + + // Stricter comparison: white seam artifacts are very subtle + await expect(workspace.canvas).toHaveScreenshot({ + maxDiffPixelRatio: 0, + threshold: 0.1, + }); +}); + test("BUG 13551 - Blurs affecting other elements", async ({ page, }) => { diff --git a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Correct-stroke-closing-at-self-intersection-of-overlapping-shapes-with-outer-strokes-1.png b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Correct-stroke-closing-at-self-intersection-of-overlapping-shapes-with-outer-strokes-1.png new file mode 100644 index 0000000000..3b041ad6ed Binary files /dev/null and b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Correct-stroke-closing-at-self-intersection-of-overlapping-shapes-with-outer-strokes-1.png differ diff --git a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/No-white-seam-at-intersections-of-overlapping-shapes-with-inner-strokes-1.png b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/No-white-seam-at-intersections-of-overlapping-shapes-with-inner-strokes-1.png new file mode 100644 index 0000000000..171b678f26 Binary files /dev/null and b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/No-white-seam-at-intersections-of-overlapping-shapes-with-inner-strokes-1.png differ diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index 950c60baaa..f8be9f857b 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -675,6 +675,13 @@ (let [structure-entries (parse-structure-modifiers modif-tree)] (wasm.api/set-structure-modifiers structure-entries)) + ;; Apply property changes (e.g. grow-type) to WASM shapes before + ;; propagating geometry, so propagate_modifiers sees the updated state. + (doseq [[id {:keys [property value]}] (extract-property-changes modif-tree)] + (when (= property :grow-type) + (wasm.api/use-shape id) + (wasm.api/set-shape-grow-type value))) + (let [objects (dsh/lookup-page-objects state) geometry-entries diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index b93258a869..989e85eb38 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -114,6 +114,12 @@ (cts/check-shape shape) (ptk/reify ::add-shape + ptk/UpdateEvent + (update [_ state] + (cond-> state + (and (cfh/text-shape? shape) (nil? (:content shape))) + (update :workspace-new-text-shapes (fnil conj #{}) (:id shape)))) + ptk/WatchEvent (watch [it state _] (let [page-id (:current-page-id state) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index 421b29d4d0..b1903ec5c5 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -13,6 +13,7 @@ [app.common.files.changes-builder :as pcb] [app.common.files.helpers :as cfh] [app.common.geom.point :as gpt] + [app.common.geom.rect :as grc] [app.common.geom.shapes :as gsh] [app.common.math :as mth] [app.common.types.fills :as types.fills] @@ -101,7 +102,9 @@ (update [_ state] (if (some? editor-state) (update state :workspace-editor-state assoc id editor-state) - (update state :workspace-editor-state dissoc id))))) + (-> state + (update :workspace-editor-state dissoc id) + (update :workspace-new-text-shapes disj id)))))) (defn finalize-editor-state [id update-name?] @@ -116,11 +119,12 @@ (ted/get-editor-current-content)) name (gen-name editor-state) - new-shape? (nil? (:content shape))] + new-shape? (contains? (:workspace-new-text-shapes state) id)] (if (ted/content-has-text? content) - (if (features/active-feature? state "render-wasm/v1") - (let [content (d/merge (ted/export-content content) - (dissoc (:content shape) :children))] + (when (features/active-feature? state "render-wasm/v1") + (let [content (d/merge (ted/export-content content) + (dissoc (:content shape) :children)) + new-size (dwwt/get-wasm-text-new-size shape content)] (rx/merge (rx/of (update-editor-state shape nil)) (when (and (not= content (:content shape)) @@ -133,39 +137,38 @@ (-> shape (assoc :content content) (cond-> (and update-name? (some? name)) - (assoc :name name)))) - {:undo-group (when new-shape? id)}) - - (dwm/apply-wasm-modifiers - (dwwt/resize-wasm-text-modifiers shape content) - {:undo-group (when new-shape? id)}))))) - - (let [content (d/merge (ted/export-content content) - (dissoc (:content shape) :children)) - modifiers (get-in state [:workspace-text-modifier id])] - (rx/merge - (rx/of (update-editor-state shape nil)) - (when (and (not= content (:content shape)) - (some? (:current-page-id state)) - (some? shape)) - (rx/of - (dwsh/update-shapes - [id] - (fn [shape] - (let [{:keys [width height position-data]} modifiers] - (-> shape - (assoc :content content) - (cond-> position-data - (assoc :position-data position-data)) - (cond-> (and update-name? (some? name)) - (assoc :name name)) - (cond-> (or (some? width) (some? height)) - (gsh/transform-shape (ctm/change-size shape width height)))))) + (assoc :name name)) + (cond-> (some? new-size) + (gsh/transform-shape + (ctm/change-size shape (:width new-size) (:height new-size)))))) {:undo-group (when new-shape? id)})))))) - (when (some? id) - (rx/of (dws/deselect-shape id) - (dwsh/delete-shapes #{id}))))))))) + (let [content (d/merge (ted/export-content content) + (dissoc (:content shape) :children)) + modifiers (get-in state [:workspace-text-modifier id])] + (rx/merge + (rx/of (update-editor-state shape nil)) + (when (and (not= content (:content shape)) + (some? (:current-page-id state)) + (some? shape)) + (rx/of + (dwsh/update-shapes + [id] + (fn [shape] + (let [{:keys [width height position-data]} modifiers] + (-> shape + (assoc :content content) + (cond-> position-data + (assoc :position-data position-data)) + (cond-> (and update-name? (some? name)) + (assoc :name name)) + (cond-> (or (some? width) (some? height)) + (gsh/transform-shape (ctm/change-size shape width height)))))) + {:undo-group (when new-shape? id)})))))) + + (when (some? id) + (rx/of (dws/deselect-shape id) + (dwsh/delete-shapes #{id})))))))) (defn initialize-editor-state [{:keys [id name content] :as shape} decorator] @@ -178,8 +181,7 @@ editor (cond-> (ted/create-editor-state text-state decorator) (and (nil? content) (some? attrs)) (ted/update-editor-current-block-data attrs))] - (-> state - (assoc-in [:workspace-editor-state id] editor)))) + (assoc-in state [:workspace-editor-state id] editor))) ptk/WatchEvent (watch [_ state stream] @@ -917,6 +919,46 @@ (update [_ state] (update-in state [:workspace-text-modifier shape-id] {:position-data position-data})))) +(defn- add-geometry-undo-to-commit + "Adds geometry undo/redo to a commit so undo restores both content and geometry. + old-geom and final-geom are maps with :selrect :points and optionally :width :height." + [base objects id old-geom final-geom attrs] + (let [objects-with-old (update objects id #(merge % old-geom)) + final-shape-fn (fn [shape] (merge shape final-geom))] + (-> base + (pcb/with-objects objects-with-old) + (pcb/update-shapes [id] final-shape-fn {:attrs attrs}) + (pcb/set-stack-undo? true)))) + +(defn- build-finalize-commit-changes + "Builds the commit changes for text finalization (content + geometry undo). + For auto-width text, include geometry so undo restores e.g. width." + [it state id {:keys [new-shape? content-has-text? content original-content undo-group]}] + (let [page-id (:current-page-id state) + objects (dsh/lookup-page-objects state page-id) + shape* (get objects id) + base (-> (pcb/empty-changes it page-id) + (pcb/set-text-content id content original-content) + (cond-> new-shape? + (-> (pcb/set-undo-group id) + (pcb/set-stack-undo? true))) + (cond-> (and (not new-shape?) (some? undo-group)) + (-> (pcb/set-undo-group undo-group) + (pcb/set-stack-undo? true)))) + final-geom (select-keys shape* [:selrect :points :width :height]) + geom-keys (if new-shape? [:selrect :points] [:selrect :points :width :height]) + old-geom (when (and content-has-text? (not= :fixed (:grow-type shape*))) + (or (get-in state [:workspace-text-session-geom id]) + (let [sr (:selrect shape*) + r (grc/make-rect (or (:x sr) 0) (or (:y sr) 0) 0.01 0.01)] + {:selrect r :points (grc/rect->points r)})))] + (if (some? old-geom) + (add-geometry-undo-to-commit base objects id + (select-keys old-geom geom-keys) + (select-keys final-geom geom-keys) + geom-keys) + base))) + (defn v2-update-text-shape-content [id content & {:keys [update-name? name finalize? save-undo? original-content] :or {update-name? false name nil finalize? false save-undo? true original-content nil}}] @@ -926,64 +968,84 @@ (if (features/active-feature? state "render-wasm/v1") (let [objects (dsh/lookup-page-objects state) shape (get objects id) - new-shape? (nil? (:content shape)) + new-shape? (contains? (:workspace-new-text-shapes state) id) prev-content (:content shape) has-prev-content? (not (nil? (:prev-content shape))) - has-content? (when-not new-shape? - (v2-content-has-text? content)) - did-has-content? (when-not new-shape? - (v2-content-has-text? prev-content))] + ;; For existing shapes, capture geometry at session start once so + ;; finalize can build a single undo entry. Stored in workspace state, + ;; not in the shape, to avoid persisting session-only data. + session-start-geom (or (get-in state [:workspace-text-session-geom id]) + (select-keys shape [:selrect :points :width :height])) + content-has-text? (v2-content-has-text? content) + prev-content-has-text? (v2-content-has-text? prev-content) + new-size (when (and (not= :fixed (:grow-type shape)) + content-has-text?) + (dwwt/get-wasm-text-new-size shape content)) + ;; New shapes: single undo on finalize only (no per-keystroke undo) + effective-save-undo? (if new-shape? finalize? save-undo?) + effective-stack-undo? (and new-shape? finalize?) + finalize-undo-group (when (and finalize? (not new-shape?)) (uuid/next))] (rx/concat (rx/of + ;; Store session-start geometry in workspace state once for existing shapes + (when (and (not new-shape?) + (nil? (get-in state [:workspace-text-session-geom id]))) + (fn [s] (assoc-in s [:workspace-text-session-geom id] session-start-geom))) (dwsh/update-shapes [id] (fn [shape] - (let [new-shape (-> shape - (assoc :content content) - (cond-> (and has-content? - has-prev-content?) - (dissoc :prev-content)) - (cond-> (and did-has-content? - (not has-content?)) - (assoc :prev-content prev-content)) - (cond-> (and update-name? (some? name)) - (assoc :name name)))] - new-shape)) - {:save-undo? save-undo? :undo-group (when new-shape? id)}) + (-> shape + (assoc :content content) + (cond-> (and (not new-shape?) + content-has-text? + has-prev-content?) + (dissoc :prev-content)) + (cond-> (and (not new-shape?) + prev-content-has-text? + (not content-has-text?)) + (assoc :prev-content prev-content)) + (cond-> (and update-name? (some? name)) + (assoc :name name)) + (cond-> (some? new-size) + (gsh/transform-shape + (ctm/change-size shape (:width new-size) (:height new-size)))))) + {:save-undo? effective-save-undo? + :stack-undo? effective-stack-undo? + :undo-group (or finalize-undo-group (when new-shape? id))}) - (when-let [modifiers (dwwt/resize-wasm-text-modifiers shape content)] - (let [options {:undo-group (when new-shape? id)}] - (if (and (not= :fixed (:grow-type shape)) finalize?) - (dwm/apply-wasm-modifiers modifiers options) - (dwm/set-wasm-modifiers modifiers options))))) + ;; When we don't update the shape (no new-size), still update WASM display + (when-not (some? new-size) + (when-let [modifiers (dwwt/resize-wasm-text-modifiers shape content)] + (dwm/set-wasm-modifiers modifiers {:undo-group (when new-shape? id)})))) (when finalize? (rx/concat - (when (and (not has-content?) (some? id)) + (when (and (not content-has-text?) (some? id)) (rx/of (when has-prev-content? (dwsh/update-shapes [id] - (fn [shape] - (let [new-shape (-> shape - (assoc :content (:prev-content shape)))] - new-shape)) + (fn [shape] (assoc shape :content (:prev-content shape))) {:save-undo? false})) (dws/deselect-shape id) (dwsh/delete-shapes #{id}))) (rx/of - ;; This commit is necesary for undo and component propagation - ;; on finalization (dch/commit-changes - (-> (pcb/empty-changes it (:current-page-id state)) - (pcb/set-text-content id content original-content))) - (dwt/finish-transform)))))) + (build-finalize-commit-changes it state id + {:new-shape? new-shape? + :content-has-text? content-has-text? + :content content + :original-content original-content + :undo-group finalize-undo-group})) + (dwt/finish-transform) + (fn [state] + (-> state + (update :workspace-new-text-shapes disj id) + (update :workspace-text-session-geom (fnil dissoc {}) id)))))))) - (let [objects (dsh/lookup-page-objects state) - shape (get objects id) - modifiers (get-in state [:workspace-text-modifier id]) - new-shape? (nil? (:content shape))] + (let [modifiers (get-in state [:workspace-text-modifier id]) + new-shape? (contains? (:workspace-new-text-shapes state) id)] (rx/of (dwsh/update-shapes [id] (fn [shape] diff --git a/frontend/src/app/main/data/workspace/wasm_text.cljs b/frontend/src/app/main/data/workspace/wasm_text.cljs index 594a657105..c43cee567f 100644 --- a/frontend/src/app/main/data/workspace/wasm_text.cljs +++ b/frontend/src/app/main/data/workspace/wasm_text.cljs @@ -22,26 +22,36 @@ [beicon.v2.core :as rx] [potok.v2.core :as ptk])) -(defn resize-wasm-text-modifiers +(defn get-wasm-text-new-size + "Computes the new {width, height} for a text shape from WASM text layout. + For :fixed grow-type, updates WASM content and returns current dimensions (no resize)." ([shape] - (resize-wasm-text-modifiers shape (:content shape))) + (get-wasm-text-new-size shape (:content shape))) - ([{:keys [id points selrect grow-type] :as shape} content] + ([{:keys [id selrect grow-type] :as shape} content] (when id (wasm.api/use-shape id) (wasm.api/set-shape-text-content id content) (wasm.api/set-shape-text-images id content) + (let [dimension (when (not= :fixed grow-type) + (wasm.api/get-text-dimensions))] + {:width (if (#{:fixed :auto-height} grow-type) + (:width selrect) + (:width dimension)) + :height (if (= :fixed grow-type) + (:height selrect) + (:height dimension))})))) - (let [dimension (wasm.api/get-text-dimensions) - width-scale (if (#{:fixed :auto-height} grow-type) - 1.0 - (/ (:width dimension) (:width selrect))) - height-scale (if (= :fixed grow-type) - 1.0 - (/ (:height dimension) (:height selrect))) - resize-v (gpt/point width-scale height-scale) - origin (first points)] +(defn resize-wasm-text-modifiers + ([shape] + (resize-wasm-text-modifiers shape (:content shape))) + ([{:keys [id points selrect] :as shape} content] + (when-let [new-size (get-wasm-text-new-size shape content)] + (let [width-scale (/ (:width new-size) (:width selrect)) + height-scale (/ (:height new-size) (:height selrect)) + resize-v (gpt/point width-scale height-scale) + origin (first points)] {id {:modifiers (ctm/resize-modifiers diff --git a/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs index fd33cdcb45..29e6ec6759 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/v2_editor.cljs @@ -118,7 +118,8 @@ :update-name? update-name? :name generated-name :finalize? true - :save-undo? false + ;; Single undo entry for the whole edit + :save-undo? true :original-content original-content)))) (let [container-node (mf/ref-val container-ref)] diff --git a/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs index 9fd4a23e99..d0cb12caeb 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/v3_editor.cljs @@ -334,8 +334,6 @@ :on-cut on-cut :on-focus on-focus :on-blur on-blur - ;; FIXME on-click - ;; :on-click on-click :id "text-editor-wasm-input" :class (dm/str (cur/get-dynamic "text" (:rotation shape)) " " diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index d5a1f181dd..c7413af9ac 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -15,6 +15,7 @@ [app.main.data.helpers :as dsh] [app.main.data.workspace :as udw] [app.main.data.workspace.common :as dwc] + [app.main.features :as features] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.context :as ctx] @@ -39,7 +40,6 @@ [app.main.ui.workspace.sidebar.options.shapes.text :as text] [app.util.i18n :as i18n :refer [tr]] [okulary.core :as l] - [rumext.v2 :as mf])) ;; --- Options @@ -50,10 +50,15 @@ (let [shape-type (dm/get-prop shape :type) shape-id (dm/get-prop shape :id) + wasm-modifiers (mf/deref refs/workspace-wasm-modifiers) modifiers (mf/deref refs/workspace-modifiers) - modifiers (dm/get-in modifiers [shape-id :modifiers]) - shape (gsh/transform-shape shape modifiers) + shape + (if (features/active-feature? @st/state "render-wasm/v1") + (let [wasm-modifiers (into {} wasm-modifiers)] + (gsh/apply-transform shape (get wasm-modifiers shape-id))) + (gsh/transform-shape shape (dm/get-in modifiers [shape-id :modifiers]))) + props (mf/spread-props props {:shape shape :file-id file-id :page-id page-id :libraries libraries})] (case shape-type diff --git a/render-wasm/src/math/bools.rs b/render-wasm/src/math/bools.rs index 67de1e874e..0215e50dca 100644 --- a/render-wasm/src/math/bools.rs +++ b/render-wasm/src/math/bools.rs @@ -365,11 +365,10 @@ fn beziers_to_segments(beziers: &[(BezierSource, Bezier)]) -> Vec { let mut bm = init_bm(beziers); while let Some(bezier) = pop_first(&mut bm) { - result.push(Segment::MoveTo(( - bezier.1.start.x as f32, - bezier.1.start.y as f32, - ))); + let start = (bezier.1.start.x as f32, bezier.1.start.y as f32); + result.push(Segment::MoveTo(start)); push_bezier(&mut result, &bezier.1); + let mut last_end = (bezier.1.end.x as f32, bezier.1.end.y as f32); let mut next_p = BezierStart(bezier.0, bezier.1.end); loop { @@ -377,8 +376,24 @@ fn beziers_to_segments(beziers: &[(BezierSource, Bezier)]) -> Vec { break; }; push_bezier(&mut result, &next.1); + last_end = (next.1.end.x as f32, next.1.end.y as f32); next_p = BezierStart(next.0, next.1.end); } + + // Close the subpath if the last point is close to the start. + if (last_end.0 - start.0).abs() < INTERSECT_THRESHOLD_SAME + && (last_end.1 - start.1).abs() < INTERSECT_THRESHOLD_SAME + { + // Remove the redundant LineTo that goes back to start, if present. + if let Some(Segment::LineTo(p)) = result.last() { + if (p.0 - start.0).abs() < INTERSECT_THRESHOLD_SAME + && (p.1 - start.1).abs() < INTERSECT_THRESHOLD_SAME + { + result.pop(); + } + } + result.push(Segment::Close); + } } result } diff --git a/render-wasm/src/render/surfaces.rs b/render-wasm/src/render/surfaces.rs index 5bb227ab04..11239c2007 100644 --- a/render-wasm/src/render/surfaces.rs +++ b/render-wasm/src/render/surfaces.rs @@ -427,17 +427,23 @@ impl Surfaces { let mut stroke_paint = paint.clone(); stroke_paint.set_stroke_width(s * 2.0); canvas.draw_path(&path, &stroke_paint); + } else if let Some(eps) = inset.filter(|&e| e > 0.0) { + // Wrap fill + clear in a save_layer so the BlendMode::Clear + // only erases the current shape's fill, not other shapes + // already drawn on this surface (avoids white seams at + // intersections of shapes with inner strokes). + let layer_rec = skia::canvas::SaveLayerRec::default(); + canvas.save_layer(&layer_rec); + canvas.draw_path(&path, paint); + let mut clear_paint = skia::Paint::default(); + clear_paint.set_style(skia::PaintStyle::Stroke); + clear_paint.set_stroke_width(eps * 2.0); + clear_paint.set_blend_mode(skia::BlendMode::Clear); + clear_paint.set_anti_alias(paint.is_anti_alias()); + canvas.draw_path(&path, &clear_paint); + canvas.restore(); } else { canvas.draw_path(&path, paint); - // Inset: avoid seam with inner strokes by clearing a thin border from the fill - if let Some(eps) = inset.filter(|&e| e > 0.0) { - let mut clear_paint = skia::Paint::default(); - clear_paint.set_style(skia::PaintStyle::Stroke); - clear_paint.set_stroke_width(eps * 2.0); - clear_paint.set_blend_mode(skia::BlendMode::Clear); - clear_paint.set_anti_alias(paint.is_anti_alias()); - canvas.draw_path(&path, &clear_paint); - } } } } diff --git a/render-wasm/src/shapes/paths.rs b/render-wasm/src/shapes/paths.rs index d6d6a8d1ef..8fa81c6b77 100644 --- a/render-wasm/src/shapes/paths.rs +++ b/render-wasm/src/shapes/paths.rs @@ -32,34 +32,25 @@ impl Default for Path { impl Path { pub fn new(segments: Vec) -> Self { let mut pb = skia::PathBuilder::new(); - let mut start = None; + // Don't auto-close the Skia path when start ≈ end. + // SVG treats these as open paths (caps apply at endpoints). + // Auto-closing changes stroke behavior from caps to joins, + // producing artifacts at self-intersection points. + // Only explicit Segment::Close should close the Skia path. for segment in segments.iter() { - let destination = match *segment { + match *segment { Segment::MoveTo(xy) => { - start = Some(xy); pb.move_to(xy); - None } Segment::LineTo(xy) => { pb.line_to(xy); - Some(xy) } Segment::CurveTo((c1, c2, xy)) => { pb.cubic_to(c1, c2, xy); - Some(xy) } Segment::Close => { pb.close(); - None - } - }; - - if let (Some(start), Some(destination)) = (start, destination) { - if math::is_close_to(destination.0, start.0) - && math::is_close_to(destination.1, start.1) - { - pb.close(); } } } diff --git a/render-wasm/src/shapes/strokes.rs b/render-wasm/src/shapes/strokes.rs index 426d5939c3..4ca3b5f906 100644 --- a/render-wasm/src/shapes/strokes.rs +++ b/render-wasm/src/shapes/strokes.rs @@ -213,12 +213,24 @@ impl Stroke { paint.set_anti_alias(antialias); if let Some(svg_attrs) = svg_attrs { - if svg_attrs.stroke_linecap == StrokeLineCap::Round { - paint.set_stroke_cap(skia::paint::Cap::Round); + match svg_attrs.stroke_linecap { + StrokeLineCap::Round => { + paint.set_stroke_cap(skia::paint::Cap::Round); + } + StrokeLineCap::Square => { + paint.set_stroke_cap(skia::paint::Cap::Square); + } + StrokeLineCap::Butt => {} // Skia default } - if svg_attrs.stroke_linejoin == StrokeLineJoin::Round { - paint.set_stroke_join(skia::paint::Join::Round); + match svg_attrs.stroke_linejoin { + StrokeLineJoin::Round => { + paint.set_stroke_join(skia::paint::Join::Round); + } + StrokeLineJoin::Bevel => { + paint.set_stroke_join(skia::paint::Join::Bevel); + } + StrokeLineJoin::Miter => {} // Skia default } } diff --git a/render-wasm/src/shapes/text.rs b/render-wasm/src/shapes/text.rs index 298b44cc55..a23010585f 100644 --- a/render-wasm/src/shapes/text.rs +++ b/render-wasm/src/shapes/text.rs @@ -673,7 +673,7 @@ impl TextContent { }); let size = TextContentSize::new_with_normalized_line_height( - width, + width.ceil(), paragraph_height.ceil(), DEFAULT_TEXT_CONTENT_SIZE, normalized_line_height, @@ -708,12 +708,12 @@ impl TextContent { pub fn set_layout_from_result( &mut self, result: TextContentLayoutResult, - default_height: f32, default_width: f32, + default_height: f32, ) { self.layout.set(result.0, result.1); self.size - .copy_finite_size(result.2, default_height, default_width); + .copy_finite_size(result.2, default_width, default_height); } pub fn update_layout(&mut self, selrect: Rect) -> TextContentSize { @@ -1151,7 +1151,7 @@ impl TextSpan { pub fn apply_text_transform(&self) -> String { let browser = crate::with_state!(state, { state.current_browser }); let text = Self::process_ignored_chars(&self.text, browser); - let transformed_text = match self.text_transform { + match self.text_transform { Some(TextTransform::Uppercase) => text.to_uppercase(), Some(TextTransform::Lowercase) => text.to_lowercase(), Some(TextTransform::Capitalize) => text @@ -1166,9 +1166,7 @@ impl TextSpan { .collect::>() .join(" "), None => text, - }; - - transformed_text.replace("/", "/\u{200B}") + } } pub fn scale_content(&mut self, value: f32) { diff --git a/render-wasm/src/wasm/text.rs b/render-wasm/src/wasm/text.rs index 737925b9b4..c4f6a682d5 100644 --- a/render-wasm/src/wasm/text.rs +++ b/render-wasm/src/wasm/text.rs @@ -308,7 +308,6 @@ pub extern "C" fn set_shape_text_content() -> crate::error::Result<()> { #[no_mangle] pub extern "C" fn set_shape_grow_type(grow_type: u8) { let grow_type = RawGrowType::from(grow_type); - with_current_shape_mut!(state, |shape: &mut Shape| { if let Type::Text(text_content) = &mut shape.shape_type { text_content.set_grow_type(GrowType::from(grow_type));