mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-13 20:58:33 +00:00
* feat(cli): hyperframes timeline prints a project's tracks and clips * docs(cli): list the timeline command * feat(cli): timeline command reports unreadable lanes; add skill docs * fix(cli): timeline stays inside the project, shares one start cache, keeps small volumes * fix(cli): timeline resolves symlinks before the project containment check * fix(cli): timeline accepts project folders whose name starts with two dots * refactor(cli): timeline and compositions share the engine's start resolver * docs(skills): point agents at the timeline command where they decide how to read a project * fix(cli): timeline prints absolute main-timeline times and the owning file for nested clips Nested rows carry absStart/absEnd on the main timeline and their owning file, alongside existing local start/end. Text prints absolute time first, local time in parentheses. Adds jq/node one-liners for common queries, tested against a fixture. * fix(cli): timeline places nested media at the runtime start and tests its query one-liners A nested media clip with a negative literal data-start was reported later than it plays, because the raw start was clamped before the host offset was added. The skill's jq and node one-liners now run against the fixture in the test suite. * fix(cli): timeline clamps a negative media start to 0 when its host starts at 0 The runtime skips the host-offset formula for a host at t=0 and clamps the start, so the timeline command now does the same. * refactor(core): one media-start rule shared by the runtime and the timeline command The runtime and the CLI each carried a copy of the rule for when a nested media clip's start takes the host offset, and the copies had drifted. Both now call resolveMediaStartSeconds. * test(cli): timeline fixture for a media start written as an expression parseNumeric and the runtime's parseStrictFiniteTimingNumber are the same function, so the two callers cannot disagree on what counts as a literal; this pins the expression case.
41 lines
1.4 KiB
TypeScript
41 lines
1.4 KiB
TypeScript
export const MEDIA_START_BASIS_ATTR = "data-hf-media-start-basis";
|
|
|
|
export type MediaStartBasis = "local" | "global";
|
|
|
|
export function readMediaStartBasis(value: string | null | undefined): MediaStartBasis {
|
|
return value?.trim().toLowerCase() === "global" ? "global" : "local";
|
|
}
|
|
|
|
/** Resolve authored media time onto the root timeline. Nested media is local
|
|
* by default; only an explicit compatibility marker preserves a legacy
|
|
* root-global value. */
|
|
export function resolveAbsoluteMediaStartSeconds(input: {
|
|
authoredStart: number;
|
|
hostStart: number;
|
|
basis?: string | null;
|
|
}): number {
|
|
return readMediaStartBasis(input.basis) === "global"
|
|
? input.authoredStart
|
|
: input.hostStart + input.authoredStart;
|
|
}
|
|
|
|
/** The one rule for a media element's root-timeline start; the runtime and the CLI both call it.
|
|
* With no literal start, an auto-injected start, or a host at t<=0 there is nothing for the basis
|
|
* to disambiguate, so the ordinary start resolution applies. */
|
|
export function resolveMediaStartSeconds(input: {
|
|
authoredStart: number | null;
|
|
hostStart: number;
|
|
hasAutoStart: boolean;
|
|
basis?: string | null;
|
|
ordinaryStart: () => number;
|
|
}): number {
|
|
if (input.hasAutoStart || input.authoredStart == null || input.hostStart <= 0) {
|
|
return input.ordinaryStart();
|
|
}
|
|
return resolveAbsoluteMediaStartSeconds({
|
|
authoredStart: input.authoredStart,
|
|
hostStart: input.hostStart,
|
|
basis: input.basis,
|
|
});
|
|
}
|