fix: fork animations to run concurrently instead of serializing reconcile loop#29
Merged
Conversation
Deploying effex with
|
| Latest commit: |
3f3a839
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://27ba56b6.effex.pages.dev |
| Branch Preview URL: | https://fix-animation-serialization.effex.pages.dev |
Deploying effex-api with
|
| Latest commit: |
3f3a839
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://310dfdd4.effex-api.pages.dev |
| Branch Preview URL: | https://fix-animation-serialization.effex-api.pages.dev |
…cile loop Both ClientControlCtx and HydrationControlCtx were awaiting runEnterAnimation and runExitAnimation inline, which caused the sync loop in reconcile to serialize: item N's animation blocked item N+1's addSlot from starting. Made stagger configs functionally broken and exit animations cascade one-at-a-time instead of in parallel. Enter animations now fork into the slot's own scope. Exit animations fork into the parent scope so removeSlot returns immediately while the exit still plays; the slot's scope is closed inside the fork before the exit runs so any in-flight enter is interrupted first. All 50 existing Control/hydrate/Animation tests pass. Closes #25 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
e090853 to
0dd6440
Compare
Pulls the config-read + fork-enter and close-scope + config-read + exit + remove-DOM blocks (repeated three times each across ClientControlCtx and both HydrationControlCtx factories) into forkSlotEnter and forkSlotRemoval in a new slotAnimation.ts module. Net -116 lines across the two ControlCtx files. All 704 tests still pass. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Both
ClientControlCtx.addSlotandHydrationControlCtx.addSlotwere awaitingrunEnterAnimationinline withyield*. Same forrunExitAnimationinremoveSlot. Becausereconcile's sync loop yields sequentially, this caused every animation to serialize:[a, b, c]at once took3 × animationDurationwall time. Users saw items appear one after another when they'd configured all three to enter at the same time.staggerwas effectively broken — the configured stagger delay applied on top of each preceding animation's full duration, since addSlot didn't return until the animation completed.Fix
addSlotreturns as soon as the slot is registered and DOM inserted. If the slot is removed mid-enter, closing the slot scope interrupts the in-flight animation.removeSlotreturns immediately. The forked fiber closes the slot's scope first (interrupting any in-flight enter), then plays the exit animation, then removes the DOM node. Running on the parent scope means the exit continues even if a fresh slot re-uses the same key immediately.Applied to:
packages/dom/src/Control/ClientControlCtx.ts(bothaddSlotandremoveSlot)packages/dom/src/Control/HydrationControlCtx.ts— both factories (createClientLikeControlCtxandcreateHydrationControlCtx), bothaddSlotandremoveSlotInterface signature
removeSlotusesEffect.scopeto get the parent scope forEffect.forkIn, which pullsScope.Scopeinto R. The interface declaresEffect.Effect<void>(concrete no-deps signature), so a cast is applied at the end — same pattern the existingsubscribemethod uses in these files. Callers ofremoveSlot(i.e.,reconcile) always haveScope.Scopein their runtime context, so this is safe.Depends on
This PR is stacked on top of #23. It targets
fix/hydration-animation-config-lazy-readas base, so once #23 merges, this can rebase to main.Test plan
pnpm exec tsc --noEmit— cleanCloses #25
🤖 Generated with Claude Code