🐛 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
This commit is contained in:
alonso.torres 2026-10-28 19:57:10 +01:00
parent 78feb03b83
commit b11e91f6e1
2 changed files with 41 additions and 5 deletions

View File

@ -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)))))))

View File

@ -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}