feat(sdk): stage 4 — canUndo/canRedo, removeElement GSAP cascade, override-set cleanup (#1431)

* feat(sdk): stage 4 — canUndo/canRedo, removeElement GSAP cascade, override-set cleanup

* docs(sdk): document cascadeRemoveAnimations bare-id v1 limitation for scoped ids

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>

* chore(sdk): remove sdk-status-report.txt from source tree

Internal planning artifact should not be committed to the repo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>

---------

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
This commit is contained in:
Vance Ingalls
2026-06-15 02:07:39 -07:00
committed by GitHub
co-authored by Claude Sonnet 4.6 Miguel Ángel
parent 5ecaac1fcb
commit 9627e03fa7
5 changed files with 186 additions and 3 deletions
+65
View File
@@ -232,3 +232,68 @@ describe("batch rollback on throw", () => {
expect(comp.getElement("hf-title")?.inlineStyles["color"]).toBe("#fff");
});
});
// ─── canUndo / canRedo ────────────────────────────────────────────────────────
describe("canUndo / canRedo", () => {
it("returns false before any mutation", async () => {
const comp = await openComposition(BASE_HTML);
expect(comp.canUndo()).toBe(false);
expect(comp.canRedo()).toBe(false);
});
it("canUndo true after a mutation, false after undoing back to start", async () => {
const comp = await openComposition(BASE_HTML);
comp.setStyle("hf-title", { color: "#ff0000" });
expect(comp.canUndo()).toBe(true);
expect(comp.canRedo()).toBe(false);
comp.undo();
expect(comp.canUndo()).toBe(false);
expect(comp.canRedo()).toBe(true);
});
it("canRedo cleared after a new mutation", async () => {
const comp = await openComposition(BASE_HTML);
comp.setStyle("hf-title", { color: "#ff0000" });
comp.undo();
expect(comp.canRedo()).toBe(true);
comp.setStyle("hf-title", { color: "#00ff00" });
expect(comp.canRedo()).toBe(false);
});
it("returns false in embedded (T3) mode — no history", async () => {
const comp = await openComposition(BASE_HTML, { overrides: {} });
comp.setStyle("hf-title", { color: "#ff0000" });
expect(comp.canUndo()).toBe(false);
expect(comp.canRedo()).toBe(false);
});
});
// ─── override-set orphan cleanup ──────────────────────────────────────────────
describe("override-set orphan cleanup on removeElement", () => {
it("purges property keys for removed element from the override-set", async () => {
const comp = await openComposition(BASE_HTML);
comp.setStyle("hf-title", { color: "#ff0000", fontSize: "96px" });
expect(Object.keys(comp.getOverrides())).toContain("hf-title.style.color");
comp.removeElement("hf-title");
const overrides = comp.getOverrides();
// removal marker present
expect(overrides["hf-title"]).toBeNull();
// orphan property keys gone
expect(Object.keys(overrides)).not.toContain("hf-title.style.color");
expect(Object.keys(overrides)).not.toContain("hf-title.style.fontSize");
});
it("property keys for other elements are unaffected", async () => {
const comp = await openComposition(BASE_HTML);
comp.setStyle("hf-title", { color: "#ff0000" });
comp.setStyle("hf-sub", { opacity: "1" });
comp.removeElement("hf-title");
const overrides = comp.getOverrides();
expect(overrides["hf-sub.style.opacity"]).toBe("1");
});
});