Skip to content

fix(studio-server): cascade GSAP cleanup when deleting subtrees - #3655

Merged
jrusso1020 merged 1 commit into
mainfrom
fix/gsap-subtree-deletion
Sep 4, 2026
Merged

jrusso1020 merged 1 commit into
mainfrom
fix/gsap-subtree-deletion

Conversation

@jrusso1020

Copy link
Copy Markdown
Collaborator

Deleting a parent through Studio's server fallback left its GSAP tweens and descendant tweens in the source. Share subtree cleanup with the HTML parser, matching each element's DOM id and stable id and visiting nested composition-template scripts. Reparse after each removal so multiple tweens are all removed.

If a selector still matches a surviving element, retain its animations to protect repeated composition instances. This intentionally handles direct id/stable-id selectors; broad or computed selectors remain unchanged. SDK undo/redo behavior is unchanged.

Builds on #1566 by @vanceingalls, updated for the parser/server package split.

Validation: 197 targeted parser/source-mutation/file-route tests passed (2 existing todo); parser and studio-server typechecks, oxlint/oxfmt, and commit hooks passed.

@miga-heygen miga-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at exact 69cc4ffef.

GSAP deletion cascade — when an element subtree is deleted, all GSAP tweens targeting any descendant's #id or [data-hf-id] selector are now removed, not just the root element's.

Key correctness checks:

  • Survivor walk prevents false removal: after removing the element, walkCompositionDescendants(doc, survivor) deletes any selector from the removal set that a surviving element still uses. This prevents erasing a tween that targets a shared selector in a different composition instance (e.g. two <template> instances with the same #box). Test "retains shared selectors used by a surviving composition instance" validates this.

  • Positional re-parse loop preserved: animation writer IDs are count-based and renumber after each removal. The for (;;) loop correctly re-parses after each tween deletion so later matches aren't skipped by stale IDs.

  • Nested composition templates: findScriptElementsDeep + walkCompositionDescendants traverse into <template data-composition-id> boundaries, so tweens inside nested composition templates are also cascaded. Test "cascades a stable-id deletion into nested composition template scripts" validates this.

  • No-op when element absent: if findTargetElement returns null, the function is never called and the source is returned unchanged. Test "does not strip scripts when the requested element is absent" validates this.

  • Dead comment removed: the ponytail comment in useElementLifecycleOps.ts documented the exact gap this PR closes — correctly removed.

+103/-40, 5 files. LGTM.

— Miga

@jrusso1020
jrusso1020 merged commit 3bc46a8 into main Sep 4, 2026
47 checks passed
@jrusso1020
jrusso1020 deleted the fix/gsap-subtree-deletion branch September 4, 2026 21:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants