Repository navigation
perf(studio): parse saved HTML once when syncing timeline length - #5092
Merged
Merged
Conversation
miguel-heygen
marked this pull request as ready for review
October 5, 2026 22:51
Contributor
Edit accuracy: accurate 2055 (base branch 2055), smooth 1613 of thoseThe gate passes. Quarantined, measured but not gated (0) |
jrusso1020
approved these changes
Oct 6, 2026
jrusso1020
left a comment
Collaborator
There was a problem hiding this comment.
Approving at a0dd85d0. Each length sync now parses the saved HTML once, and the result matches the old code.
Checked
- One parse, used everywhere the length is worked out.
syncCompositionDurationWithEndparses once and reads both the furthest clip end and the root duration from that one document.syncCompositionDurationToContentwraps it, so move, resize, batch persist and link-edit all go through the single parse. Delete uses the same function and reusescontentEndfor the store readout, which before took a separatefurthestClipEndFromSourceparse. - Same result as before. I wrote a scratch test that runs the old path (
furthestClipEndFromSourceplus the 2-argumentsetCompositionDurationToContent) and the new path on 12 sources. The synced source andcontentEndmatched on every one:- empty, invalid markup, and no root;
- a root with no
data-duration, and a root withdata-duration="abc"; - an empty timeline, and a clip with no duration;
- a duration that needs rounding, and one that is already equal (
4.00vs 4); - single-quoted attributes;
- a nested composition whose inner clip runs past the inner end.
- No stale cache. Nothing is cached across calls. The document is parsed from the same string that is then patched, inside one call, so the parse can't be out of date.
- Reuse.
rootCompositionDurationOf(doc)is split out ofreadRootCompositionDurationrather than written again, andfurthestClipEndFromDocumentwas already the shared helper. RemovingfurthestClipEndFromSourcealso drops its Fallow entry.
Non-blocking
setCompositionDurationToContent's optionalrootDurationhas three states:undefinedreads the root itself,nullmeans no root, and a number is used as given. Thenull-vs-undefineddifference is easy to break later, though your mutation test catches??. A one-line doc comment on the parameter would help.- Unrelated to this PR:
extendCompositionDurationIfNeeded(timelineAssetDrop.ts) andextendRootDurationInSource(rootDuration.ts) are near copies of the same grow-only ratchet, differing only in rounding. They could be one helper someday.
Checks at a0dd85d0
- The 4 changed test files pass 81 of 81. The full Studio suite passes 7634 tests, and
tscis clean. - CI: all 93 checks pass, and one is skipped.
— Rames
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 changes
Timeline moves and trims parsed the saved HTML twice while synchronizing the composition length. Read the furthest clip end and the root duration from one parsed document, and reuse that result on delete. Saved markup, duration rounding, empty-timeline behavior, and delete rollback remain unchanged.
The optional precomputed duration preserves
nullas a missing root. Zero or invalid content ends return without parsing.Validation
At
a0dd85d0(main merged at43d07781):??, two parses for a rootless composition), and returning a zero content end on delete (the length readout stays at 6 instead of 4).Performance
Built Studio on Linux, the same launch project and one-clip timeline drag as the original measurement, 15 strictly alternating base/head rounds against
43d07781, each round started only below load 2.5. Each round drags the clip twice: the first edit after load, then the same clip again. Main-thread work is from CPU profiles of the release-to-settle window; a long task is a run of non-idle samples over 8 ms.User-visible work is not worse: the medians and p90s for this PR are at or below base for total work and longest task on both drags. The earlier five-round run showed a single head round with more long-task time; across 15 rounds per side that does not reproduce. The one head round above base's p90 (first drag, round 9: 360 ms total, longest task 101 ms) spent that task in React rendering and live GSAP retiming, which this PR does not touch, and base has outliers of the same size (second drag, round 2: 428 ms total, longest 93 ms). The table does not claim a total-work saving from this change: the parse saving is about 9 ms, and round-to-round spread is larger than that.
Before
Parent build after moving card 13 under the playhead.
After
Same move on the change (captured at
9350d4ac; the product code is unchanged since). Card placement and timeline match, and the saved HTML is byte-identical to the parent's.