mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
refactor(studio): single-source timeline id-resolution and resize-clamp math
Extract resolveTimelineIdForSelection so DOM-to-timeline id mapping lives in one place with a single sourceFile / activeCompPath / index.html fallback, fixing a sub-composition selection that previously diverged between callers. Extract shared start-trim delta helpers used by both single-clip and group resize, and remove the never-called refreshDomEditGroupSelectionsFromPreview.
This commit is contained in:
@@ -4,7 +4,11 @@ import type { StackingTimelineLayer, TimelineLayerId } from "./timelineTrackOrde
|
||||
import { resolveTimelineLayerStackingMove } from "./timelineLayerDrag";
|
||||
import type { TimelineStackingElement, TimelineStackingReorderIntent } from "./timelineStacking";
|
||||
|
||||
import { resolveTimelineMinDuration } from "./timelineGroupEditing";
|
||||
import {
|
||||
applyClipStartTrimDelta,
|
||||
clipStartTrimDeltaBounds,
|
||||
resolveTimelineMinDuration,
|
||||
} from "./timelineGroupEditing";
|
||||
|
||||
export {
|
||||
clampTimelineGroupResizeDelta,
|
||||
@@ -220,23 +224,15 @@ export function resolveTimelineResize(
|
||||
};
|
||||
}
|
||||
|
||||
const playbackRate = Math.max(0.1, input.playbackRate ?? 1);
|
||||
const maxLeftExtensionFromMedia =
|
||||
input.playbackStart != null ? input.playbackStart / playbackRate : Number.POSITIVE_INFINITY;
|
||||
const minDelta = -Math.min(input.start - input.minStart, maxLeftExtensionFromMedia);
|
||||
const maxDelta = input.duration - minDuration;
|
||||
const { minDelta, maxDelta } = clipStartTrimDeltaBounds(input, input.minStart, minDuration);
|
||||
const clampedDelta = clamp(deltaTime, minDelta, maxDelta);
|
||||
const nextStart = roundToCentiseconds(input.start + clampedDelta);
|
||||
const nextDuration = roundToCentiseconds(input.duration - clampedDelta);
|
||||
const nextPlaybackStart =
|
||||
input.playbackStart != null
|
||||
? roundToCentiseconds(Math.max(0, input.playbackStart + clampedDelta * playbackRate))
|
||||
: undefined;
|
||||
const trimmed = applyClipStartTrimDelta(input, clampedDelta);
|
||||
|
||||
return {
|
||||
start: nextStart,
|
||||
duration: nextDuration,
|
||||
playbackStart: nextPlaybackStart,
|
||||
start: roundToCentiseconds(trimmed.start),
|
||||
duration: roundToCentiseconds(trimmed.duration),
|
||||
playbackStart:
|
||||
trimmed.playbackStart != null ? roundToCentiseconds(trimmed.playbackStart) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,58 @@ export function resolveTimelineMinDuration(minDuration?: number): number {
|
||||
return Math.max(ABSOLUTE_TIMELINE_MIN_DURATION, minDuration ?? DEFAULT_TIMELINE_MIN_DURATION);
|
||||
}
|
||||
|
||||
/** Playback rate never drops to zero (would make media-in-point math divide by ~0). */
|
||||
function resolveTimelinePlaybackRate(rate?: number): number {
|
||||
return Math.max(0.1, rate ?? 1);
|
||||
}
|
||||
|
||||
interface TimelineStartTrimClip {
|
||||
start: number;
|
||||
duration: number;
|
||||
playbackStart?: number;
|
||||
playbackRate?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delta bounds for trimming a clip's START edge (shared by single-clip and group
|
||||
* resize). Left-bounded by how far the start can move toward `minStart` and by the
|
||||
* media in-point (`playbackStart / playbackRate`); right-bounded by `minDuration`.
|
||||
* Returned deltas are unrounded — callers round with their own centisecond helper.
|
||||
*/
|
||||
export function clipStartTrimDeltaBounds(
|
||||
clip: TimelineStartTrimClip,
|
||||
minStart: number,
|
||||
minDuration: number,
|
||||
): { minDelta: number; maxDelta: number } {
|
||||
const playbackRate = resolveTimelinePlaybackRate(clip.playbackRate);
|
||||
const maxLeftExtensionFromMedia =
|
||||
clip.playbackStart != null ? clip.playbackStart / playbackRate : Number.POSITIVE_INFINITY;
|
||||
return {
|
||||
minDelta: -Math.min(clip.start - minStart, maxLeftExtensionFromMedia),
|
||||
maxDelta: clip.duration - minDuration,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a start-edge delta to one clip (unrounded): moves the start, shrinks the
|
||||
* duration by the same amount, and shifts the media in-point by the delta scaled to
|
||||
* the playback rate (clamped at 0).
|
||||
*/
|
||||
export function applyClipStartTrimDelta(
|
||||
clip: TimelineStartTrimClip,
|
||||
delta: number,
|
||||
): { start: number; duration: number; playbackStart?: number } {
|
||||
const playbackRate = resolveTimelinePlaybackRate(clip.playbackRate);
|
||||
return {
|
||||
start: clip.start + delta,
|
||||
duration: clip.duration - delta,
|
||||
playbackStart:
|
||||
clip.playbackStart != null
|
||||
? Math.max(0, clip.playbackStart + delta * playbackRate)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export interface TimelineGroupTimingMember {
|
||||
start: number;
|
||||
duration: number;
|
||||
@@ -70,17 +122,10 @@ export function clampTimelineGroupResizeDelta(
|
||||
return roundTimelineTime(Math.max(rawDelta, minDelta));
|
||||
}
|
||||
|
||||
const minDelta = Math.max(
|
||||
...members.map((member) => {
|
||||
const playbackRate = Math.max(0.1, member.playbackRate ?? 1);
|
||||
const maxLeftExtensionFromMedia =
|
||||
member.playbackStart != null
|
||||
? member.playbackStart / playbackRate
|
||||
: Number.POSITIVE_INFINITY;
|
||||
return -Math.min(member.start, maxLeftExtensionFromMedia);
|
||||
}),
|
||||
);
|
||||
const maxDelta = Math.min(...members.map((member) => member.duration - minDuration));
|
||||
// Rigid group: the applied delta is bounded by the most-constrained member.
|
||||
const bounds = members.map((member) => clipStartTrimDeltaBounds(member, 0, minDuration));
|
||||
const minDelta = Math.max(...bounds.map((b) => b.minDelta));
|
||||
const maxDelta = Math.min(...bounds.map((b) => b.maxDelta));
|
||||
return roundTimelineTime(clamp(rawDelta, minDelta, maxDelta));
|
||||
}
|
||||
|
||||
@@ -102,14 +147,12 @@ export function resolveTimelineGroupResize(
|
||||
};
|
||||
}
|
||||
|
||||
const playbackRate = Math.max(0.1, member.playbackRate ?? 1);
|
||||
const trimmed = applyClipStartTrimDelta(member, delta);
|
||||
return {
|
||||
start: roundTimelineTime(member.start + delta),
|
||||
duration: roundTimelineTime(member.duration - delta),
|
||||
start: roundTimelineTime(trimmed.start),
|
||||
duration: roundTimelineTime(trimmed.duration),
|
||||
playbackStart:
|
||||
member.playbackStart != null
|
||||
? roundTimelineTime(Math.max(0, member.playbackStart + delta * playbackRate))
|
||||
: undefined,
|
||||
trimmed.playbackStart != null ? roundTimelineTime(trimmed.playbackStart) : undefined,
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user