fix(studio): undo and redo show on the next frame instead of after the server answers - #4798
Merged
Merged
Conversation
Edit accuracy: 494 passing here, 494 on the base branchThe gate passes. |
miguel-heygen
force-pushed
the
studio/fastpaint-nongsap
branch
from
October 1, 2026 03:14
1b34a68 to
e9a5316
Compare
miguel-heygen
marked this pull request as ready for review
October 1, 2026 03:24
somanshreddy
approved these changes
Oct 1, 2026
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.
What
Undo and redo of a style or position edit in a composition without a GSAP script now show their result on the first frame after the key, in the root composition and inside nested compositions. Before, the preview held the old state for 3 to 16 frames while Studio asked the server for the restored file. A nested one also reloaded the edited scene, and the timeline dropped to its empty state while it did.
Why
Two things made undo slow:
POST /history/step, read the file again, apply the restore. Every round trip was paid with the old state on screen. For a restore Studio itself wrote, it already holds both sides.How
undoes). It offers a step's restore only while nothing has overtaken its last view of the history (a claim or a step in flight clears it).undoes). Then the server's restore is applied as a diff from what the preview already shows, so a correct guess changes nothing. Otherwise (an agent, the CLI, another tab or a debounced save wrote in between, and the server undid that) the copied attributes are put back and the server's restore is applied against the file read from disk, as before this change. A refused step puts the copy back.data-hf-idonto every host the preview inlines it into ([data-composition-file]). It still reloads when the restore changes the sub-composition's own root (the preview rewrites that element), touches a sub-composition that has a GSAP script (only the active composition's script can be re-run in place), or touches a file the preview does not show.The in-place apply is the same function the undo stack PRs rework (#4783); this changes only how it picks its targets, at the top.
Measurement
Per-frame probe in the built Studio: a rAF sample of the target's box and computed style in the live preview, plus the timeline's clip count, from the key to 2.5 s after. The fixtures are the edit accuracy bench's gsap=none crop cases at 100% zoom, on the same machine, comparing main at the base commit against this branch. "First after-frame" is the first frame after the key that shows the settled state.
On the branch, no frame left the settled state after reaching it, and no preview document was swapped, in any row. With the move and nudge PR (#4799) applied on top, move and nudge undo and redo are also on frame 1, root and nested.
Edit accuracy gate in CI at this head (full grid): 494 of 660 cases pass, the same as the base baseline, with none regressed. The gsap=none crop rows (36 cases, every zoom) pass undo, drop and reload in 36 of 36, as on the base. The per-frame probe above was run at the first commit; the later commits change which restores are shown early (none with a GSAP script, none after an outside write), not how a shown one paints.
Test plan
useEditHistoryActions.paint.test.tsx: the real history engine, preview persistence and undo action over a live preview document. Undo and redo change the element in the key's own task, before the server answers. An undo pressed while a save is running shows nothing early. Each fails when the early show is removed or the pending-save check is dropped.useEditHistoryActions.paint.test.tsx: after a Studio edit, an outside write and a reload, Cmd+Z ends with the preview equal to the file the server restored. Fails without the predicted-entry check.useEditHistoryActions.test.tsx: a shown step is corrected from the server's restore as a diff from what is shown; when the server stepped another entry, the shown attributes are put back and the server's restore applies as read from disk; a refused step is put back. The second fails without the predicted-entry check.usePersistentEditHistory.test.ts: a step is predicted from what the tab wrote, and not while a claim or step is in flight or right after an outside change; the step's reply names the predicted entry. Fails when the outside change does not drop the prediction. An undo taken before the view caught up still reports its before and after; it fails without the fallback.gsapUndoRestore.test.ts: a sub-composition file's restore lands in place on every host that inlines it; a restore of its own root, or of a sub-composition with a GSAP script, reloads. They fail without the host scope, the root guard and the script guard.Before
The first repaints after Ctrl+Z, then Ctrl+Shift+Z, on a cropped element: the old crop is still on screen.
After
The first repaint after each key already shows the restored crop.