fix(studio): undo right after an edit undoes that edit, even before its save lands - #4807
Merged
Merged
Conversation
Edit accuracy: 852 passing here, 846 on the base branchThe gate passes. Newly passing (6)
Quarantined, measured but not gated (2)
|
miguel-heygen
force-pushed
the
studio/undo-waits-for-committed-edit
branch
2 times, most recently
from
October 1, 2026 11:27
57498a4 to
ba5d8f9
Compare
miguel-heygen
force-pushed
the
studio/undo-waits-for-committed-edit
branch
3 times, most recently
from
October 1, 2026 16:34
e7a3399 to
9bfedc8
Compare
miguel-heygen
changed the base branch from
main
to
fix/watcher-ignores-atomic-temp
October 1, 2026 16:34
miguel-heygen
force-pushed
the
fix/watcher-ignores-atomic-temp
branch
from
October 1, 2026 16:53
47885aa to
d989d5e
Compare
miguel-heygen
force-pushed
the
studio/undo-waits-for-committed-edit
branch
from
October 1, 2026 16:58
9bfedc8 to
1bafb01
Compare
This was referenced Oct 1, 2026
miguel-heygen
force-pushed
the
studio/undo-waits-for-committed-edit
branch
from
October 1, 2026 18:41
4a4d750 to
b16fca2
Compare
5 tasks done
…s ends its pending edit
…ete counts as pending
miguel-heygen
force-pushed
the
studio/undo-waits-for-committed-edit
branch
from
October 1, 2026 21:22
c527274 to
0532755
Compare
miguel-heygen
marked this pull request as ready for review
October 1, 2026 22:21
This was referenced Oct 1, 2026
Merged
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
Cmd+Z pressed right after an edit, before the edit's save has landed, now undoes that edit. Before, it could undo nothing or the edit before it, and the edit's save then landed after the undo and stayed. A resize or move undone that way left the element in a third state (size reverted, position not), and a nudge burst was never undone at all.
Why
Undo first waits for pending edits (
waitForPendingDomEditSaves), but it only saw saves already queued in the DOM edit save queue or registered as pending edits. A canvas or panel edit counts from the moment its commit is called, and many commits spend a while before their first write: a resize parses the element's animations before it writes, and a nudge burst does not call its commit until 400 ms after the last arrow key. An undo in that window found nothing pending, stepped the history, and the save landed afterwards.How
trackedStudioEdit, counts an edit as pending from its call: the context's saving actions, timeline edits, the Delete and z-order hotkeys, and every GSAP script commit (so keyframe resets and deletes from the keyboard count too). The drain reports what happened, failures and conflicts alike, because the outside-change check needs them; only undo waits failures out. A conflict no longer cuts the drain short: it waits for the edits still running, then reports the conflict.Measurement
Per-frame probe in the built Studio on the edit accuracy bench's gsap=none fixtures, px placement, no rotation, root, 100% zoom. Undo is pressed on the frame after release (or after the last arrow key), then the file and the screen are read 8 s later.
The edit accuracy bench's sequence cases (gsap=none, 6 each), 3 jobs, all builds on base 3d8325e. "File checks" is every check except smoothness: tracking, drop, reload, undo.
On main the nudge-then-undo cases fail drop, reload and undo: the nudge is never undone. This PR alone breaks some of the other two rows. The cause is #4857's bug: each save's temp file reaches Studio as an outside change, and its drain now commits a nudge burst early, splitting it into two saves. With #4857 all three rows pass; #4857 is merged and this PR is on top of it.
A real outside change that arrives mid-burst still commits the burst: the drain exists so a local edit lands before Studio accepts someone else's file, and holding the burst would let the outside reload wipe it or let it save over the newer file. Only the false outside changes were the bug, and #4857 removes them. Smoothness fails in most cases on every build on this machine, main included.
In CI the edit accuracy gate now passes the six nudge-then-undo cases, and the last commit banks them in
baseline.jsonfrom that run.Test plan
DomEditContext.test.tsx: an edit committed through the context actions, whose save has not written anything yet, holds undo's drain until it settles; an edit that fails does not fail the drain. The first fails when the action is not marked as saving, the second when the registration keeps the rejection.useDomEditNudge.test.tsx: a burst counts as a pending edit from its first key until its save lands; undo's drain commits a burst still inside its wait and waits for its save. The first fails without the burst's registration, the second without the flush listener (the test runs on fake timers, so the burst's own 400 ms timer can't commit it). A commit that throws still ends the burst's pending edit; fails when that isn't in afinally.useEditHistoryActions.paint.test.tsx, over the real history engine:useAppHotkeys.previewForwarding.test.tsx: Cmd+Z settles pending edits before it steps the history. Fails when the hotkeys skip the settle.studioPendingEdits.test.ts: a drain that meets a conflict still waits for the edits that started while it ran (fails with the old early return);trackedStudioEditcounts a call until it settles and reports its failure.useGsapScriptCommits.test.tsx: a GSAP script commit counts as pending from its call until it lands. Fails without the tracker.Not covered by a test:
App.tsxitself is not mounted in tests, so its wiring (the settle into the hotkeys, the tracked Delete and z-order) is checked by the typecheck and by name only.Before
Undo pressed on the frame after a resize, with the save slowed to 1.5 s so the race is certain: the box ends in neither the original nor the resized state, and the file keeps the resize.
After
The same undo waits for the resize's save and undoes it: the box is back where it started and the file matches.