feat(studio): design panel, timeline polish, feature flag [6/6]#1172
Conversation
674db24 to
a18980f
Compare
4bddbdd to
242d54b
Compare
a18980f to
6ecac38
Compare
044c22d to
a4b899e
Compare
4e65216 to
4137a18
Compare
a4b899e to
23563ad
Compare
4137a18 to
edc45a2
Compare
23563ad to
9fb06d5
Compare
82a5989 to
9a94cd9
Compare
9a94cd9 to
bc87ce5
Compare
9fb06d5 to
766ebbc
Compare
bc87ce5 to
55d81f3
Compare
0858462 to
c5b813b
Compare
1670e95 to
6f66981
Compare
0474d74 to
dcf1075
Compare
6f66981 to
96ccb09
Compare
jrusso1020
left a comment
There was a problem hiding this comment.
Final integration PR. +800/-77 across 20 files. The 77 deletions are where the most regression risk lives — they touch existing design-panel code. Feature flag defaulting false is the right safety net but doesn't substitute for verifying the flag-OFF path.
Critical — flag-OFF path regression check:
With the feature flag default-off, every change in this PR should be either:
- Behind the flag — code path that only executes when
flag === true. - Refactor-neutral — touches existing code but produces identical output when flag is off.
The 20 files + 77 deletions makes (2) hard to eyeball. The PR body's test plan ("Flag off → no keyframe UI") covers UI visibility but not behavior — for example, do the existing "Restore defaults" / drag / property-edit paths still produce the exact same script output with the flag off?
I'd want at minimum one test that:
- Sets flag = false.
- Performs the prior workflows (drag, color change, font change, restore defaults).
- Asserts the resulting
script.txtbyte-equals the pre-PR output for the same input sequence.
If that's not there, this PR is "trust me bro" on the flag-OFF path.
Flag naming + storage:
What's the flag called? Where does it live (env var, localStorage, server-side)? Two checks:
- Name should be
hyperframes_keyframes_v1or similar versioned slug so a future v2 can coexist. - Default-off in prod explicitly — if it's a localStorage flag, an early-adopter who toggled it on will keep that state across releases. Confirm the storage layer can't accidentally persist a user-toggle into other users' sessions.
Timeline polish — 48px tracks:
Verify this matches existing visual-density expectations. If older Studio sessions render 32px or 40px tracks, the change shifts vertical positions of every clip element. With keyframe diamonds added (from #1170), the layout system has new pressure. Worth a screenshot diff verification.
0.1% precision on diamond drag:
Reasonable for fine control. But: the parser stores percentages as Number.parseFloat(pctMatch[1]!) from a regex /^(\d+(?:\.\d+)?)%$/ (in #1167). A 0.1% drag at percentage 33.3 produces 33.3% — the regex accepts decimals so round-trip is safe. Good.
But — the mutation functions in #1167 use percentage exact-match (existingIdx !== -1). At 0.1% drag granularity, the user can drag from 33.3% to 33.4% and end up with two adjacent keyframes the parser treats as distinct (because 33.3 !== 33.4). Likely intended, but worth a note in the keyframe-edit UX: "drag with no Shift key snaps to 0.1%; with Shift snaps to 1%" or similar to prevent unintentional clutter.
clip-path + filter UI:
Two new editable property classes. Verify the preset list is sane (no broken CSS values that GSAP would silently drop) and that custom-input validation is in place for the freeform string case.
Review by Jerrai (hyperframes specialist)
dcf1075 to
90e4d0b
Compare
96ccb09 to
343b43b
Compare
90e4d0b to
8e3d552
Compare
343b43b to
1d95509
Compare
Revert totalTime nudge that caused black first frames in from() tweens. Keep stale CSS offset cleanup. Regenerate baselines for offset cleanup.
…od baselines Baselines regenerated inside Dockerfile.test on the devbox to match the current runtime init.ts changes. Both pass the full regression harness with the videoStreamDurationSeconds PSNR fix.
…ation U1: stripGsapTranslateFromTransform now rotates the offset vector by the element's CSS rotation angle before subtracting from m41/m42. Fixes elements drifting from cursor during drag when rotated. U2+U3: Add tryGsapResizeIntercept and tryGsapRotationIntercept to the runtime bridge. Resize and rotation handle changes now create keyframes via the same async pipeline as position drag. CSS path guards prevent double-persistence for GSAP-animated elements.
CSS compose order is translate → rotate → transform. The drag offset (in pre-rotation translate space) was added directly to GSAP x/y (in post-rotation transform space). Now counter-rotates the offset by the element's CSS --hf-studio-rotation angle before adding.
Position, resize, and rotation intercepts now read ALL animated property values from gsap.getProperty() at commit time and include them in the keyframe. Prevents other properties from jumping to interpolated values between surrounding keyframes when only one property (e.g., width) was explicitly changed.
1d95509 to
845b499
Compare

Summary
Property panel keyframe-aware editing. clip-path + filter UI. AnimationCard string props with presets. Timeline 48px tracks, diamond sizing, 0.1% precision. Feature flag defaults false.
Part 6 of 6 — final integration. Depends on PR 5.
Test plan