From b11e91f6e1ddace5d198ea17c00c39bbaac6a611 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 28 Oct 2026 19:57:10 +0100 Subject: [PATCH] :bug: Keep layout child grid assignment derived Changing a grid copy child to absolute touched its parent's grid cells and blocked later main cell updates. Separate parent sizing writes from derived grid assignment in the same undo transaction. Skip generic grid reassignment until the derived write, so it cannot touch the parent first. Add a real-event regression for clean parent cells, later main-cell propagation and exact undo/redo. It fails two of sixteen assertions before the fix and passes afterward. A live WASM check passes 27 checks, including exact page-object snapshots through undo and redo for both edits. Validate the final continuation on common JVM (2004 tests / 32919 assertions), common JS (1940 / 31522), frontend (1745 / 13569), backend (1793 / 9007) and exporter (16 / 40). All four modules pass lint and format. All six generated properties pass 40 programs at seed 712 in both runtimes. Golden data is unchanged. The final continuation benchmark passes with no regression flags. Lower quantile ratios against Phase 1e: main 10/100/1000 0.51/0.58/0.57, three levels 0.57, chain 0.53, library 0.57, many shapes 0.45, variant switch 0.66. Machine/JVM differences make these rough comparisons; baseline unchanged. AI-assisted-by: gpt-6 --- .../app/main/data/workspace/shape_layout.cljs | 11 +++--- .../completion_contract_test.cljs | 35 +++++++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index 0983e2c523..7087aef3f9 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -603,11 +603,12 @@ (dwsh/update-shapes parent-ids (fn [parent objects] - (-> parent - (fix-parent-sizing objects (set ids) attrs) - (cond-> (ctl/grid-layout? parent) - (ctl/assign-cells objects)))) - (merge options {:with-objects? true})) + (fix-parent-sizing parent objects (set ids) attrs)) + (merge options {:with-objects? true :skip-grid-reassignment? true})) + (dwsh/update-shapes + (filterv #(ctl/grid-layout? objects %) parent-ids) + ctl/assign-cells + (merge options {:with-objects? true :ignore-touched true})) (ptk/data-event :layout/update {:ids ids}) (dwu/commit-undo-transaction undo-id))))))) diff --git a/frontend/test/frontend_tests/composable_tests/completion_contract_test.cljs b/frontend/test/frontend_tests/composable_tests/completion_contract_test.cljs index 4ce7aeede9..7775e63e1c 100644 --- a/frontend/test/frontend_tests/composable_tests/completion_contract_test.cljs +++ b/frontend/test/frontend_tests/composable_tests/completion_contract_test.cljs @@ -33,6 +33,7 @@ [app.common.uuid :as uuid] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.reflow :as wrf] + [app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.texts :as dwt] [app.main.data.workspace.thumbnails-wasm :as dwthw] @@ -54,6 +55,40 @@ (t/use-fixtures :each ftm/fixture) +(t/deftest ^:async making-a-grid-copy-child-absolute-keeps-parent-cells-derived + (await + (mock/with-mocks* {rp/cmd! mock/rpc-cmd-mock rx/timer mock/timer-mock} + (let [situation (cg/world [1 0 {:layout :grid :layout-children true}]) + copy-id ((n/nth-copy-head-of "random-0" 0) situation) + child-id (first (:shapes (tm/shape-by-id situation copy-id))) + main-id (:shape-ref (tm/shape-by-id situation copy-id)) + sibling (first (remove #{child-id} (:shapes (tm/shape-by-id situation copy-id)))) + source-id (:shape-ref (tm/shape-by-id situation sibling)) + get-cell (fn [parent id] (first (filter #(some #{id} (:shapes %)) + (vals (:layout-grid-cells parent))))) + _ (ftm/install! situation)] + (t/is (= :settled (await (ftm/await-step [(ptk/data-event :layout/update {:ids [main-id copy-id]})])))) + (let [before (ftm/snapshot)] + (t/is (= :settled (await (ftm/await-step [(dwsl/update-layout-child [child-id] {:layout-item-absolute true})])))) + (let [after (ftm/snapshot) + file (ftm/current-file)] + (t/is (= #{:layout-item-absolute} (:touched (ths/get-shape-by-id file child-id)))) + (t/is (empty? (:touched (ths/get-shape-by-id file copy-id)))) + (t/is (nil? (get-cell (ths/get-shape-by-id file copy-id) child-id))) + (t/is (= :settled (await (ftm/await-step [dwu/undo])))) + (t/is (uc/same-state? before (ftm/snapshot))) + (t/is (= :settled (await (ftm/await-step [dwu/redo])))) + (t/is (uc/same-state? after (ftm/snapshot))) + (let [cell (get-cell (ths/get-shape-by-id (ftm/current-file) main-id) source-id)] + (t/is (some? cell)) + (t/is (= :settled (await (ftm/await-step [(dwsl/update-grid-cells main-id [(:id cell)] {:align-self :end})])))) + (t/is (= :end (:align-self (get-cell (ths/get-shape-by-id (ftm/current-file) copy-id) sibling)))) + (let [synced (ftm/snapshot)] + (t/is (= :settled (await (ftm/await-step [dwu/undo])))) + (t/is (uc/same-state? after (ftm/snapshot))) + (t/is (= :settled (await (ftm/await-step [dwu/redo])))) + (t/is (uc/same-state? synced (ftm/snapshot))))))))))) + (t/deftest ^:async deleting-a-flex-child-undoes-with-its-delayed-reflow (await (mock/with-mocks* {rp/cmd! mock/rpc-cmd-mock rx/timer mock/timer-mock}