mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-09 20:07:39 +00:00
fix(studio): close the review findings that survived the stack
Selector reads now go through one inverse of `idSelector`. Every writer emits `[id="01-hook-hero"]` for an id a `#id` selector can't address, but the readers still matched `#id` only, so the post-commit keyframe-cache refresh, the AST load and the remove-all-keyframes clear all silently skipped exactly the ids `idSelector` was added to support. A keyframe merged from two tweens with different eases kept whichever ease iterated last. Readers that don't check `easeAmbiguous` showed a curve from a different animation than an edit would target, so the ambiguous flag now clears `ease` instead of leaving an arbitrary one behind. One tolerance for "the playhead is on this keyframe". The motion-path drag used 0.05% while the toolbar and the playhead apply used 1%, so a drag that landed a fraction of a percent off an authored waypoint skipped the update-point branch and appended a near-duplicate. `buildTemporalArcKeyframes` now owns the invariant and replaces any keyframe inside the tolerance, rather than trusting each caller's own pre-check. The pending-retime bookkeeping matches on keyframe identity, not just on "something is near that percentage" — an evenly spaced row cleared the entry off an unrelated sibling. The neighbour clamp composes pending destinations in before sorting, so a second drag can't cross a neighbour that already moved. Also: `keyframeCache`/`gsapAnimations` setters return the same state for a write that changes nothing (every no-op re-rendered every subscriber), the auto-expand set drops clips that left the source so an undo/paste under the same id expands again, `invalidateGsapCache` has a stable identity instead of re-creating the whole timeline edit context each render, the studio test hook deletes its window key rather than leaving it enumerable as undefined, and the past-last-row extrapolation documents why it uses TRACK_H where the pre-first-row branch uses row 0's own height. Covers `idFromSelector` round-trips, the insert boundary band across plain, expanded and unusable row heights, and the collapsed selection key for a colon-bearing element id.
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import {
|
||||
CLIP_Y,
|
||||
INSERT_BOUNDARY_BAND,
|
||||
getTimelineInsertBoundaryBand,
|
||||
RULER_H,
|
||||
TRACK_H,
|
||||
LANE_H,
|
||||
@@ -225,6 +228,7 @@ describe("getTimelineScrubTime", () => {
|
||||
clientX: 500,
|
||||
viewportLeft: 0,
|
||||
scrollLeft: 0,
|
||||
contentOrigin: GUTTER + TRACKS_LEFT_PAD,
|
||||
pixelsPerSecond: 0,
|
||||
duration: 10,
|
||||
}),
|
||||
@@ -232,3 +236,25 @@ describe("getTimelineScrubTime", () => {
|
||||
expect(at(origin + 250, Number.NaN)).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
// The only production hook keeping resolveInsertRow's band aligned with the
|
||||
// rendered clip inset once rows can be taller than TRACK_H. Pinned directly so a
|
||||
// change to CLIP_Y or the invalid-height fallback can't silently drift it.
|
||||
describe("getTimelineInsertBoundaryBand", () => {
|
||||
it("matches the fixed band for a plain track row", () => {
|
||||
expect(getTimelineInsertBoundaryBand(TRACK_H)).toBe(INSERT_BOUNDARY_BAND);
|
||||
expect(getTimelineInsertBoundaryBand(TRACK_H)).toBe(CLIP_Y / TRACK_H);
|
||||
});
|
||||
|
||||
it("shrinks as the row grows, so the band stays CLIP_Y pixels tall", () => {
|
||||
const expanded = TRACK_H + 2 * LANE_H;
|
||||
expect(getTimelineInsertBoundaryBand(expanded)).toBeCloseTo(CLIP_Y / expanded, 10);
|
||||
expect(getTimelineInsertBoundaryBand(expanded)).toBeLessThan(INSERT_BOUNDARY_BAND);
|
||||
});
|
||||
|
||||
it("falls back to the plain-track band for a height that is not usable", () => {
|
||||
for (const height of [0, -10, Number.NaN]) {
|
||||
expect(getTimelineInsertBoundaryBand(height)).toBe(INSERT_BOUNDARY_BAND);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user