@editframe/elements 0.37.3-beta → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. package/dist/EF_FRAMEGEN.js +17 -14
  2. package/dist/EF_FRAMEGEN.js.map +1 -1
  3. package/dist/EF_RENDERING.js.map +1 -1
  4. package/dist/canvas/EFCanvas.d.ts +9 -2
  5. package/dist/canvas/EFCanvas.js +14 -4
  6. package/dist/canvas/EFCanvas.js.map +1 -1
  7. package/dist/canvas/EFCanvasItem.d.ts +4 -4
  8. package/dist/canvas/overlays/SelectionOverlay.d.ts +10 -2
  9. package/dist/canvas/overlays/SelectionOverlay.js +5 -12
  10. package/dist/canvas/overlays/SelectionOverlay.js.map +1 -1
  11. package/dist/canvas/overlays/overlayState.js.map +1 -1
  12. package/dist/canvas/selection/SelectionController.js.map +1 -1
  13. package/dist/elements/EFAudio.d.ts +1 -11
  14. package/dist/elements/EFAudio.js +2 -10
  15. package/dist/elements/EFAudio.js.map +1 -1
  16. package/dist/elements/EFCaptions.d.ts +5 -9
  17. package/dist/elements/EFCaptions.js +34 -11
  18. package/dist/elements/EFCaptions.js.map +1 -1
  19. package/dist/elements/EFImage.d.ts +10 -8
  20. package/dist/elements/EFImage.js +117 -32
  21. package/dist/elements/EFImage.js.map +1 -1
  22. package/dist/elements/EFMedia/AssetMediaEngine.js +2 -2
  23. package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
  24. package/dist/elements/EFMedia/BaseMediaEngine.js +15 -92
  25. package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
  26. package/dist/elements/EFMedia/BufferedSeekingInput.js +10 -11
  27. package/dist/elements/EFMedia/BufferedSeekingInput.js.map +1 -1
  28. package/dist/elements/EFMedia/{AssetIdMediaEngine.js → FileMediaEngine.js} +44 -24
  29. package/dist/elements/EFMedia/FileMediaEngine.js.map +1 -0
  30. package/dist/elements/EFMedia/JitMediaEngine.js +14 -13
  31. package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
  32. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -3
  33. package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
  34. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +12 -7
  35. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
  36. package/dist/elements/EFMedia/shared/timeoutUtils.js +44 -0
  37. package/dist/elements/EFMedia/shared/timeoutUtils.js.map +1 -0
  38. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +1 -1
  39. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
  40. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +4 -4
  41. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
  42. package/dist/elements/EFMedia.d.ts +14 -8
  43. package/dist/elements/EFMedia.js +52 -19
  44. package/dist/elements/EFMedia.js.map +1 -1
  45. package/dist/elements/EFPanZoom.d.ts +2 -2
  46. package/dist/elements/EFPanZoom.js +1 -1
  47. package/dist/elements/EFPanZoom.js.map +1 -1
  48. package/dist/elements/EFSourceMixin.js +16 -8
  49. package/dist/elements/EFSourceMixin.js.map +1 -1
  50. package/dist/elements/EFSurface.d.ts +7 -10
  51. package/dist/elements/EFSurface.js +4 -43
  52. package/dist/elements/EFSurface.js.map +1 -1
  53. package/dist/elements/EFTemporal.d.ts +33 -8
  54. package/dist/elements/EFTemporal.js +92 -40
  55. package/dist/elements/EFTemporal.js.map +1 -1
  56. package/dist/elements/EFText.d.ts +3 -0
  57. package/dist/elements/EFText.js +54 -21
  58. package/dist/elements/EFText.js.map +1 -1
  59. package/dist/elements/EFTextSegment.js +8 -4
  60. package/dist/elements/EFTextSegment.js.map +1 -1
  61. package/dist/elements/EFTimegroup.d.ts +26 -43
  62. package/dist/elements/EFTimegroup.js +295 -314
  63. package/dist/elements/EFTimegroup.js.map +1 -1
  64. package/dist/elements/EFVideo.d.ts +44 -42
  65. package/dist/elements/EFVideo.js +259 -172
  66. package/dist/elements/EFVideo.js.map +1 -1
  67. package/dist/elements/EFWaveform.d.ts +3 -8
  68. package/dist/elements/EFWaveform.js +18 -13
  69. package/dist/elements/EFWaveform.js.map +1 -1
  70. package/dist/elements/ElementPositionInfo.js.map +1 -1
  71. package/dist/elements/FetchMixin.js.map +1 -1
  72. package/dist/elements/TargetController.d.ts +0 -3
  73. package/dist/elements/TargetController.js +12 -35
  74. package/dist/elements/TargetController.js.map +1 -1
  75. package/dist/elements/TimegroupController.js.map +1 -1
  76. package/dist/elements/cloneFactoryRegistry.d.ts +14 -0
  77. package/dist/elements/cloneFactoryRegistry.js +15 -0
  78. package/dist/elements/cloneFactoryRegistry.js.map +1 -0
  79. package/dist/elements/renderTemporalAudio.js +8 -6
  80. package/dist/elements/renderTemporalAudio.js.map +1 -1
  81. package/dist/elements/setupTemporalHierarchy.js +62 -0
  82. package/dist/elements/setupTemporalHierarchy.js.map +1 -0
  83. package/dist/elements/updateAnimations.js +62 -87
  84. package/dist/elements/updateAnimations.js.map +1 -1
  85. package/dist/getRenderInfo.d.ts +3 -2
  86. package/dist/getRenderInfo.js +20 -4
  87. package/dist/getRenderInfo.js.map +1 -1
  88. package/dist/gui/ContextMixin.js +68 -12
  89. package/dist/gui/ContextMixin.js.map +1 -1
  90. package/dist/gui/Controllable.js +1 -1
  91. package/dist/gui/Controllable.js.map +1 -1
  92. package/dist/gui/EFActiveRootTemporal.d.ts +4 -4
  93. package/dist/gui/EFActiveRootTemporal.js.map +1 -1
  94. package/dist/gui/EFControls.d.ts +2 -2
  95. package/dist/gui/EFControls.js +2 -2
  96. package/dist/gui/EFControls.js.map +1 -1
  97. package/dist/gui/EFDial.d.ts +4 -4
  98. package/dist/gui/EFDial.js +12 -9
  99. package/dist/gui/EFDial.js.map +1 -1
  100. package/dist/gui/EFFilmstrip.d.ts +2 -0
  101. package/dist/gui/EFFilmstrip.js +18 -10
  102. package/dist/gui/EFFilmstrip.js.map +1 -1
  103. package/dist/gui/EFFitScale.d.ts +28 -4
  104. package/dist/gui/EFFitScale.js +88 -26
  105. package/dist/gui/EFFitScale.js.map +1 -1
  106. package/dist/gui/EFFocusOverlay.d.ts +4 -4
  107. package/dist/gui/EFFocusOverlay.js +3 -3
  108. package/dist/gui/EFFocusOverlay.js.map +1 -1
  109. package/dist/gui/EFOverlayItem.d.ts +4 -4
  110. package/dist/gui/EFOverlayLayer.d.ts +4 -4
  111. package/dist/gui/EFPause.d.ts +4 -4
  112. package/dist/gui/EFPause.js +1 -1
  113. package/dist/gui/EFPlay.d.ts +4 -4
  114. package/dist/gui/EFPlay.js +1 -1
  115. package/dist/gui/EFPreview.js +1 -1
  116. package/dist/gui/EFResizableBox.d.ts +4 -4
  117. package/dist/gui/EFResizableBox.js +5 -5
  118. package/dist/gui/EFResizableBox.js.map +1 -1
  119. package/dist/gui/EFScrubber.d.ts +4 -4
  120. package/dist/gui/EFScrubber.js +8 -13
  121. package/dist/gui/EFScrubber.js.map +1 -1
  122. package/dist/gui/EFTimeDisplay.d.ts +8 -4
  123. package/dist/gui/EFTimeDisplay.js +25 -7
  124. package/dist/gui/EFTimeDisplay.js.map +1 -1
  125. package/dist/gui/EFTimelineRuler.d.ts +4 -4
  126. package/dist/gui/EFTimelineRuler.js +3 -3
  127. package/dist/gui/EFTimelineRuler.js.map +1 -1
  128. package/dist/gui/EFToggleLoop.d.ts +4 -4
  129. package/dist/gui/EFToggleLoop.js +1 -1
  130. package/dist/gui/EFTogglePlay.d.ts +4 -4
  131. package/dist/gui/EFTogglePlay.js +1 -1
  132. package/dist/gui/EFTransformHandles.d.ts +4 -4
  133. package/dist/gui/EFTransformHandles.js +6 -6
  134. package/dist/gui/EFTransformHandles.js.map +1 -1
  135. package/dist/gui/EFWorkbench.d.ts +40 -36
  136. package/dist/gui/EFWorkbench.js +436 -822
  137. package/dist/gui/EFWorkbench.js.map +1 -1
  138. package/dist/gui/FitScaleHelpers.js.map +1 -1
  139. package/dist/gui/PlaybackController.d.ts +3 -8
  140. package/dist/gui/PlaybackController.js +59 -56
  141. package/dist/gui/PlaybackController.js.map +1 -1
  142. package/dist/gui/TWMixin.js +1 -1
  143. package/dist/gui/TWMixin.js.map +1 -1
  144. package/dist/gui/TargetOrContextMixin.js +43 -6
  145. package/dist/gui/TargetOrContextMixin.js.map +1 -1
  146. package/dist/gui/ef-theme.css +136 -0
  147. package/dist/gui/hierarchy/EFHierarchy.d.ts +2 -2
  148. package/dist/gui/hierarchy/EFHierarchy.js +14 -24
  149. package/dist/gui/hierarchy/EFHierarchy.js.map +1 -1
  150. package/dist/gui/hierarchy/EFHierarchyItem.d.ts +3 -3
  151. package/dist/gui/hierarchy/EFHierarchyItem.js +22 -10
  152. package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -1
  153. package/dist/gui/icons.js.map +1 -1
  154. package/dist/gui/previewSettingsContext.d.ts +18 -0
  155. package/dist/gui/previewSettingsContext.js.map +1 -1
  156. package/dist/gui/theme.js +34 -0
  157. package/dist/gui/theme.js.map +1 -0
  158. package/dist/gui/timeline/EFTimeline.d.ts +2 -2
  159. package/dist/gui/timeline/EFTimeline.js +70 -52
  160. package/dist/gui/timeline/EFTimeline.js.map +1 -1
  161. package/dist/gui/timeline/EFTimelineRow.d.ts +3 -1
  162. package/dist/gui/timeline/EFTimelineRow.js +55 -32
  163. package/dist/gui/timeline/EFTimelineRow.js.map +1 -1
  164. package/dist/gui/timeline/TrimHandles.d.ts +23 -9
  165. package/dist/gui/timeline/TrimHandles.js +224 -51
  166. package/dist/gui/timeline/TrimHandles.js.map +1 -1
  167. package/dist/gui/timeline/flattenHierarchy.js.map +1 -1
  168. package/dist/gui/timeline/timelineEditingContext.d.ts +34 -0
  169. package/dist/gui/timeline/timelineEditingContext.js +24 -0
  170. package/dist/gui/timeline/timelineEditingContext.js.map +1 -0
  171. package/dist/gui/timeline/timelineStateContext.js.map +1 -1
  172. package/dist/gui/timeline/tracks/AudioTrack.js +1 -1
  173. package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -1
  174. package/dist/gui/timeline/tracks/CaptionsTrack.d.ts +2 -3
  175. package/dist/gui/timeline/tracks/CaptionsTrack.js +17 -75
  176. package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -1
  177. package/dist/gui/timeline/tracks/EFThumbnailStrip.d.ts +52 -0
  178. package/dist/gui/timeline/tracks/EFThumbnailStrip.js +596 -0
  179. package/dist/gui/timeline/tracks/EFThumbnailStrip.js.map +1 -0
  180. package/dist/gui/timeline/tracks/HTMLTrack.js.map +1 -1
  181. package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -1
  182. package/dist/gui/timeline/tracks/TextTrack.d.ts +3 -2
  183. package/dist/gui/timeline/tracks/TextTrack.js +17 -43
  184. package/dist/gui/timeline/tracks/TextTrack.js.map +1 -1
  185. package/dist/gui/timeline/tracks/TimegroupTrack.d.ts +3 -4
  186. package/dist/gui/timeline/tracks/TimegroupTrack.js +33 -23
  187. package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -1
  188. package/dist/gui/timeline/tracks/TrackItem.d.ts +7 -9
  189. package/dist/gui/timeline/tracks/TrackItem.js +18 -17
  190. package/dist/gui/timeline/tracks/TrackItem.js.map +1 -1
  191. package/dist/gui/timeline/tracks/VideoTrack.d.ts +3 -3
  192. package/dist/gui/timeline/tracks/VideoTrack.js +11 -14
  193. package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -1
  194. package/dist/gui/timeline/tracks/WaveformTrack.js.map +1 -1
  195. package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -1
  196. package/dist/gui/timeline/tracks/waveformUtils.js +1 -1
  197. package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -1
  198. package/dist/gui/tree/EFTree.d.ts +4 -4
  199. package/dist/gui/tree/EFTree.js +8 -14
  200. package/dist/gui/tree/EFTree.js.map +1 -1
  201. package/dist/gui/tree/EFTreeItem.d.ts +4 -4
  202. package/dist/gui/tree/EFTreeItem.js +3 -3
  203. package/dist/gui/tree/EFTreeItem.js.map +1 -1
  204. package/dist/gui/tree/treeContext.js.map +1 -1
  205. package/dist/index.d.ts +10 -8
  206. package/dist/index.js +6 -5
  207. package/dist/index.js.map +1 -1
  208. package/dist/node.d.ts +2 -2
  209. package/dist/node.js +2 -2
  210. package/dist/preview/AdaptiveResolutionTracker.js +3 -3
  211. package/dist/preview/AdaptiveResolutionTracker.js.map +1 -1
  212. package/dist/preview/FrameController.d.ts +2 -17
  213. package/dist/preview/FrameController.js +40 -63
  214. package/dist/preview/FrameController.js.map +1 -1
  215. package/dist/preview/QualityUpgradeScheduler.d.ts +76 -0
  216. package/dist/preview/QualityUpgradeScheduler.js +158 -0
  217. package/dist/preview/QualityUpgradeScheduler.js.map +1 -0
  218. package/dist/preview/RenderContext.d.ts +119 -1
  219. package/dist/preview/RenderContext.js +21 -3
  220. package/dist/preview/RenderContext.js.map +1 -1
  221. package/dist/preview/RenderProfiler.js.map +1 -1
  222. package/dist/preview/RenderStats.js +85 -0
  223. package/dist/preview/RenderStats.js.map +1 -0
  224. package/dist/preview/encoding/canvasEncoder.js +2 -52
  225. package/dist/preview/encoding/canvasEncoder.js.map +1 -1
  226. package/dist/preview/encoding/mainThreadEncoder.js.map +1 -1
  227. package/dist/preview/encoding/workerEncoder.js.map +1 -1
  228. package/dist/preview/logger.js.map +1 -1
  229. package/dist/preview/previewSettings.d.ts +34 -0
  230. package/dist/preview/previewSettings.js +29 -17
  231. package/dist/preview/previewSettings.js.map +1 -1
  232. package/dist/preview/previewTypes.js +4 -4
  233. package/dist/preview/previewTypes.js.map +1 -1
  234. package/dist/preview/renderElementToCanvas.d.ts +44 -0
  235. package/dist/preview/renderElementToCanvas.js +72 -0
  236. package/dist/preview/renderElementToCanvas.js.map +1 -0
  237. package/dist/preview/renderTimegroupToCanvas.js +267 -145
  238. package/dist/preview/renderTimegroupToCanvas.js.map +1 -1
  239. package/dist/preview/renderTimegroupToCanvas.types.d.ts +30 -0
  240. package/dist/preview/renderTimegroupToVideo.js +85 -105
  241. package/dist/preview/renderTimegroupToVideo.js.map +1 -1
  242. package/dist/preview/{renderTimegroupToVideo.d.ts → renderTimegroupToVideo.types.d.ts} +9 -9
  243. package/dist/preview/renderVideoToVideo.js +286 -0
  244. package/dist/preview/renderVideoToVideo.js.map +1 -0
  245. package/dist/preview/renderers.js.map +1 -1
  246. package/dist/preview/rendering/ScaleConfig.js +74 -0
  247. package/dist/preview/rendering/ScaleConfig.js.map +1 -0
  248. package/dist/preview/rendering/inlineImages.js +1 -44
  249. package/dist/preview/rendering/inlineImages.js.map +1 -1
  250. package/dist/preview/rendering/loadImage.js +22 -0
  251. package/dist/preview/rendering/loadImage.js.map +1 -0
  252. package/dist/preview/rendering/renderToImageNative.js +3 -3
  253. package/dist/preview/rendering/renderToImageNative.js.map +1 -1
  254. package/dist/preview/rendering/serializeTimelineDirect.js +224 -68
  255. package/dist/preview/rendering/serializeTimelineDirect.js.map +1 -1
  256. package/dist/preview/statsTrackingStrategy.js +1 -101
  257. package/dist/preview/statsTrackingStrategy.js.map +1 -1
  258. package/dist/preview/workers/WorkerPool.js +0 -1
  259. package/dist/preview/workers/WorkerPool.js.map +1 -1
  260. package/dist/preview/workers/encoderWorkerInline.js +21 -54
  261. package/dist/preview/workers/encoderWorkerInline.js.map +1 -1
  262. package/dist/render/EFRenderAPI.d.ts +2 -1
  263. package/dist/render/EFRenderAPI.js +12 -36
  264. package/dist/render/EFRenderAPI.js.map +1 -1
  265. package/dist/render/getRenderData.js +4 -4
  266. package/dist/render/getRenderData.js.map +1 -1
  267. package/dist/style.css +114 -163
  268. package/dist/transcoding/cache/RequestDeduplicator.js +1 -0
  269. package/dist/transcoding/cache/RequestDeduplicator.js.map +1 -1
  270. package/dist/transcoding/types/index.d.ts +1 -1
  271. package/dist/transcoding/utils/UrlGenerator.js +10 -3
  272. package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
  273. package/dist/utils/LRUCache.js +1 -0
  274. package/dist/utils/LRUCache.js.map +1 -1
  275. package/dist/utils/frameTime.js +23 -1
  276. package/dist/utils/frameTime.js.map +1 -1
  277. package/package.json +21 -8
  278. package/scripts/build-css.js +8 -1
  279. package/test/setup.ts +0 -1
  280. package/test/useAssetMSW.ts +50 -0
  281. package/test/visualRegressionUtils.ts +23 -9
  282. package/dist/_virtual/rolldown_runtime.js +0 -27
  283. package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +0 -1
  284. package/dist/elements/EFThumbnailStrip.d.ts +0 -167
  285. package/dist/elements/EFThumbnailStrip.js +0 -731
  286. package/dist/elements/EFThumbnailStrip.js.map +0 -1
  287. package/dist/elements/SessionThumbnailCache.js +0 -154
  288. package/dist/elements/SessionThumbnailCache.js.map +0 -1
  289. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +0 -688
  290. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +0 -1
  291. package/dist/node_modules/react/cjs/react.development.js +0 -1521
  292. package/dist/node_modules/react/cjs/react.development.js.map +0 -1
  293. package/dist/node_modules/react/index.js +0 -13
  294. package/dist/node_modules/react/index.js.map +0 -1
  295. package/dist/node_modules/react/jsx-runtime.js +0 -13
  296. package/dist/node_modules/react/jsx-runtime.js.map +0 -1
  297. package/dist/preview/encoding/types.d.ts +0 -1
  298. package/dist/preview/renderTimegroupPreview.js +0 -686
  299. package/dist/preview/renderTimegroupPreview.js.map +0 -1
  300. package/dist/preview/renderTimegroupToCanvas.d.ts +0 -42
  301. package/dist/preview/rendering/renderToImage.d.ts +0 -2
  302. package/dist/preview/rendering/renderToImage.js +0 -95
  303. package/dist/preview/rendering/renderToImage.js.map +0 -1
  304. package/dist/preview/rendering/renderToImageForeignObject.js +0 -163
  305. package/dist/preview/rendering/renderToImageForeignObject.js.map +0 -1
  306. package/dist/preview/rendering/renderToImageNative.d.ts +0 -1
  307. package/dist/preview/rendering/svgSerializer.js +0 -43
  308. package/dist/preview/rendering/svgSerializer.js.map +0 -1
  309. package/dist/preview/rendering/types.d.ts +0 -2
  310. package/dist/preview/thumbnailCacheSettings.js +0 -52
  311. package/dist/preview/thumbnailCacheSettings.js.map +0 -1
  312. package/dist/sandbox/PlaybackControls.d.ts +0 -1
  313. package/dist/sandbox/PlaybackControls.js +0 -10
  314. package/dist/sandbox/PlaybackControls.js.map +0 -1
  315. package/dist/sandbox/ScenarioRunner.d.ts +0 -1
  316. package/dist/sandbox/ScenarioRunner.js +0 -1
  317. package/dist/sandbox/defineSandbox.d.ts +0 -1
  318. package/dist/sandbox/index.d.ts +0 -3
  319. package/dist/sandbox/index.js +0 -2
  320. package/test/EFVideo.framegen.browsertest.ts +0 -80
  321. package/test/thumbnail-performance-test.html +0 -116
@@ -1,22 +1,21 @@
1
1
  import { quantizeToFrameTimeS } from "../utils/frameTime.js";
2
+ import { getCloneFactory } from "./cloneFactoryRegistry.js";
2
3
  import { EF_RENDERING } from "../EF_RENDERING.js";
3
- import { evaluateAnimationVisibilityState, updateAnimations } from "./updateAnimations.js";
4
- import { parseTimeToMs } from "./parseTimeToMs.js";
5
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.95.0/helpers/decorate.js";
6
- import { EFTemporal, deepGetElementsWithFrameTasks, flushStartTimeMsCache, registerIsTimegroupCalculatingDuration, resetTemporalCache, shallowGetTemporalElements, timegroupContext } from "./EFTemporal.js";
7
4
  import { efContext } from "../gui/efContext.js";
8
- import { isContextMixin } from "../gui/ContextMixin.js";
9
5
  import { TWMixin } from "../gui/TWMixin2.js";
10
6
  import { isTracingEnabled, withSpan } from "../otel/tracingHelpers.js";
11
7
  import { FrameController, PRIORITY_DEFAULT } from "../preview/FrameController.js";
8
+ import { QualityUpgradeScheduler } from "../preview/QualityUpgradeScheduler.js";
9
+ import { updateAnimations } from "./updateAnimations.js";
10
+ import { parseTimeToMs } from "./parseTimeToMs.js";
11
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.95.0/helpers/decorate.js";
12
+ import { EFTemporal, flushStartTimeMsCache, registerIsTimegroupCalculatingDuration, resetTemporalCache, shallowGetTemporalElements, timegroupContext } from "./EFTemporal.js";
12
13
  import { renderTemporalAudio } from "./renderTemporalAudio.js";
13
14
  import { EFTargetable } from "./TargetController.js";
14
15
  import { deepGetMediaElements } from "./EFMedia.js";
15
16
  import { TimegroupController } from "./TimegroupController.js";
16
17
  import { getContainerInfoFromElement } from "./ContainerInfo.js";
17
18
  import { getPositionInfoFromElement } from "./ElementPositionInfo.js";
18
- import { captureFromClone, captureTimegroupAtTime } from "../preview/renderTimegroupToCanvas.js";
19
- import { renderTimegroupToVideo } from "../preview/renderTimegroupToVideo.js";
20
19
  import "../canvas/EFCanvas.js";
21
20
  import "../gui/hierarchy/EFHierarchy.js";
22
21
  import "../gui/EFFilmstrip.js";
@@ -29,10 +28,9 @@ import { LitElement, css, html } from "lit";
29
28
  import { customElement, property } from "lit/decorators.js";
30
29
 
31
30
  //#region src/elements/EFTimegroup.ts
32
- var _EFTimegroup;
33
31
  const log = debug("ef:elements:EFTimegroup");
34
- const INITIALIZER_ERROR_THRESHOLD_MS = 100;
35
- const INITIALIZER_WARN_THRESHOLD_MS = 10;
32
+ const INITIALIZER_ERROR_THRESHOLD_MS = 2e3;
33
+ const INITIALIZER_WARN_THRESHOLD_MS = 100;
36
34
  let durationCache = /* @__PURE__ */ new WeakMap();
37
35
  const flushDurationCache = () => {
38
36
  durationCache = /* @__PURE__ */ new WeakMap();
@@ -176,34 +174,14 @@ function evaluateSeekTarget(requestedTime, durationMs, fps) {
176
174
  return Math.max(0, Math.min(quantizedTime, durationMs / 1e3));
177
175
  }
178
176
  let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(LitElement))) {
179
- static {
180
- _EFTimegroup = this;
181
- }
182
177
  constructor(..._args) {
183
178
  super(..._args);
184
179
  this._timeGroupContext = this;
185
180
  this.efContext = this;
186
- this.mode = "contain";
187
- this.overlapMs = 0;
188
181
  this.fps = 30;
189
182
  this.autoInit = false;
190
183
  this.workbench = false;
191
184
  this.fit = "none";
192
- this.frameTask = (() => {
193
- const self = this;
194
- const taskObj = {
195
- run: () => {
196
- self.#frameTaskAbortController?.abort();
197
- self.#frameTaskAbortController = new AbortController();
198
- const signal = self.#frameTaskAbortController.signal;
199
- self.#frameTaskPromise = self.#runFrameTask(signal);
200
- taskObj.taskComplete = self.#frameTaskPromise;
201
- return self.#frameTaskPromise;
202
- },
203
- taskComplete: Promise.resolve()
204
- };
205
- return taskObj;
206
- })();
207
185
  this.seekTask = (() => {
208
186
  const self = this;
209
187
  const taskObj = {
@@ -251,25 +229,95 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
251
229
  }
252
230
  `;
253
231
  }
232
+ #trackedChildren = /* @__PURE__ */ new Set();
233
+ shouldAutoReady() {
234
+ return false;
235
+ }
236
+ #childReadyStateHandler = () => {
237
+ this.#recomputeAggregateReadyState();
238
+ };
239
+ #childContentChangeHandler = (e) => {
240
+ const detail = e.detail;
241
+ this.emitContentChange(detail?.reason ?? "content");
242
+ };
243
+ #recomputeAggregateReadyState() {
244
+ const children = shallowGetTemporalElements(this);
245
+ if (children.length === 0) {
246
+ this.setContentReadyState("ready");
247
+ return;
248
+ }
249
+ let hasLoading = false;
250
+ let hasError = false;
251
+ let hasIdle = false;
252
+ for (const child of children) {
253
+ const state$1 = child.contentReadyState;
254
+ if (state$1 === "loading") hasLoading = true;
255
+ else if (state$1 === "error") hasError = true;
256
+ else if (state$1 === "idle") hasIdle = true;
257
+ }
258
+ if (hasError) this.setContentReadyState("error");
259
+ else if (hasLoading) this.setContentReadyState("loading");
260
+ else if (hasIdle) this.setContentReadyState("loading");
261
+ else this.setContentReadyState("ready");
262
+ }
263
+ #syncChildListeners() {
264
+ const currentChildren = new Set(shallowGetTemporalElements(this));
265
+ for (const child of this.#trackedChildren) if (!currentChildren.has(child)) {
266
+ child.removeEventListener("readystatechange", this.#childReadyStateHandler);
267
+ child.removeEventListener("contentchange", this.#childContentChangeHandler);
268
+ }
269
+ for (const child of currentChildren) if (!this.#trackedChildren.has(child)) {
270
+ child.addEventListener("readystatechange", this.#childReadyStateHandler);
271
+ child.addEventListener("contentchange", this.#childContentChangeHandler);
272
+ }
273
+ this.#trackedChildren = currentChildren;
274
+ this.#recomputeAggregateReadyState();
275
+ }
276
+ /** @public */
277
+ #mode = "contain";
278
+ get mode() {
279
+ return this.#mode;
280
+ }
281
+ set mode(value) {
282
+ if (this.#mode === value) return;
283
+ const old = this.#mode;
284
+ this.#mode = value;
285
+ this.requestUpdate("mode", old);
286
+ if (this.getAttribute("mode") !== value) this.setAttribute("mode", value);
287
+ }
288
+ /** @public */
289
+ #overlapMs = 0;
290
+ get overlapMs() {
291
+ return this.#overlapMs;
292
+ }
293
+ set overlapMs(value) {
294
+ if (this.#overlapMs === value) return;
295
+ const old = this.#overlapMs;
296
+ this.#overlapMs = value;
297
+ this.requestUpdate("overlapMs", old);
298
+ const attrVal = value > 0 ? `${value}ms` : null;
299
+ if (attrVal && this.getAttribute("overlap") !== attrVal) this.setAttribute("overlap", attrVal);
300
+ else if (!attrVal && this.hasAttribute("overlap")) this.removeAttribute("overlap");
301
+ }
254
302
  #initializer;
255
303
  /**
256
304
  * Initializer function for setting up JavaScript behavior on this timegroup.
257
305
  * This function is called ONCE per instance - on the prime timeline when first connected,
258
306
  * and on each render clone when created.
259
- *
307
+ *
260
308
  * Use this to register frame callbacks, set up event listeners, or initialize state.
261
309
  * The same initializer code runs on both prime and clones, eliminating duplication.
262
- *
310
+ *
263
311
  * CONSTRAINTS:
264
312
  * - MUST be synchronous (no async/await, no Promise return)
265
313
  * - MUST complete in <100ms (error thrown) or <10ms (warning logged)
266
314
  * - Should only register callbacks and set up behavior, not do expensive work
267
- *
315
+ *
268
316
  * TIMING:
269
317
  * - If set before element connects to DOM: runs automatically after connectedCallback
270
318
  * - If set after element is connected: runs immediately
271
319
  * - Clones automatically copy and run the initializer when created
272
- *
320
+ *
273
321
  * @example
274
322
  * ```javascript
275
323
  * const tg = document.querySelector('ef-timegroup');
@@ -287,29 +335,12 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
287
335
  }
288
336
  set initializer(fn) {
289
337
  this.#initializer = fn;
290
- if (fn && this.isConnected && !this.#initializerHasRun) this.#initializerComplete = this.updateComplete.then(() => {
291
- this.#runInitializer();
292
- });
293
338
  }
294
339
  /**
295
340
  * Track if initializer has run on this instance to prevent double execution.
296
341
  * @internal
297
342
  */
298
343
  #initializerHasRun = false;
299
- /**
300
- * Promise that resolves when initializer completes.
301
- * Used by createRenderClone to ensure frame tasks are registered before rendering.
302
- * @internal
303
- */
304
- #initializerComplete;
305
- /**
306
- * Public accessor for initializer completion promise.
307
- * Allows createRenderClone to wait for initializer before rendering.
308
- * @internal
309
- */
310
- get initializerComplete() {
311
- return this.#initializerComplete;
312
- }
313
344
  attributeChangedCallback(name, old, value) {
314
345
  if (name === "mode" && value) this.mode = value;
315
346
  if (name === "overlap" && value) this.overlapMs = parseTimeToMs(value);
@@ -352,6 +383,18 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
352
383
  return this.#frameController;
353
384
  }
354
385
  /**
386
+ * Centralized quality upgrade scheduler for coordinating main-quality segment fetching.
387
+ * Lives alongside FrameController to manage background quality upgrades.
388
+ */
389
+ #qualityUpgradeScheduler = new QualityUpgradeScheduler({ requestFrameRender: () => this.requestFrameRender() });
390
+ /**
391
+ * Get the quality upgrade scheduler for background segment fetching.
392
+ * @public
393
+ */
394
+ get qualityUpgradeScheduler() {
395
+ return this.#qualityUpgradeScheduler;
396
+ }
397
+ /**
355
398
  * Query timegroup's readiness state for a given time.
356
399
  * Timegroups are always ready (no async preparation needed).
357
400
  * @public
@@ -406,9 +449,28 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
406
449
  incrementContentEpoch() {
407
450
  this.#contentEpoch++;
408
451
  }
452
+ /**
453
+ * Request a frame re-render at the current time.
454
+ *
455
+ * Use this when the source-to-timeline mapping has changed (e.g., sourcein/sourceout)
456
+ * but currentTimeMs hasn't. The FrameController only re-renders when currentTimeMs
457
+ * or durationMs change, so this provides a way for child elements to request a
458
+ * re-render when their internal state changes the visual output.
459
+ * @public
460
+ */
461
+ requestFrameRender() {
462
+ this.#runThrottledFrameTask();
463
+ }
409
464
  async #runThrottledFrameTask() {
410
465
  if (this.playbackController) return this.playbackController.runThrottledFrameTask();
411
- await this.frameTask.run();
466
+ try {
467
+ await this.#frameController.renderFrame(this.currentTimeMs, { onAnimationsUpdate: (root) => {
468
+ updateAnimations(root);
469
+ } });
470
+ } catch (error) {
471
+ if (error instanceof DOMException && error.name === "AbortError") return;
472
+ console.error("FrameController error:", error);
473
+ }
412
474
  }
413
475
  /** @public */
414
476
  set currentTime(time) {
@@ -432,8 +494,10 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
432
494
  this.#currentTime = seekTarget;
433
495
  this.#userTimeMs = seekTarget * 1e3;
434
496
  this.#seekInProgress = true;
435
- this.seekTask.run().catch(() => {}).finally(() => {
497
+ Promise.resolve(this.seekTask.run()).catch(() => {}).finally(async () => {
436
498
  this.#seekInProgress = false;
499
+ const { updateAnimations: updateAnimations$1 } = await import("./updateAnimations.js");
500
+ updateAnimations$1(this);
437
501
  if (this.#pendingSeekTime !== void 0 && this.#pendingSeekTime !== seekTarget) {
438
502
  const pendingTime = this.#pendingSeekTime;
439
503
  this.#pendingSeekTime = void 0;
@@ -474,7 +538,7 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
474
538
  *
475
539
  * Combines seeking (Purpose 3) with frame rendering (Purpose 4) to ensure
476
540
  * all visible elements are ready after the seek completes.
477
- *
541
+ *
478
542
  * Updates both the source time AND userTimeMs (what the preview displays).
479
543
  *
480
544
  * @param timeMs - Time in milliseconds to seek to
@@ -486,12 +550,9 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
486
550
  this.currentTimeMs = timeMs;
487
551
  await this.seekTask.taskComplete;
488
552
  if (this.playbackController) this.saveTimeToLocalStorage(this.currentTime);
489
- await this.frameTask.taskComplete;
490
- const visibleElements = this.#evaluateVisibleElementsForFrame();
491
- await Promise.all(visibleElements.map(async (element) => {
492
- if ("waitForFrameReady" in element && typeof element.waitForFrameReady === "function") await element.waitForFrameReady();
493
- else await element.updateComplete;
494
- }));
553
+ await this.#frameController.renderFrame(timeMs, { onAnimationsUpdate: (root) => {
554
+ updateAnimations(root);
555
+ } });
495
556
  }
496
557
  /**
497
558
  * Optimized seek for render loops.
@@ -499,9 +560,9 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
499
560
  * - Skips waitForMediaDurations (already loaded at render setup)
500
561
  * - Skips localStorage persistence
501
562
  * - Uses FrameController for centralized element coordination
502
- *
563
+ *
503
564
  * Still waits for all content to be ready (Lit updates, element preparation, rendering).
504
- *
565
+ *
505
566
  * @param timeMs - Time in milliseconds to seek to
506
567
  * @internal
507
568
  */
@@ -509,15 +570,21 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
509
570
  const newTime = timeMs / 1e3;
510
571
  this.#userTimeMs = timeMs;
511
572
  this.#currentTime = newTime;
573
+ if (this.playbackController) this.playbackController.currentTime = newTime;
574
+ this._setLocalTimeMs(timeMs);
512
575
  this.requestUpdate("currentTime");
513
576
  await this.updateComplete;
514
577
  const allLitElements = this.#getAllLitElementDescendants();
515
578
  await Promise.all(allLitElements.map((el) => el.updateComplete));
579
+ await Promise.all(allLitElements.map((el) => el.updateComplete));
516
580
  const textElements = allLitElements.filter((el) => el.tagName === "EF-TEXT");
517
- if (textElements.length > 0) await Promise.all(textElements.map((el) => {
518
- if ("whenSegmentsReady" in el && typeof el.whenSegmentsReady === "function") return el.whenSegmentsReady();
519
- return Promise.resolve();
520
- }));
581
+ if (textElements.length > 0) {
582
+ await Promise.all(textElements.map((el) => {
583
+ if ("whenSegmentsReady" in el && typeof el.whenSegmentsReady === "function") return el.whenSegmentsReady();
584
+ return Promise.resolve();
585
+ }));
586
+ this.offsetHeight;
587
+ }
521
588
  await this.#frameController.renderFrame(timeMs, {
522
589
  waitForLitUpdate: false,
523
590
  onAnimationsUpdate: (root) => {
@@ -530,36 +597,21 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
530
597
  /**
531
598
  * Collects all LitElement descendants recursively.
532
599
  * Used by seekForRender to ensure all reactive elements have updated.
533
- * Optimized to filter by temporal visibility and skip hidden subtrees.
600
+ * Prunes subtrees of temporally-invisible elements their Lit updates
601
+ * still fire via microtasks (OwnCurrentTimeController), so skipping
602
+ * the explicit await is safe.
534
603
  */
535
604
  #getAllLitElementDescendants() {
536
605
  const result = [];
537
606
  const currentTimeMs = this.currentTimeMs;
538
- let hiddenSubtreesSkipped = 0;
539
- let temporallyFilteredOut = 0;
540
- let totalLitElements = 0;
541
607
  const walk = (el) => {
542
- if (el instanceof HTMLElement && el.style.display === "none") {
543
- hiddenSubtreesSkipped++;
544
- return;
545
- }
546
- if (el instanceof HTMLElement) {
547
- const style = getComputedStyle(el);
548
- if (style.display === "none" || style.visibility === "hidden") {
549
- hiddenSubtreesSkipped++;
550
- return;
551
- }
552
- }
553
608
  for (const child of el.children) {
554
- if (child instanceof LitElement) {
555
- totalLitElements++;
556
- if ("startTimeMs" in child && "endTimeMs" in child) {
557
- const startMs = child.startTimeMs ?? -Infinity;
558
- const endMs = child.endTimeMs ?? Infinity;
559
- if (currentTimeMs >= startMs && currentTimeMs <= endMs) result.push(child);
560
- else temporallyFilteredOut++;
561
- } else result.push(child);
609
+ if ("startTimeMs" in child && "endTimeMs" in child) {
610
+ const startMs = child.startTimeMs ?? -Infinity;
611
+ const endMs = child.endTimeMs ?? Infinity;
612
+ if (endMs > startMs && (currentTimeMs < startMs || currentTimeMs >= endMs)) continue;
562
613
  }
614
+ if (child instanceof LitElement) result.push(child);
563
615
  walk(child);
564
616
  }
565
617
  };
@@ -639,6 +691,8 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
639
691
  flushSequenceDurationCache();
640
692
  flushStartTimeMsCache();
641
693
  this.requestUpdate();
694
+ this.#syncChildListeners();
695
+ this.emitContentChange("structure");
642
696
  };
643
697
  /** @internal */
644
698
  loadTimeFromLocalStorage() {
@@ -654,8 +708,10 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
654
708
  }
655
709
  connectedCallback() {
656
710
  super.connectedCallback();
711
+ if (this.canvasPreviewActive) return;
657
712
  this.updateComplete.then(() => {
658
713
  this.#runInitializer();
714
+ this.#syncChildListeners();
659
715
  });
660
716
  requestAnimationFrame(() => {
661
717
  requestAnimationFrame(() => {
@@ -679,9 +735,7 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
679
735
  #setupPlaybackListener() {
680
736
  if (this.#playbackListener || !this.playbackController) return;
681
737
  this.#playbackListener = (event) => {
682
- if (event.property === "currentTimeMs" && typeof event.value === "number") {
683
- if (this.playing) this.#userTimeMs = event.value;
684
- }
738
+ if (event.property === "currentTimeMs" && typeof event.value === "number") this.#userTimeMs = event.value;
685
739
  };
686
740
  this.playbackController.addListener(this.#playbackListener);
687
741
  }
@@ -703,79 +757,29 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
703
757
  }
704
758
  disconnectedCallback() {
705
759
  super.disconnectedCallback();
760
+ if (this.canvasPreviewActive) return;
706
761
  this.#resizeObserver?.disconnect();
707
762
  this.#removePlaybackListener();
708
- }
709
- /**
710
- * Capture the timegroup at a specific timestamp as a canvas.
711
- * Does NOT modify currentTimeMs - captures are rendered independently.
712
- *
713
- * @param options - Capture options including timeMs, scale, contentReadyMode
714
- * @returns Promise resolving to an HTMLCanvasElement with the captured frame
715
- * @public
716
- */
717
- async captureAtTime(options) {
718
- return captureTimegroupAtTime(this, options);
719
- }
720
- /**
721
- * Capture multiple timestamps as canvas thumbnails in a single batch.
722
- *
723
- * CLONE-TIMELINE ARCHITECTURE:
724
- * Creates a single render clone and reuses it across all captures.
725
- * Prime-timeline is NEVER seeked - user can continue previewing/editing during capture.
726
- *
727
- * @param timestamps - Array of timestamps (in milliseconds) to capture
728
- * @param options - Capture options (scale, contentReadyMode, blockingTimeoutMs)
729
- * @returns Promise resolving to array of HTMLCanvasElements
730
- * @public
731
- */
732
- async captureBatch(timestamps, options = {}) {
733
- if (timestamps.length === 0) return [];
734
- const { scale = .25, contentReadyMode = "immediate", blockingTimeoutMs = 5e3 } = options;
735
- const batchStartTime = performance.now();
736
- const cloneStartTime = performance.now();
737
- const { clone: renderClone, container: renderContainer, cleanup: cleanupRenderClone } = await this.createRenderClone();
738
- const cloneTime = performance.now() - cloneStartTime;
739
- const prefetchStartTime = performance.now();
740
- const videoElements = renderClone.querySelectorAll("ef-video");
741
- if (videoElements.length > 0) await Promise.all(Array.from(videoElements).map((video) => video.prefetchScrubSegments(timestamps)));
742
- const prefetchTime = performance.now() - prefetchStartTime;
743
- const canvases = [];
744
- let totalSeekTime = 0;
745
- let totalCaptureTime = 0;
746
- try {
747
- for (let i = 0; i < timestamps.length; i++) {
748
- const timeMs = timestamps[i];
749
- const seekStart = performance.now();
750
- await renderClone.seekForRender(timeMs);
751
- totalSeekTime += performance.now() - seekStart;
752
- const captureStart = performance.now();
753
- const canvas = await captureFromClone(renderClone, renderContainer, {
754
- scale,
755
- contentReadyMode,
756
- blockingTimeoutMs,
757
- originalTimegroup: this
758
- });
759
- totalCaptureTime += performance.now() - captureStart;
760
- canvases.push(canvas);
761
- }
762
- return canvases;
763
- } finally {
764
- const totalTime = performance.now() - batchStartTime;
765
- console.log(`[captureBatch] ${timestamps.length} frames: clone=${cloneTime.toFixed(0)}ms, prefetch=${prefetchTime.toFixed(0)}ms, seek=${totalSeekTime.toFixed(0)}ms, capture=${totalCaptureTime.toFixed(0)}ms, total=${totalTime.toFixed(0)}ms`);
766
- cleanupRenderClone();
763
+ for (const child of this.#trackedChildren) {
764
+ child.removeEventListener("readystatechange", this.#childReadyStateHandler);
765
+ child.removeEventListener("contentchange", this.#childContentChangeHandler);
767
766
  }
767
+ this.#trackedChildren.clear();
768
+ this.#qualityUpgradeScheduler.dispose();
768
769
  }
769
770
  /**
770
771
  * Render the timegroup to an MP4 video file and trigger download.
771
772
  * Captures each frame at the specified fps, encodes using WebCodecs via
772
773
  * MediaBunny, and downloads the resulting video.
773
- *
774
+ *
775
+ * Uses dynamic import to only load render utilities in browser context.
776
+ *
774
777
  * @param options - Rendering options (fps, codec, bitrate, filename, etc.)
775
778
  * @returns Promise that resolves when video is downloaded
776
779
  * @public
777
780
  */
778
781
  async renderToVideo(options) {
782
+ const { renderTimegroupToVideo } = await import("../preview/renderTimegroupToVideo.js");
779
783
  return renderTimegroupToVideo(this, options);
780
784
  }
781
785
  /**
@@ -808,7 +812,8 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
808
812
  for (let i = 0; i < originalCaptions.length && i < cloneCaptions.length; i++) {
809
813
  const origCap = originalCaptions[i];
810
814
  const cloneCap = cloneCaptions[i];
811
- if (origCap.captionsData) cloneCap.captionsData = origCap.captionsData;
815
+ const loadedData = origCap.captionsData ?? origCap.unifiedCaptionsDataTask?.value;
816
+ if (loadedData) cloneCap.captionsData = loadedData;
812
817
  }
813
818
  }
814
819
  /**
@@ -873,10 +878,10 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
873
878
  * @internal
874
879
  */
875
880
  async #copyInitializersToClone(original, clone) {
876
- const initializerPromises = [];
877
881
  if (original.initializer) {
878
882
  clone.initializer = original.initializer;
879
- if (clone.initializerComplete) initializerPromises.push(clone.initializerComplete);
883
+ await clone.updateComplete;
884
+ clone.#runInitializer();
880
885
  }
881
886
  const originalNested = Array.from(original.querySelectorAll("ef-timegroup"));
882
887
  const cloneNested = Array.from(clone.querySelectorAll("ef-timegroup"));
@@ -885,29 +890,104 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
885
890
  const cloneNestedItem = cloneNested[i];
886
891
  if (origNested.initializer) {
887
892
  cloneNestedItem.initializer = origNested.initializer;
888
- if (cloneNestedItem.initializerComplete) initializerPromises.push(cloneNestedItem.initializerComplete);
893
+ await cloneNestedItem.updateComplete;
894
+ cloneNestedItem.#runInitializer();
889
895
  }
890
896
  }
891
- if (initializerPromises.length > 0) await Promise.all(initializerPromises);
892
897
  }
893
898
  /**
894
899
  * Create an independent clone of this timegroup for rendering.
895
900
  * The clone is a fully functional ef-timegroup with its own animations
896
901
  * and time state, isolated from the original (Prime-timeline).
897
- *
902
+ *
898
903
  * OPTIONAL: An initializer can be set via `timegroup.initializer = (tg) => { ... }`
899
904
  * to re-run JavaScript setup (frame callbacks, React components) on each clone.
900
- *
905
+ *
901
906
  * This enables:
902
907
  * - Rendering without affecting user's preview position
903
908
  * - Concurrent renders with different clones
904
909
  * - Re-running JavaScript setup on each clone (if initializer is provided)
905
- *
910
+ *
906
911
  * @returns Promise resolving to clone, container, and cleanup function
907
912
  * @throws Error if initializer is async or takes too long
908
913
  * @public
909
914
  */
910
915
  async createRenderClone() {
916
+ const factory = getCloneFactory(this);
917
+ if (factory) return this.#createRenderCloneFromFactory(factory);
918
+ return this.#createRenderCloneFromDOM();
919
+ }
920
+ /**
921
+ * Wait for all LitElement descendants to update and for text segments to be ready.
922
+ * This ensures the clone is fully initialized before rendering.
923
+ * @internal
924
+ */
925
+ async #waitForDescendants(actualClone) {
926
+ const allLitElements = Array.from(actualClone.querySelectorAll("*")).filter((el) => el instanceof LitElement);
927
+ await Promise.all(allLitElements.map((el) => el.updateComplete));
928
+ const textElements = allLitElements.filter((el) => el.tagName === "EF-TEXT");
929
+ if (textElements.length > 0) {
930
+ await Promise.all(textElements.map((el) => {
931
+ if ("whenSegmentsReady" in el && typeof el.whenSegmentsReady === "function") return el.whenSegmentsReady();
932
+ return Promise.resolve();
933
+ }));
934
+ actualClone.offsetHeight;
935
+ await new Promise((resolve) => requestAnimationFrame(resolve));
936
+ }
937
+ }
938
+ /**
939
+ * Factory path: mount a fresh component tree (React, etc.) to produce
940
+ * a fully functional clone. The factory is responsible for rendering
941
+ * the component into the container and returning the root ef-timegroup.
942
+ */
943
+ async #createRenderCloneFromFactory(factory) {
944
+ const width = this.offsetWidth || 1920;
945
+ const height = this.offsetHeight || 1080;
946
+ const container = document.createElement("div");
947
+ container.className = "ef-render-clone-container";
948
+ container.style.cssText = `
949
+ position: fixed;
950
+ left: -9999px;
951
+ top: 0;
952
+ width: ${width}px;
953
+ height: ${height}px;
954
+ pointer-events: none;
955
+ overflow: hidden;
956
+ `;
957
+ let renderTarget = container;
958
+ const originalConfig = this.closest("ef-configuration");
959
+ if (originalConfig) {
960
+ const configClone = originalConfig.cloneNode(false);
961
+ container.appendChild(configClone);
962
+ renderTarget = configClone;
963
+ }
964
+ document.body.appendChild(container);
965
+ const { timegroup: actualClone, cleanup: factoryCleanup } = factory(renderTarget);
966
+ if (!actualClone) throw new Error("Clone factory did not produce an ef-timegroup. Ensure the factory renders a component containing a Timegroup.");
967
+ actualClone.setAttribute("data-no-workbench", "true");
968
+ actualClone.setAttribute("data-no-playback-controller", "true");
969
+ actualClone.style.width = `${width}px`;
970
+ actualClone.style.height = `${height}px`;
971
+ actualClone.style.display = "block";
972
+ await customElements.whenDefined("ef-timegroup");
973
+ customElements.upgrade(container);
974
+ await actualClone.updateComplete;
975
+ await this.#waitForDescendants(actualClone);
976
+ await this.#finalizeRenderClone(actualClone);
977
+ return {
978
+ clone: actualClone,
979
+ container,
980
+ cleanup: () => {
981
+ container.remove();
982
+ factoryCleanup();
983
+ }
984
+ };
985
+ }
986
+ /**
987
+ * DOM path: deep clone the DOM tree and copy JavaScript properties.
988
+ * Used for vanilla HTML/JS timelines that don't have a factory registered.
989
+ */
990
+ async #createRenderCloneFromDOM() {
911
991
  const container = document.createElement("div");
912
992
  container.className = "ef-render-clone-container";
913
993
  container.style.cssText = `
@@ -921,6 +1001,14 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
921
1001
  `;
922
1002
  const cloneEl = this.cloneNode(true);
923
1003
  cloneEl.removeAttribute("id");
1004
+ for (const el of cloneEl.querySelectorAll("[id]")) el.removeAttribute("id");
1005
+ cloneEl.setAttribute("data-no-workbench", "true");
1006
+ cloneEl.setAttribute("data-no-playback-controller", "true");
1007
+ const width = this.offsetWidth || 1920;
1008
+ const height = this.offsetHeight || 1080;
1009
+ cloneEl.style.width = `${width}px`;
1010
+ cloneEl.style.height = `${height}px`;
1011
+ cloneEl.style.display = "block";
924
1012
  this.#copyCaptionsData(this, cloneEl);
925
1013
  this.#copyTextContent(this, cloneEl);
926
1014
  const originalConfig = this.closest("ef-configuration");
@@ -940,7 +1028,30 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
940
1028
  actualClone = container.querySelector("ef-timegroup");
941
1029
  if (!actualClone) throw new Error("ef-timegroup element lost after upgrade");
942
1030
  await actualClone.updateComplete;
1031
+ await this.#waitForDescendants(actualClone);
943
1032
  await this.#copyTextSegmentData(this, actualClone);
1033
+ await this.#finalizeRenderClone(actualClone);
1034
+ return {
1035
+ clone: actualClone,
1036
+ container,
1037
+ cleanup: () => {
1038
+ container.remove();
1039
+ const reactRoot = actualClone._reactRoot;
1040
+ if (reactRoot) queueMicrotask(() => {
1041
+ reactRoot.unmount();
1042
+ });
1043
+ }
1044
+ };
1045
+ }
1046
+ /**
1047
+ * Shared finalization for both factory and DOM clone paths:
1048
+ * - Set up parent-child temporal relationships
1049
+ * - Lock root timegroup references
1050
+ * - Wait for media durations and captions
1051
+ * - Remove PlaybackController
1052
+ * - Initial seek to frame 0
1053
+ */
1054
+ async #finalizeRenderClone(actualClone) {
944
1055
  const setupParentChildRelationships = (parent, root) => {
945
1056
  for (const child of parent.children) if (child.tagName === "EF-TIMEGROUP") {
946
1057
  const childTG = child;
@@ -953,10 +1064,10 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
953
1064
  temporal.parentTimegroup = parent;
954
1065
  temporal.rootTimegroup = root;
955
1066
  if ("lockRootTimegroup" in temporal && typeof temporal.lockRootTimegroup === "function") temporal.lockRootTimegroup();
956
- } else if (child instanceof Element) setupParentChildRelationshipsInContainer(child, parent, root);
1067
+ } else if (child instanceof Element) setupInContainer(child, parent, root);
957
1068
  };
958
- const setupParentChildRelationshipsInContainer = (container$1, nearestParentTG, root) => {
959
- for (const child of container$1.children) if (child.tagName === "EF-TIMEGROUP") {
1069
+ const setupInContainer = (container, nearestParentTG, root) => {
1070
+ for (const child of container.children) if (child.tagName === "EF-TIMEGROUP") {
960
1071
  const childTG = child;
961
1072
  childTG.parentTimegroup = nearestParentTG;
962
1073
  childTG.rootTimegroup = root;
@@ -967,7 +1078,7 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
967
1078
  temporal.parentTimegroup = nearestParentTG;
968
1079
  temporal.rootTimegroup = root;
969
1080
  if ("lockRootTimegroup" in temporal && typeof temporal.lockRootTimegroup === "function") temporal.lockRootTimegroup();
970
- } else if (child instanceof Element) setupParentChildRelationshipsInContainer(child, nearestParentTG, root);
1081
+ } else if (child instanceof Element) setupInContainer(child, nearestParentTG, root);
971
1082
  };
972
1083
  actualClone.rootTimegroup = actualClone;
973
1084
  setupParentChildRelationships(actualClone, actualClone);
@@ -989,17 +1100,6 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
989
1100
  actualClone.playbackController = void 0;
990
1101
  }
991
1102
  await actualClone.seek(0);
992
- return {
993
- clone: actualClone,
994
- container,
995
- cleanup: () => {
996
- container.remove();
997
- const reactRoot = actualClone._reactRoot;
998
- if (reactRoot) queueMicrotask(() => {
999
- reactRoot.unmount();
1000
- });
1001
- }
1002
- };
1003
1103
  }
1004
1104
  /** @internal */
1005
1105
  get storageKey() {
@@ -1020,54 +1120,6 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
1020
1120
  const childTemporalsAsElements = this.childTemporals;
1021
1121
  return evaluateDurationForMode(this, this.mode, childTemporalsAsElements);
1022
1122
  }
1023
- /**
1024
- * Evaluates which elements should be rendered in the current frame.
1025
- * Filters to only include temporally visible elements for frame processing.
1026
- * Uses animation-friendly visibility to prevent animation jumps at exact boundaries.
1027
- */
1028
- #evaluateVisibleElementsForFrame() {
1029
- return deepGetElementsWithFrameTasks(this).filter((element) => {
1030
- return evaluateAnimationVisibilityState(element).isVisible;
1031
- });
1032
- }
1033
- /** @internal */
1034
- async waitForFrameTasks(signal) {
1035
- return await withSpan("timegroup.waitForFrameTasks", {
1036
- timegroupId: this.id || "unknown",
1037
- mode: this.mode
1038
- }, void 0, async (span) => {
1039
- signal?.throwIfAborted();
1040
- const innerStart = performance.now();
1041
- const temporalElements = deepGetElementsWithFrameTasks(this);
1042
- if (isTracingEnabled()) span.setAttribute("temporalElementsCount", temporalElements.length);
1043
- signal?.throwIfAborted();
1044
- const visibleElements = this.#evaluateVisibleElementsForFrame();
1045
- if (isTracingEnabled()) span.setAttribute("visibleElementsCount", visibleElements.length);
1046
- const promiseStart = performance.now();
1047
- await Promise.all(visibleElements.map(async (element) => {
1048
- signal?.throwIfAborted();
1049
- try {
1050
- if ("waitForFrameReady" in element && typeof element.waitForFrameReady === "function") await element.waitForFrameReady();
1051
- else {
1052
- await element.updateComplete;
1053
- await element.frameTask.run();
1054
- }
1055
- } catch (error) {
1056
- if (error instanceof DOMException && error.name === "AbortError" || error instanceof Error && (error.name === "AbortError" || error.message?.includes("signal is aborted") || error.message?.includes("The user aborted a request"))) {
1057
- signal?.throwIfAborted();
1058
- return;
1059
- }
1060
- throw error;
1061
- }
1062
- }));
1063
- const promiseEnd = performance.now();
1064
- const innerEnd = performance.now();
1065
- if (isTracingEnabled()) {
1066
- span.setAttribute("actualInnerMs", innerEnd - innerStart);
1067
- span.setAttribute("promiseAwaitMs", promiseEnd - promiseStart);
1068
- }
1069
- });
1070
- }
1071
1123
  #mediaDurationsPromise = void 0;
1072
1124
  /** @internal */
1073
1125
  async waitForMediaDurations(signal) {
@@ -1195,14 +1247,6 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
1195
1247
  get childTemporals() {
1196
1248
  return shallowGetTemporalElements(this);
1197
1249
  }
1198
- get #contextProvider() {
1199
- let parent = this.parentNode;
1200
- while (parent) {
1201
- if (isContextMixin(parent)) return parent;
1202
- parent = parent.parentNode;
1203
- }
1204
- return null;
1205
- }
1206
1250
  /**
1207
1251
  * Returns true if the timegroup should be wrapped with a workbench.
1208
1252
  *
@@ -1215,18 +1259,26 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
1215
1259
  * @internal
1216
1260
  */
1217
1261
  shouldWrapWithWorkbench() {
1262
+ if (this.canvasPreviewActive) return false;
1218
1263
  if (!this.isRootTimegroup) return false;
1219
1264
  if (this.closest("ef-canvas") !== null) return false;
1220
1265
  if (this.closest("ef-preview") !== null || this.closest("ef-workbench") !== null || this.closest("ef-preview-context") !== null) return false;
1221
- if (this.closest("test-context") !== null) return false;
1222
- if (this.closest(".ef-render-clone-container") !== null) return false;
1223
- if (EF_RENDERING?.() === true) return true;
1266
+ if (this.closest("test-context") !== null || this.hasAttribute("data-no-workbench")) return false;
1267
+ if (EF_RENDERING?.() === true) return false;
1268
+ if (typeof window !== "undefined") {
1269
+ const params = new URLSearchParams(window.location.search);
1270
+ if (params.get("noWorkbench") === "true" || params.get("no-workbench") === "true") return false;
1271
+ }
1224
1272
  return this.workbench;
1225
1273
  }
1226
1274
  /** @internal */
1227
1275
  wrapWithWorkbench() {
1228
1276
  const workbench = document.createElement("ef-workbench");
1229
1277
  const parent = this.parentElement;
1278
+ if (EF_RENDERING()) {
1279
+ workbench.setAttribute("rendering", "");
1280
+ workbench.rendering = true;
1281
+ }
1230
1282
  if (parent === document.body) {
1231
1283
  workbench.style.position = "fixed";
1232
1284
  workbench.style.top = "0";
@@ -1234,6 +1286,12 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
1234
1286
  workbench.style.width = "100vw";
1235
1287
  workbench.style.height = "100vh";
1236
1288
  workbench.style.zIndex = "0";
1289
+ } else {
1290
+ workbench.style.position = "absolute";
1291
+ workbench.style.top = "0";
1292
+ workbench.style.left = "0";
1293
+ workbench.style.width = "100%";
1294
+ workbench.style.height = "100%";
1237
1295
  }
1238
1296
  parent?.append(workbench);
1239
1297
  if (!this.hasAttribute("id")) this.setAttribute("id", "root-timegroup");
@@ -1246,8 +1304,8 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
1246
1304
  const rect = this.getBoundingClientRect();
1247
1305
  const canvas = document.createElement("ef-canvas");
1248
1306
  canvas.id = "workbench-canvas";
1249
- canvas.style.width = `${Math.max(rect.width, 1920)}px`;
1250
- canvas.style.height = `${Math.max(rect.height, 1080)}px`;
1307
+ canvas.style.width = `${rect.width}px`;
1308
+ canvas.style.height = `${rect.height}px`;
1251
1309
  canvas.style.display = "block";
1252
1310
  canvas.append(this);
1253
1311
  panZoom.append(canvas);
@@ -1262,9 +1320,6 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
1262
1320
  filmstrip.setAttribute("target", this.id);
1263
1321
  workbench.append(filmstrip);
1264
1322
  }
1265
- get #efElements() {
1266
- return Array.from(this.querySelectorAll("ef-audio, ef-video, ef-image, ef-captions, ef-waveform"));
1267
- }
1268
1323
  /**
1269
1324
  * Returns media elements for playback audio rendering
1270
1325
  * For standalone media, returns [this]; for timegroups, returns all descendants
@@ -1283,80 +1338,6 @@ let EFTimegroup = class EFTimegroup$1 extends EFTargetable(EFTemporal(TWMixin(Li
1283
1338
  async renderAudio(fromMs, toMs, signal) {
1284
1339
  return renderTemporalAudio(this, fromMs, toMs, signal);
1285
1340
  }
1286
- /**
1287
- * TEMPORARY TEST METHOD: Renders audio and immediately plays it back
1288
- * Usage: timegroup.testPlayAudio(0, 5000) // Play first 5 seconds
1289
- */
1290
- async #testPlayAudio(fromMs, toMs) {
1291
- const renderedBuffer = await this.renderAudio(fromMs, toMs);
1292
- const playbackContext = new AudioContext();
1293
- const bufferSource = playbackContext.createBufferSource();
1294
- bufferSource.buffer = renderedBuffer;
1295
- bufferSource.connect(playbackContext.destination);
1296
- bufferSource.start(0);
1297
- return new Promise((resolve) => {
1298
- bufferSource.onended = () => {
1299
- playbackContext.close();
1300
- resolve();
1301
- };
1302
- });
1303
- }
1304
- async #loadMd5Sums() {
1305
- const efElements = this.#efElements;
1306
- const loaderTasks = [];
1307
- for (const el of efElements) {
1308
- const md5SumLoader = el.md5SumLoader;
1309
- if (md5SumLoader && typeof md5SumLoader.run === "function") {
1310
- md5SumLoader.run().catch(() => {});
1311
- if (md5SumLoader.taskComplete) loaderTasks.push(md5SumLoader.taskComplete);
1312
- }
1313
- }
1314
- await Promise.all(loaderTasks);
1315
- efElements.forEach((el) => {
1316
- if ("productionSrc" in el && el.productionSrc instanceof Function) el.setAttribute("src", el.productionSrc());
1317
- });
1318
- }
1319
- #timegroupFrameTaskCount = 0;
1320
- #timegroupFrameTaskLastReset = Date.now();
1321
- static {
1322
- this.TIMEGROUP_FRAME_TASK_THRESHOLD = 100;
1323
- }
1324
- static {
1325
- this.TIMEGROUP_FRAME_TASK_RESET_MS = 1e3;
1326
- }
1327
- /** @internal */
1328
- #frameTaskPromise = Promise.resolve();
1329
- #frameTaskAbortController = null;
1330
- async #runFrameTask(signal) {
1331
- const now = Date.now();
1332
- if (now - this.#timegroupFrameTaskLastReset > _EFTimegroup.TIMEGROUP_FRAME_TASK_RESET_MS) {
1333
- this.#timegroupFrameTaskCount = 0;
1334
- this.#timegroupFrameTaskLastReset = now;
1335
- }
1336
- this.#timegroupFrameTaskCount++;
1337
- if (this.#timegroupFrameTaskCount > _EFTimegroup.TIMEGROUP_FRAME_TASK_THRESHOLD) return;
1338
- try {
1339
- signal.throwIfAborted();
1340
- if (this.isRootTimegroup) await withSpan("timegroup.frameTask", {
1341
- timegroupId: this.id || "unknown",
1342
- ownCurrentTimeMs: this.ownCurrentTimeMs,
1343
- currentTimeMs: this.currentTimeMs
1344
- }, void 0, async () => {
1345
- await this.#frameController.renderFrame(this.currentTimeMs, {
1346
- waitForLitUpdate: false,
1347
- onAnimationsUpdate: (root) => {
1348
- updateAnimations(root);
1349
- }
1350
- });
1351
- signal.throwIfAborted();
1352
- await this.#executeCustomFrameTasks();
1353
- });
1354
- else await this.#executeCustomFrameTasks();
1355
- } catch (error) {
1356
- if (error instanceof DOMException && error.name === "AbortError") return;
1357
- console.error("EFTimegroup frameTask error", error);
1358
- }
1359
- }
1360
1341
  async #executeCustomFrameTasks() {
1361
1342
  if (this.#customFrameTasks.size > 0) {
1362
1343
  const percentComplete = this.durationMs > 0 ? this.ownCurrentTimeMs / this.durationMs : 0;
@@ -1462,7 +1443,7 @@ __decorate([property({
1462
1443
  type: Number,
1463
1444
  attribute: "currenttime"
1464
1445
  })], EFTimegroup.prototype, "currentTime", null);
1465
- EFTimegroup = _EFTimegroup = __decorate([customElement("ef-timegroup")], EFTimegroup);
1446
+ EFTimegroup = __decorate([customElement("ef-timegroup")], EFTimegroup);
1466
1447
 
1467
1448
  //#endregion
1468
1449
  export { EFTimegroup, flushSequenceDurationCache, shallowGetTimegroups };