From 4185a7a6f357b8a73189497c5b9073f98469b138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Tue, 17 Feb 2026 12:58:15 +0100 Subject: [PATCH 1/2] :bug: Fix grid layout lines persisted after board is deleted --- render-wasm/src/main.rs | 3 +++ render-wasm/src/render/ui.rs | 4 ++++ render-wasm/src/shapes.rs | 10 ++++++++++ render-wasm/src/state.rs | 4 ++++ 4 files changed, 21 insertions(+) diff --git a/render-wasm/src/main.rs b/render-wasm/src/main.rs index 005bf31781..c435e03ded 100644 --- a/render-wasm/src/main.rs +++ b/render-wasm/src/main.rs @@ -412,6 +412,9 @@ fn set_children_set(entries: Vec) { for id in entries { state.touch_shape(id); + if let Some(children_shape) = state.shapes.get_mut(&id) { + children_shape.set_deleted(false); + } } }); diff --git a/render-wasm/src/render/ui.rs b/render-wasm/src/render/ui.rs index 97c8dd4867..7d6436fbc8 100644 --- a/render-wasm/src/render/ui.rs +++ b/render-wasm/src/render/ui.rs @@ -45,6 +45,10 @@ pub fn render(render_state: &mut RenderState, shapes: ShapesPoolRef) { continue; } + if shape.deleted() { + continue; + } + if let Some(shape) = shapes.get(&shape.id) { grid_layout::render_overlay(zoom, canvas, shape, shapes); } diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 2eed111226..8967b6ee49 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -196,6 +196,7 @@ pub struct Shape { pub extrect_cache: RefCell>, pub svg_transform: Option, pub ignore_constraints: bool, + deleted: bool, } // Returns all ancestor shapes of this shape, traversing up the parent hierarchy @@ -284,6 +285,7 @@ impl Shape { extrect_cache: RefCell::new(None), svg_transform: None, ignore_constraints: false, + deleted: false, } } @@ -441,6 +443,14 @@ impl Shape { self.svg_transform } + pub fn set_deleted(&mut self, value: bool) { + self.deleted = value; + } + + pub fn deleted(&self) -> bool { + self.deleted + } + // FIXME: These arguments could be grouped or simplified #[allow(clippy::too_many_arguments)] pub fn set_flex_layout_child_data( diff --git a/render-wasm/src/state.rs b/render-wasm/src/state.rs index 7762d4b5aa..b99b768334 100644 --- a/render-wasm/src/state.rs +++ b/render-wasm/src/state.rs @@ -157,6 +157,10 @@ impl State { self.render_state.tiles.remove_shape_at(tile, shape.id); } } + + if let Some(shape_to_delete) = self.shapes.get_mut(&id) { + shape_to_delete.set_deleted(true); + } } } From cd4b9ddd47942bd7779f3b5851dfc44eee24c09b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Tue, 17 Feb 2026 14:32:03 +0100 Subject: [PATCH 2/2] :sparkles: Add regression test for bug 13415 --- .../data/workspace/get-file-13415.json | 135 ++++++++++++++++++ .../data/workspace/update-file-13415.json | 18 +++ .../playwright/ui/specs/workspace.spec.js | 22 +++ ...is-not-removed-when-deleting-a-board-1.png | Bin 0 -> 9528 bytes 4 files changed, 175 insertions(+) create mode 100644 frontend/playwright/data/workspace/get-file-13415.json create mode 100644 frontend/playwright/data/workspace/update-file-13415.json create mode 100644 frontend/playwright/ui/specs/workspace.spec.js-snapshots/BUG-13415---Grid-layout-overlay-is-not-removed-when-deleting-a-board-1.png diff --git a/frontend/playwright/data/workspace/get-file-13415.json b/frontend/playwright/data/workspace/get-file-13415.json new file mode 100644 index 0000000000..1ad5dcb9ec --- /dev/null +++ b/frontend/playwright/data/workspace/get-file-13415.json @@ -0,0 +1,135 @@ +{ + "~:features": { + "~#set": [ + "fdata/path-data", + "plugins/runtime", + "design-tokens/v1", + "variants/v1", + "layout/grid", + "styles/v2", + "fdata/objects-map", + "render-wasm/v1", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:team-id": "~u99e49e93-362f-80ef-8007-3450ea52c9a4", + "~: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": "BUG 13415", + "~:revn": 14, + "~:modified-at": "~m1771334256704", + "~:vern": 0, + "~:id": "~u0472abff-2573-8186-8007-961793e53f45", + "~: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": "~ucd8f7672-e5d1-810f-8007-87e124eda82a", + "~:created-at": "~m1771326794644", + "~:backend": "legacy-db", + "~:data": { + "~:pages": [ + "~u0472abff-2573-8186-8007-961793e53f46" + ], + "~:pages-index": { + "~u0472abff-2573-8186-8007-961793e53f46": { + "~:objects": { + "~#penpot/objects-map/v2": { + "~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",0,\"~:proportion\",1.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",0.01,\"~:height\",0.01,\"~:x1\",0,\"~:y1\",0,\"~:x2\",0.01,\"~:y2\",0.01]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^H\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~uaef184da-e9c1-80f8-8007-961cf253d534\"]]]", + "~uaef184da-e9c1-80f8-8007-961cf253d534": "[\"~#shape\",[\"^ \",\"~:y\",286,\"~:layout-grid-columns\",[[\"^ \",\"~:type\",\"~:flex\",\"~:value\",1],[\"^ \",\"^2\",\"^3\",\"^4\",1]],\"~:hide-fill-on-export\",false,\"~:layout-gap-type\",\"~:multiple\",\"~:layout-padding\",[\"^ \",\"~:p1\",0,\"~:p2\",0,\"~:p3\",0,\"~:p4\",0],\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:layout\",\"~:grid\",\"~:hide-in-viewer\",false,\"~:name\",\"Board\",\"~:layout-align-items\",\"~:start\",\"~:width\",298,\"~:layout-grid-cells\",[\"^ \",\"~uaef184da-e9c1-80f8-8007-961cf50d67b4\",[\"^ \",\"~:justify-self\",\"~:auto\",\"~:column\",1,\"~:id\",\"~uaef184da-e9c1-80f8-8007-961cf50d67b4\",\"~:position\",\"^L\",\"~:column-span\",1,\"~:align-self\",\"^L\",\"~:row\",1,\"~:row-span\",1,\"~:shapes\",[]],\"~uaef184da-e9c1-80f8-8007-961cf50d67b5\",[\"^ \",\"^K\",\"^L\",\"^M\",2,\"^N\",\"~uaef184da-e9c1-80f8-8007-961cf50d67b5\",\"^O\",\"^L\",\"^P\",1,\"^Q\",\"^L\",\"^R\",1,\"^S\",1,\"^T\",[]],\"~uaef184da-e9c1-80f8-8007-961cf50d67b6\",[\"^ \",\"^K\",\"^L\",\"^M\",1,\"^N\",\"~uaef184da-e9c1-80f8-8007-961cf50d67b6\",\"^O\",\"^L\",\"^P\",1,\"^Q\",\"^L\",\"^R\",2,\"^S\",1,\"^T\",[]],\"~uaef184da-e9c1-80f8-8007-961cf50d67b7\",[\"^ \",\"^K\",\"^L\",\"^M\",2,\"^N\",\"~uaef184da-e9c1-80f8-8007-961cf50d67b7\",\"^O\",\"^L\",\"^P\",1,\"^Q\",\"^L\",\"^R\",2,\"^S\",1,\"^T\",[]]],\"~:layout-padding-type\",\"~:simple\",\"^2\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",322,\"~:y\",286]],[\"^10\",[\"^ \",\"~:x\",620,\"~:y\",286]],[\"^10\",[\"^ \",\"~:x\",620,\"~:y\",552]],[\"^10\",[\"^ \",\"~:x\",322,\"~:y\",552]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:layout-gap\",[\"^ \",\"~:row-gap\",0,\"~:column-gap\",0],\"~:transform-inverse\",[\"^>\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:layout-justify-content\",\"~:stretch\",\"~:r1\",0,\"^N\",\"~uaef184da-e9c1-80f8-8007-961cf253d534\",\"~:layout-justify-items\",\"^G\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:layout-align-content\",\"^19\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",322,\"~:proportion\",1,\"~:r4\",0,\"~:layout-grid-rows\",[[\"^ \",\"^2\",\"^3\",\"^4\",1],[\"^ \",\"^2\",\"^3\",\"^4\",1]],\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",322,\"~:y\",286,\"^H\",298,\"~:height\",266,\"~:x1\",322,\"~:y1\",286,\"~:x2\",620,\"~:y2\",552]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:layout-grid-dir\",\"^R\",\"~:flip-x\",null,\"^1E\",266,\"~:flip-y\",null,\"^T\",[]]]" + } + }, + "~:id": "~u0472abff-2573-8186-8007-961793e53f46", + "~:name": "Page 1" + } + }, + "~:id": "~u0472abff-2573-8186-8007-961793e53f45", + "~:options": { + "~:components-v2": true, + "~:base-font-size": "16px" + } + } +} \ No newline at end of file diff --git a/frontend/playwright/data/workspace/update-file-13415.json b/frontend/playwright/data/workspace/update-file-13415.json new file mode 100644 index 0000000000..866b4eb8eb --- /dev/null +++ b/frontend/playwright/data/workspace/update-file-13415.json @@ -0,0 +1,18 @@ +{ + "~:revn": 14, + "~:lagged": [ + { + "~:id": "~u0472abff-2573-8186-8007-96347d525f65", + "~:revn": 15, + "~:file-id": "~u0472abff-2573-8186-8007-961793e53f45", + "~:session-id": "~uf25e6d2f-d10c-8021-8007-96344433f08d", + "~:changes": [ + { + "~:type": "~:del-obj", + "~:page-id": "~u0472abff-2573-8186-8007-961793e53f46", + "~:id": "~uaef184da-e9c1-80f8-8007-961cf253d534" + } + ] + } + ] +} \ No newline at end of file diff --git a/frontend/playwright/ui/specs/workspace.spec.js b/frontend/playwright/ui/specs/workspace.spec.js index 8c20a9efbe..2d9188d171 100644 --- a/frontend/playwright/ui/specs/workspace.spec.js +++ b/frontend/playwright/ui/specs/workspace.spec.js @@ -492,3 +492,25 @@ test("Bug 8371 - Flatten option is not visible in context menu", async ({ .filter({ visible: true }), ).toBeVisible(); }); + +test("BUG 13415 - Grid layout overlay is not removed when deleting a board", async ({ + page, +}) => { + const workspacePage = new WasmWorkspacePage(page); + await workspacePage.setupEmptyFile(page); + await workspacePage.mockGetFile("workspace/get-file-13415.json"); + await workspacePage.mockRPC( + "update-file?id=*", + "workspace/update-file-13415.json", + ); + + await workspacePage.goToWorkspace(); + await workspacePage.clickLeafLayer("Board"); + + const currentRenderCount = await workspacePage.getRenderCount(); + await workspacePage.page.keyboard.press("Delete"); + + await workspacePage.waitForNextRender(currentRenderCount); + await workspacePage.hideUI(); + await expect(workspacePage.canvas).toHaveScreenshot(); +}); diff --git a/frontend/playwright/ui/specs/workspace.spec.js-snapshots/BUG-13415---Grid-layout-overlay-is-not-removed-when-deleting-a-board-1.png b/frontend/playwright/ui/specs/workspace.spec.js-snapshots/BUG-13415---Grid-layout-overlay-is-not-removed-when-deleting-a-board-1.png new file mode 100644 index 0000000000000000000000000000000000000000..199d021532d30e10175858a4a3846ac600f18686 GIT binary patch literal 9528 zcmeHNSx{3~7``YXrBdx8aa0Jcqf#}AiV&0ltwPa;5{E6-7=mcUKp-ee2oPXu)w0Pj zEk+hel|l+y0#=q-2w38Rl%+~o!V)EHYLY8Lu0gUi7uzSF>hz_Z^Yov)%$#$+^Znny z%t_zt?`vzb(guPc+daFz10ZN|8U$IoSuZfZiKq_w0D_i5d%QmlN+?qGZLxqHmqC}X zDnZ~E2j60!T5*-9G5-cZ&FrXo>*)A{1v=PmeRTf$y#*^#oAynb6>1$n1fNkH04#tG zW*Pu50qOu$5x4;el4wu_$pOfj%rpSG;v443M^Gn%Y=D@cFaR+DVq&HN5R*3)lh^CB zU+R@TfA<2+$bu5~b%+2gz~BBge*bYgnK}Rq_N~lr|-8m3&O0Kb=B{Bazwo{KKz(l&zoq)K#n{Ak|$taRRjPBfCH*i z|6?A&V)8okB>@?FF8~eA|Mp({vfKEk#PON@S_HX56{!?Wrr-08xw((#^6q6$SY^n1 zjk>cE$pZmTBMwhC$ZH1b6qV)W+{eFRGB2|Avs$ci@=i@n&8_1E)&*_67$BGZ?8) zf)rEluP^qzro2dDIjQX}=A&>Jaeo1=tW1~zc@T+py4dOq`T51n7$(lDq@+Z^Mfy1k zp3u6>+1XjCR4NR^S8^OY_9wuNm+kp&W504>1VKC#Hk)))_Yn61xm==E!<+DUqk+vA z3Wbr8k;DjBYG(WaJlwR!O?UtF+XFcb&$HR*vWH9)vMY<1I$Yhz7r4zY>1e+WgGim% zqUQj4ihFu``t$qq^742B`HRUv0=76iy9Y`K!#8gwozUyjKf;SDD?_MM(N$7tXeb`R zs-J8K$@C+6x`r*t6>yDkaYp$rCtMiW)MOiLN)>UtJC^+=^sA*~~8cDU!vASs&R z#w08f?KRSoBR+`HziK##5#{bXVuo()E6=K}t?B6Oq);dX&*Z-D?x0QW9qr4-V&&dD zckjk>IGvbK636he<}zIDF*keTI5(QtSUdiJOeRmqP2+I50l28BNZ&3>4YnNnZhcZ= zVIjuO&NU*VRKzpn-M)R>e6B{L@#f7`ZA^o_n!f9haA@k-Mg)W!K_&Ry(abd-hC+?Gn8n)1+^uq`Ge3?y~I! zJWgZd2m}l{xKr=d8Xxb#jW|5oSA7VZpe~n4R-HP9oe~ooVi81LS7$sxr`y{bI)A1S zlM>JI`f}~z!DB6W-J)&ioXvO_B9W4nL#0xQuC90-PRp_!`@Hmc1$=_3WR7QJa21-I zp{8SpswxjSxDl&PB4kF&?8BR7xw*MQky@j{7idSN-p39<6=#)|mGPUN%i3*y9>+vQ zG3Yd0G^u`SPGP8(NF+_B+4JYms|hTYmzjICRdV?NIqAnpR`O*_bRAGqJK