@@ -53,60 +53,51 @@ export function createRuntimeStartTimeResolver(params: {
5353 ) ;
5454 } ;
5555
56- const resolveDurationForElement = ( element : Element ) : number | null => {
57- const cached = durationCache . get ( element ) ;
58- if ( cached !== undefined ) return cached ;
59- let resolved : number | null = null ;
60- const durationTiming = resolveAuthoredTimingWindow ( {
56+ const authoredAttr = ( element : Element , name : string ) =>
57+ includeAuthoredTimingAttrs ? element . getAttribute ( name ) : null ;
58+
59+ const resolveAuthoredDuration = ( element : Element ) : number | null =>
60+ resolveAuthoredTimingWindow ( {
6161 start : 0 ,
6262 duration : element . getAttribute ( "data-duration" ) ,
63- authoredDuration : includeAuthoredTimingAttrs
64- ? element . getAttribute ( "data-hf-authored-duration" )
65- : null ,
66- } ) ;
67- if ( durationTiming ?. duration != null && durationTiming . duration > 0 ) {
68- resolved = durationTiming . duration ;
63+ authoredDuration : authoredAttr ( element , "data-hf-authored-duration" ) ,
64+ } ) ?. duration ??
65+ resolveAuthoredTimingWindow ( {
66+ start : resolveStartForElementInternal ( element , 0 ) ,
67+ end : element . getAttribute ( "data-end" ) ,
68+ authoredEnd : authoredAttr ( element , "data-hf-authored-end" ) ,
69+ } ) ?. duration ??
70+ null ;
71+
72+ const positive = ( value : number | null | undefined ) : number | null =>
73+ value != null && Number . isFinite ( value ) && value > 0 ? value : null ;
74+
75+ const resolveRegisteredTimelineDuration = ( element : Element ) : number | null => {
76+ const timeline = timelineRegistry [ element . getAttribute ( "data-composition-id" ) ?? "" ] ;
77+ if ( ! timeline || typeof timeline . duration !== "function" ) return null ;
78+ try {
79+ return positive ( Number ( timeline . duration ( ) ) ) ;
80+ } catch ( err ) {
81+ // ignore broken timeline impls
82+ swallow ( "runtime.startResolver.site1" , err ) ;
83+ return null ;
6984 }
70- if ( resolved == null || resolved <= 0 ) {
71- const start = resolveStartForElementInternal ( element , 0 ) ;
72- const endTiming = resolveAuthoredTimingWindow ( {
73- start,
74- end : element . getAttribute ( "data-end" ) ,
75- authoredEnd : includeAuthoredTimingAttrs
76- ? element . getAttribute ( "data-hf-authored-end" )
85+ } ;
86+
87+ const resolveDurationForElement = ( element : Element ) : number | null => {
88+ const cached = durationCache . get ( element ) ;
89+ if ( cached !== undefined ) return cached ;
90+ const resolved =
91+ resolveAuthoredDuration ( element ) ??
92+ positive (
93+ isMediaElement ( element )
94+ ? resolveNaturalMediaTimelineDuration ( element , element . duration )
7795 : null ,
78- } ) ;
79- if ( endTiming ?. duration != null && endTiming . duration > 0 ) {
80- resolved = endTiming . duration ;
81- }
82- }
83- if ( ( resolved == null || resolved <= 0 ) && isMediaElement ( element ) ) {
84- resolved = resolveNaturalMediaTimelineDuration ( element , element . duration ) ;
85- }
86- if ( resolved == null || resolved <= 0 ) resolved = resolveTimedImageDurationSeconds ( element ) ;
87- if ( resolved == null || resolved <= 0 ) {
88- const compositionId = element . getAttribute ( "data-composition-id" ) ;
89- if ( compositionId ) {
90- const timeline = timelineRegistry [ compositionId ] ?? null ;
91- if ( timeline && typeof timeline . duration === "function" ) {
92- try {
93- const timelineDuration = Number ( timeline . duration ( ) ) ;
94- if ( Number . isFinite ( timelineDuration ) && timelineDuration > 0 ) {
95- resolved = timelineDuration ;
96- }
97- } catch ( err ) {
98- // ignore broken timeline impls
99- swallow ( "runtime.startResolver.site1" , err ) ;
100- }
101- }
102- }
103- }
104- if ( resolved != null && Number . isFinite ( resolved ) && resolved > 0 ) {
105- durationCache . set ( element , resolved ) ;
106- return resolved ;
107- }
108- durationCache . set ( element , null ) ;
109- return null ;
96+ ) ??
97+ positive ( resolveTimedImageDurationSeconds ( element ) ) ??
98+ resolveRegisteredTimelineDuration ( element ) ;
99+ durationCache . set ( element , resolved ) ;
100+ return resolved ;
110101 } ;
111102
112103 // A mounted composition root without its own `data-start` takes its timing from the host it was
@@ -201,12 +192,13 @@ export function createRuntimeStartTimeResolver(params: {
201192 } ) ;
202193 } ;
203194
195+ // Only an authored length cuts, as in the render; a GSAP length would cut what still plays.
204196 const resolveHostSlotsForElement = ( element : Element ) : HostSlot [ ] => {
205197 const slots : HostSlot [ ] = [ ] ;
206198 let root = element . closest ( "[data-composition-id]" ) ;
207199 for ( ; root ; root = root . parentElement ?. closest ( "[data-composition-id]" ) ?? null ) {
208200 const start = resolveStartForElementInternal ( root , 0 ) ;
209- const duration = resolveDurationForElement ( root ) ;
201+ const duration = resolveAuthoredDuration ( root ) ;
210202 slots . push ( {
211203 start,
212204 end : duration ? start + duration : Infinity ,
0 commit comments