fix(studio-server): files route extensions#2267
Closed
miguel-heygen wants to merge 24 commits into
Closed
Conversation
…ource geometry The color-grading engine hides its source element with inline 'opacity: 0 !important', so any code that later reads or re-captures the element's opacity sees the hide instead of the authored value. Stamp the authored inline opacity on every [data-color-grading] element at document parse time (MutationObserver installed at runtime-bundle eval, before any composition script runs) and prefer the stamp when hiding/restoring. Also re-sync the grading canvas when the source's inline geometry mutates (rAF-throttled style observer): a studio drag moves the source via its transform, which fires no media event, so the visible canvas froze in place until the next seek.
Editing commits made elements vanish or dim permanently: invalidating the whole timeline (or re-running the composition script on soft reload) made GSAP re-capture tween bounds while runtime transients were live — the grading hide's opacity 0, or a mid-flight tween value — so from()/to() bounds got poisoned and the element rendered invisible from then on. - patch only the edited tween in place, never timeline.invalidate() - soft reload restores every animated element's authored inline opacity (after-write HTML first, parse-time stamp as fallback) before the script re-runs and re-captures - a paired x/y commit whose second half is a no-op (changed=false) still applies its instant patch, so panel edits reflect without deselecting
- resize on a scale-driven element commits per-axis scale (scaleX/scaleY for non-uniform drags) with keyframe normalization to the longhands, and clears the width/height draft so size can't double-apply; the intercept moves to gsapResizeIntercept.ts - the drop frame applies the corrected position synchronously in the same microtask chain as the soft reload (no network-window jump), and the draft pins the anchor through accumulated moves on scaled elements - gesture size/position math divides by the element's own content scale - convert-to-keyframes resolves current values through the property-group filter for ALL capture passes (opacity/rotationX from unrelated tweens no longer leak into a rotation commit), and a grading-hidden source's opacity is read from its canvas, not the inline hide - canvas pointer-down confirms the hover target with a synchronous hit-test before starting a marquee (stale-hover race lost selections)
The always-on crop lifted EVERY selected element's clip-path and restored only what it could parse as a px inset — selecting an element with a circle/polygon/percentage clip visually un-clipped it, and deselecting deleted the authored clip outright. readElementCropInsets is now tri-state (zeros = no clip, null = a clip the tool can't represent): uneditable clips get no lift and no handles, and the lift restores the pre-lift inline value verbatim unless a crop gesture actually committed.
commitStaticSet merged every property into the FIRST set found for the
selector: a panel W edit on an element whose only set was positional
produced tl.set("#el",{x,y,width}) — a mixed-group set the split
machinery exists to prevent — labeled "Set 3D transform" in undo.
Commits now batch per property group into a set that owns that group
(exact group match, then a mixed set already carrying the group, then a
fresh off-timeline gsap.set), with undo labels derived from the group
(Move layer / Resize layer / Rotate layer / Set 3D transform).
A parsed timeline set carries immediateRender in extras; the recast statement builder ALSO pushed the flag unconditionally, so every split/re-add of a set wrote 'immediateRender: true, immediateRender: true' into the file, doubling on each pass. Both writer twins (recast + acorn) now emit each vars key exactly once, properties winning over extras, and reconcileEditableProps skips newProps keys it already preserved.
gsap_from_opacity_noop matched 'opacity: 0' as a prefix — an authored 'opacity: 0.98' triggered the rule. One boundary-anchored predicate now owns the exactly-zero test for both block and inline declarations, including a block's last declaration without a trailing semicolon. domEditingDom now imports the grading contract attribute from core instead of re-declaring the literal.
The deselect restore read a ref recomputed from RENDER state — on a direct A→B selection switch, state re-syncs to B before A's effect cleanup runs, so after a committed crop gesture A was restored with B's crop string (or lost its crop when B had none). The committed value is now written at gesture-commit time (tri-state: none committed / crop removal / the exact committed string), so cleanup never touches render state. Adds component tests for lift/restore ordering, including the direct A→B switch and the uneditable-clip stand-down.
- merge gsapResizeIntercept's duplicate module imports - move the core-constant imports to the file headers (picker, domEditingDom) - justify the cross-realm HTMLElement casts (iframe-realm nodes fail instanceof; access is duck-typed)
Review follow-ups (both reviewers, all findings): - resize captures scope to the resize group: convert-to-keyframes resolvedFromValues and the whole-offset backfill pass the group filter, so an opacity-touching intro tween can't ride into a converted scale tween (the rotation fix's contract, now uniform across intercepts) - commitStaticSet resolves every group's target set BEFORE committing and coalesces groups landing on the same legacy mixed set into one commit — the second commit can no longer chase a stale group-derived id - installAuthoredOpacityCapture also stamps an element the moment it GAINS data-color-grading at runtime (attributeFilter), not just at insertion - both writer twins now share the same emitted-set dedupe shape - applySoftReload's positional tail becomes a SoftReloadOptions object - readAllAnimatedProperties builds the group-filtered key set immutably instead of deleting from the set mid-iteration - applyAuthoredInlineOpacity documents the priority-lossy round-trip - the marquee hit-test reads activeCompositionPathRef like its neighbors New tests: resize intercept (scale route + group filter + non-uniform longhands), after-write-HTML / stamp / empty-stamp opacity restore, the no-op-commit-with-missed-instant-patch soft-reload contract, and the runtime-gained-grading stamp.
Selecting a rotated (cropped) element appeared to straighten it: the crop dim and dashed window were drawn on the axis-aligned bounding box, so the bright window was a straight rectangle and the element's rotated corners were masked to near-black — while the DOM transform was untouched. clip-path applies in the element's LOCAL frame, before its transform, so the crop visualization now renders inside a container rotated with the element: readElementCropFrame decomposes the computed 2D matrix into angle + per-axis scale (element scale finally factored into the px mapping too) and 3D/unparseable transforms keep the axis-aligned presentation. Pointer deltas rotate into the element frame before the inset resolvers, so edge/pan drags track the rotated handles correctly.
… tail verify sample
…l flow to error-string matching Addresses non-blocking review feedback on #2256: the previous shape used err.message.includes(...) inside a catch to distinguish a real headroom violation from a statfsSync failure — a future message tweak would silently fail open (statfs-unsupported and headroom-violation would take the same code path). Now statfsSync failure returns early (skip the gate) and the headroom check/throw happens in plain control flow outside any try/catch.
…prefs What: extends three leaf modules to their final NLE-stack form, tests in the same change: timelineInspector (isAudioTimelineElement, resolveBeatSourceTrack), timelineZoom (zoom/pps math incl. computePinnedZoomPercent), and studioUiPreferences (persisted editor prefs). Why: leaf dependencies of the NLE timeline stack; landing them first keeps the later glue PRs to wiring. How: additive from the consumer side — every export main already uses is unchanged (typecheck against main's consumers passes untouched); every new export is exercised by a test in this PR. Test plan: bunx vitest run on the three test files; tsc --noEmit in packages/studio; fallow audit --base origin/main clean.
What: useTimelineSyncCallbacks gains resolveReloadSeekTime and revealIframe — the pure contract for where the playhead lands after a preview reload (pending seek > deep-link seek > store playhead, clamped) and for undoing refreshPlayer's iframe hide. Test suite included. Why: the NLE editor reloads the preview on every committed edit; this contract is the difference between "playhead restores" and "jumps to 0". How: additive exports on an existing hook file; main's consumers unchanged. Test plan: bunx vitest run useTimelineSyncCallbacks.test.ts; tsc --noEmit in packages/studio; fallow audit clean.
What: the studio-server files route at its final NLE-stack form, with its test suite (25 tests). Why: standalone package seam — the server-side dependency of the studio asset workflow, reviewable in isolation. How: additive route behavior; existing route consumers unchanged. Test plan: bunx vitest run src/routes/files.test.ts in packages/studio-server; tsc --noEmit in packages/studio-server; fallow audit clean.
Collaborator
Author
|
Closing this manual successor: Abhai interdiff found the branch was based on stale main and replayed unrelated version/CI content. The actual files route payload is intact; Ular/Graphite should retarget/restack the original stack onto current main and open the clean successor. |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
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.
Retargeted successor for closed Graphite PR #2194 after #2192 merged. Preserves the original commit/diff; no cherry-picks.