mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-07 18:26:17 +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:
@@ -11,18 +11,13 @@ import {
|
||||
elementCacheKeys,
|
||||
writeGsapAnimationsForElement,
|
||||
} from "./gsapKeyframeCacheHelpers";
|
||||
import { toClipKeyframes } from "./gsapShared";
|
||||
import { idFromSelector, toClipKeyframes } from "./gsapShared";
|
||||
import {
|
||||
deduplicateKeyframes,
|
||||
isStaticPositionHold,
|
||||
synthesizeFlatTweenKeyframes,
|
||||
} from "./gsapTweenSynth";
|
||||
|
||||
function extractIdFromSelector(selector: string): string | null {
|
||||
const match = selector.match(/^#([\w-]+)/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a tween's target selector to the ids of the element(s) it animates.
|
||||
* A bare `#id` resolves directly; anything else (a class like `.dot`, a group
|
||||
@@ -36,10 +31,13 @@ export function resolveSelectorElementIds(
|
||||
selector: string,
|
||||
doc: Document | null | undefined,
|
||||
): string[] {
|
||||
const bareId = selector.match(/^#([\w-]+)$/);
|
||||
if (bareId) return [bareId[1]];
|
||||
// A whole-selector id match (either shape) addresses exactly one element.
|
||||
const bareId = /^(#[\w-]+|\[id="(?:\\.|[^"\\])*"\])$/.test(selector)
|
||||
? idFromSelector(selector)
|
||||
: null;
|
||||
if (bareId) return [bareId];
|
||||
if (!doc) {
|
||||
const lead = extractIdFromSelector(selector);
|
||||
const lead = idFromSelector(selector);
|
||||
return lead ? [lead] : [];
|
||||
}
|
||||
const ids = new Set<string>();
|
||||
@@ -51,7 +49,7 @@ export function resolveSelectorElementIds(
|
||||
if (el.id) ids.add(el.id);
|
||||
}
|
||||
} catch {
|
||||
const lead = extractIdFromSelector(sel);
|
||||
const lead = idFromSelector(sel);
|
||||
if (lead) ids.add(lead);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user