🐛 Sync restored components after variant name changes

Restoring a variant whose container has gone converts its variant value
into its main name. Report the restored component id to the normal sync
cascade: its main is absent from the old page objects, so ordinary shape
change detection misses it. Copies receive the new name in the restore's
undo group, while authored name overrides remain intact.

F183: the portable regression fails one assertion before the fix and
passes all six after it. Original and shrunk programs pass. The focused
suite passes 138 tests / 2576 assertions with unchanged goldens. Live
WASM checks direct, nested and indirect copies, custom names and undo/redo.
Frontend passes 1536 tests / 9179 assertions; plugin 68 cases, lint and
formatting pass. The isolated Stage 3 check passes 53 tests / 373 assertions.
Common JVM/JS, backend and exporter suites pass.

Phase 1e benchmark ratios: mains 10/100/1000 .50/.56/.57, levels .51,
chain .51, library .52, many shapes .50, variant switch .62.

AI-assisted-by: GPT-6
This commit is contained in:
alonso.torres 2026-10-25 09:18:29 +01:00
parent b6de2582b1
commit 30c833d2e3
3 changed files with 29 additions and 0 deletions

View File

@ -64,3 +64,9 @@ combines copy-structure and nesting guards for normal frontend/pure nodes.
The duplicate builder checks the actual destination after ALT placement;
a duplicate at the page root remains allowed. Undo still restores existing
nesting through its explicit inverse flag, not through a forward bypass.
Component restoration reports its component id from components-changed
(F183). Its main does not exist in the old page objects, so add/mod-object
detection cannot find it. Restore can rename a variant when its container
has gone; the restored component and its dependents must sync in that same
undo group.

View File

@ -1233,6 +1233,10 @@
(into #{} xform shape-and-parents))
(when component-id #{component-id})))
(defmethod components-changed :restore-component
[_ {:keys [id]}]
#{id})
(defmethod components-changed :default
[_ _]
nil)

View File

@ -993,6 +993,25 @@
(ch/components-changed (:data file) (change {:type :assign :value {:opacity 0.5}}))))
(t/is (nil? (ch/components-changed (:data file) (change {:type :assign :value {:interactions []}}))))))
(t/deftest restoring-a-variant-without-its-container-syncs-the-new-main-name
(let [initial (tm/apply-to (n/make-variant-container "restore" [["b" "#ffffff"]])
(setup/empty-situation))
initial (tm/with-file initial
(thc/instantiate-component (tm/file initial) :sync-restore-v0-component :copy))
deletion (n/delete-component :sync-restore-v0-root)
deleted (-> initial (r/step deletion) r/settle)
restored (-> deleted (r/step (n/restore-component deletion)) r/settle)
main (tm/resolve-shape restored :sync-restore-v0-root)
copy (tm/resolve-shape restored :copy)]
(t/is (nil? (ths/get-shape (tm/file deleted) :sync-restore-vcontainer)))
(t/is (= "Board / b" (:name main)))
(t/is (= (:name main) (:name copy)))
(t/is (empty? (:touched copy)))
(t/is (= (thn/normalize-file (tm/file deleted))
(thn/normalize-file (tm/file (r/step restored (n/undo))))))
(t/is (= (thn/normalize-file (tm/file restored))
(thn/normalize-file (tm/file (-> restored (r/step (n/undo)) (r/step (n/redo)))))))))
(t/deftest test-sync-of-a-flip
;; the main flips its child as the editor does: a transform that scales
;; it by -1, which toggles :flip-x