@editframe/elements 0.37.3-beta → 0.38.1

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 (327) 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 +2 -2
  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 +5 -8
  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 +2 -2
  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 +2 -2
  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 +2 -2
  107. package/dist/gui/EFFocusOverlay.js +3 -3
  108. package/dist/gui/EFFocusOverlay.js.map +1 -1
  109. package/dist/gui/EFOverlayItem.d.ts +2 -2
  110. package/dist/gui/EFOverlayLayer.d.ts +2 -2
  111. package/dist/gui/EFPause.d.ts +2 -2
  112. package/dist/gui/EFPause.js +1 -1
  113. package/dist/gui/EFPlay.d.ts +2 -2
  114. package/dist/gui/EFPlay.js +1 -1
  115. package/dist/gui/EFPreview.js +1 -1
  116. package/dist/gui/EFResizableBox.d.ts +2 -2
  117. package/dist/gui/EFResizableBox.js +5 -5
  118. package/dist/gui/EFResizableBox.js.map +1 -1
  119. package/dist/gui/EFScrubber.d.ts +2 -2
  120. package/dist/gui/EFScrubber.js +8 -13
  121. package/dist/gui/EFScrubber.js.map +1 -1
  122. package/dist/gui/EFTimeDisplay.d.ts +6 -2
  123. package/dist/gui/EFTimeDisplay.js +25 -7
  124. package/dist/gui/EFTimeDisplay.js.map +1 -1
  125. package/dist/gui/EFTimelineRuler.d.ts +2 -2
  126. package/dist/gui/EFTimelineRuler.js +3 -3
  127. package/dist/gui/EFTimelineRuler.js.map +1 -1
  128. package/dist/gui/EFToggleLoop.d.ts +2 -2
  129. package/dist/gui/EFToggleLoop.js +1 -1
  130. package/dist/gui/EFTogglePlay.d.ts +2 -2
  131. package/dist/gui/EFTogglePlay.js +1 -1
  132. package/dist/gui/EFTransformHandles.d.ts +2 -2
  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 +5 -3
  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 +2 -2
  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 +2 -2
  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.d.ts +134 -32
  238. package/dist/preview/renderTimegroupToCanvas.js +321 -146
  239. package/dist/preview/renderTimegroupToCanvas.js.map +1 -1
  240. package/dist/preview/renderTimegroupToCanvas.types.d.ts +51 -0
  241. package/dist/preview/renderTimegroupToVideo.d.ts +20 -35
  242. package/dist/preview/renderTimegroupToVideo.js +94 -106
  243. package/dist/preview/renderTimegroupToVideo.js.map +1 -1
  244. package/dist/preview/renderTimegroupToVideo.types.d.ts +42 -0
  245. package/dist/preview/renderVideoToVideo.js +286 -0
  246. package/dist/preview/renderVideoToVideo.js.map +1 -0
  247. package/dist/preview/renderers.d.ts +56 -0
  248. package/dist/preview/renderers.js +13 -1
  249. package/dist/preview/renderers.js.map +1 -1
  250. package/dist/preview/rendering/ScaleConfig.js +74 -0
  251. package/dist/preview/rendering/ScaleConfig.js.map +1 -0
  252. package/dist/preview/rendering/inlineImages.d.ts +13 -0
  253. package/dist/preview/rendering/inlineImages.js +7 -44
  254. package/dist/preview/rendering/inlineImages.js.map +1 -1
  255. package/dist/preview/rendering/loadImage.d.ts +8 -0
  256. package/dist/preview/rendering/loadImage.js +22 -0
  257. package/dist/preview/rendering/loadImage.js.map +1 -0
  258. package/dist/preview/rendering/renderToImageNative.js +3 -3
  259. package/dist/preview/rendering/renderToImageNative.js.map +1 -1
  260. package/dist/preview/rendering/serializeTimelineDirect.js +224 -68
  261. package/dist/preview/rendering/serializeTimelineDirect.js.map +1 -1
  262. package/dist/preview/statsTrackingStrategy.js +1 -101
  263. package/dist/preview/statsTrackingStrategy.js.map +1 -1
  264. package/dist/preview/workers/WorkerPool.js +0 -1
  265. package/dist/preview/workers/WorkerPool.js.map +1 -1
  266. package/dist/preview/workers/encoderWorkerInline.js +21 -54
  267. package/dist/preview/workers/encoderWorkerInline.js.map +1 -1
  268. package/dist/render/EFRenderAPI.d.ts +2 -1
  269. package/dist/render/EFRenderAPI.js +12 -36
  270. package/dist/render/EFRenderAPI.js.map +1 -1
  271. package/dist/render/getRenderData.js +4 -4
  272. package/dist/render/getRenderData.js.map +1 -1
  273. package/dist/style.css +114 -163
  274. package/dist/transcoding/cache/RequestDeduplicator.js +1 -0
  275. package/dist/transcoding/cache/RequestDeduplicator.js.map +1 -1
  276. package/dist/transcoding/types/index.d.ts +1 -1
  277. package/dist/transcoding/utils/UrlGenerator.js +10 -3
  278. package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
  279. package/dist/utils/LRUCache.js +1 -0
  280. package/dist/utils/LRUCache.js.map +1 -1
  281. package/dist/utils/frameTime.js +23 -1
  282. package/dist/utils/frameTime.js.map +1 -1
  283. package/package.json +45 -8
  284. package/scripts/build-css.js +8 -1
  285. package/test/setup.ts +0 -1
  286. package/test/useAssetMSW.ts +50 -0
  287. package/test/visualRegressionUtils.ts +23 -9
  288. package/tsdown.config.ts +6 -1
  289. package/dist/_virtual/rolldown_runtime.js +0 -27
  290. package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +0 -1
  291. package/dist/elements/EFThumbnailStrip.d.ts +0 -167
  292. package/dist/elements/EFThumbnailStrip.js +0 -731
  293. package/dist/elements/EFThumbnailStrip.js.map +0 -1
  294. package/dist/elements/SessionThumbnailCache.js +0 -154
  295. package/dist/elements/SessionThumbnailCache.js.map +0 -1
  296. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +0 -688
  297. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +0 -1
  298. package/dist/node_modules/react/cjs/react.development.js +0 -1521
  299. package/dist/node_modules/react/cjs/react.development.js.map +0 -1
  300. package/dist/node_modules/react/index.js +0 -13
  301. package/dist/node_modules/react/index.js.map +0 -1
  302. package/dist/node_modules/react/jsx-runtime.js +0 -13
  303. package/dist/node_modules/react/jsx-runtime.js.map +0 -1
  304. package/dist/preview/encoding/types.d.ts +0 -1
  305. package/dist/preview/renderTimegroupPreview.js +0 -686
  306. package/dist/preview/renderTimegroupPreview.js.map +0 -1
  307. package/dist/preview/rendering/renderToImage.d.ts +0 -2
  308. package/dist/preview/rendering/renderToImage.js +0 -95
  309. package/dist/preview/rendering/renderToImage.js.map +0 -1
  310. package/dist/preview/rendering/renderToImageForeignObject.js +0 -163
  311. package/dist/preview/rendering/renderToImageForeignObject.js.map +0 -1
  312. package/dist/preview/rendering/renderToImageNative.d.ts +0 -1
  313. package/dist/preview/rendering/svgSerializer.js +0 -43
  314. package/dist/preview/rendering/svgSerializer.js.map +0 -1
  315. package/dist/preview/rendering/types.d.ts +0 -2
  316. package/dist/preview/thumbnailCacheSettings.js +0 -52
  317. package/dist/preview/thumbnailCacheSettings.js.map +0 -1
  318. package/dist/sandbox/PlaybackControls.d.ts +0 -1
  319. package/dist/sandbox/PlaybackControls.js +0 -10
  320. package/dist/sandbox/PlaybackControls.js.map +0 -1
  321. package/dist/sandbox/ScenarioRunner.d.ts +0 -1
  322. package/dist/sandbox/ScenarioRunner.js +0 -1
  323. package/dist/sandbox/defineSandbox.d.ts +0 -1
  324. package/dist/sandbox/index.d.ts +0 -3
  325. package/dist/sandbox/index.js +0 -2
  326. package/test/EFVideo.framegen.browsertest.ts +0 -80
  327. package/test/thumbnail-performance-test.html +0 -116
@@ -1,13 +1,15 @@
1
1
  import { FrameController } from "./FrameController.js";
2
+ import { updateAnimations } from "../elements/updateAnimations.js";
2
3
  import { logger } from "./logger.js";
3
- import { DEFAULT_BLOCKING_TIMEOUT_MS, DEFAULT_HEIGHT, DEFAULT_THUMBNAIL_SCALE, DEFAULT_WIDTH, createPreviewContainer, isVisibleAtTime } from "./previewTypes.js";
4
- import { buildCloneStructure, collectDocumentStyles, overrideRootCloneStyles, removeHiddenNodesForSerialization, restoreHiddenNodes } from "./renderTimegroupPreview.js";
5
- import { getEffectiveRenderMode } from "./renderers.js";
6
4
  import { RenderContext } from "./RenderContext.js";
5
+ import { DEFAULT_BLOCKING_TIMEOUT_MS, DEFAULT_CAPTURE_SCALE, DEFAULT_HEIGHT, DEFAULT_WIDTH, isVisibleAtTime } from "./previewTypes.js";
6
+ import { captureTimelineToDataUri } from "./rendering/serializeTimelineDirect.js";
7
+ import { getRenderMode, isNativeCanvasApiAvailable } from "./previewSettings.js";
8
+ import { getEffectiveRenderMode, isCanvas, isImage } from "./renderers.js";
7
9
  import { defaultProfiler } from "./RenderProfiler.js";
10
+ import { loadImageFromDataUri } from "./rendering/loadImage.js";
8
11
  import { createDprCanvas, renderToImageNative } from "./rendering/renderToImageNative.js";
9
- import { clearInlineImageCache } from "./rendering/inlineImages.js";
10
- import { loadImageFromDataUri, renderToImage } from "./rendering/renderToImage.js";
12
+ import { clearInlineImageCache, getInlineImageCacheSize } from "./rendering/inlineImages.js";
11
13
 
12
14
  //#region src/preview/renderTimegroupToCanvas.ts
13
15
  /** Number of rows to sample when checking canvas content */
@@ -40,6 +42,20 @@ const renderState = {
40
42
  }
41
43
  };
42
44
  /**
45
+ * Get the current render state for testing and debugging.
46
+ * @returns The module-level render state object
47
+ */
48
+ function getRenderState() {
49
+ return renderState;
50
+ }
51
+ /**
52
+ * Get cache metrics for monitoring performance.
53
+ * @returns Object with cache hit/miss/eviction counts
54
+ */
55
+ function getCacheMetrics() {
56
+ return { ...renderState.metrics };
57
+ }
58
+ /**
43
59
  * Reset cache metrics to zero.
44
60
  */
45
61
  function resetCacheMetrics() {
@@ -57,37 +73,34 @@ function resetRenderState() {
57
73
  resetCacheMetrics();
58
74
  }
59
75
  /**
60
- * Create a debug label for showing render info.
76
+ * DEBUG: Capture a single thumbnail at the current time.
77
+ * Call from console: window.debugCaptureThumbnail()
61
78
  */
62
- function createDebugLabel() {
63
- const debugLabel = document.createElement("div");
64
- debugLabel.style.cssText = `
65
- position: absolute;
66
- top: -24px;
67
- left: 0;
68
- padding: 2px 8px;
69
- font: bold 12px monospace;
70
- background: rgba(0, 0, 0, 0.8);
71
- border-radius: 3px;
72
- white-space: nowrap;
73
- z-index: 1000;
74
- pointer-events: none;
75
- `;
76
- return debugLabel;
77
- }
78
- /**
79
- * Update debug label with resolution info.
80
- */
81
- function updateDebugLabel(label, renderWidth, renderHeight, resolutionScale) {
82
- const scaleColors = {
83
- 1: "#00ff00",
84
- .75: "#ffff00",
85
- .5: "#ff8800",
86
- .25: "#ff0000"
87
- };
88
- label.style.color = scaleColors[resolutionScale] || "#ffffff";
89
- label.textContent = `Render: ${renderWidth}x${renderHeight} (${Math.round(resolutionScale * 100)}%)`;
90
- }
79
+ if (typeof window !== "undefined") window.debugCaptureThumbnail = async function() {
80
+ const timegroup = document.querySelector("ef-timegroup");
81
+ if (!timegroup) {
82
+ console.error("No timegroup found");
83
+ return;
84
+ }
85
+ const currentTime = timegroup.currentTimeMs ?? 0;
86
+ try {
87
+ const result = await captureTimegroupAtTime(timegroup, {
88
+ timeMs: currentTime,
89
+ scale: .25,
90
+ contentReadyMode: "blocking",
91
+ blockingTimeoutMs: 1e3
92
+ });
93
+ const img = document.createElement("img");
94
+ if (result instanceof HTMLCanvasElement) img.src = result.toDataURL();
95
+ else if (result instanceof HTMLImageElement) img.src = result.src;
96
+ img.style.cssText = "position:fixed;top:10px;right:10px;border:2px solid red;z-index:99999;";
97
+ document.body.appendChild(img);
98
+ return result;
99
+ } catch (err) {
100
+ console.error("[DEBUG] Capture failed:", err);
101
+ throw err;
102
+ }
103
+ };
91
104
  /**
92
105
  * Wait for next animation frame (allows browser to complete layout)
93
106
  */
@@ -99,7 +112,7 @@ function waitForFrame() {
99
112
  * Returns true if there's ANY non-transparent pixel.
100
113
  */
101
114
  function canvasHasContent(canvas) {
102
- const ctx = canvas.getContext("2d");
115
+ const ctx = canvas.getContext("2d", { willReadFrequently: true });
103
116
  if (!ctx) return false;
104
117
  try {
105
118
  const width = canvas.width;
@@ -167,101 +180,192 @@ async function waitForVideoContent(timegroup, timeMs, maxWaitMs) {
167
180
  /**
168
181
  * Captures a frame from an already-seeked render clone.
169
182
  * Used internally by captureBatch for efficiency (reuses one clone across all captures).
170
- *
183
+ *
171
184
  * @param renderClone - A render clone that has already been seeked to the target time
172
185
  * @param renderContainer - The container holding the render clone (from createRenderClone)
173
186
  * @param options - Capture options
174
- * @returns Canvas with the rendered frame
187
+ * @returns Canvas or Image with the rendered frame (both are CanvasImageSource)
175
188
  */
176
- async function captureFromClone(renderClone, renderContainer, options = {}) {
177
- const { scale = DEFAULT_THUMBNAIL_SCALE, contentReadyMode = "immediate", blockingTimeoutMs = DEFAULT_BLOCKING_TIMEOUT_MS, originalTimegroup } = options;
189
+ async function captureFromClone(renderClone, _renderContainer, options = {}) {
190
+ const { scale = DEFAULT_CAPTURE_SCALE, contentReadyMode = "immediate", blockingTimeoutMs = DEFAULT_BLOCKING_TIMEOUT_MS, originalTimegroup, timeMs: explicitTimeMs, canvasMode } = options;
191
+ const timeMs = explicitTimeMs ?? renderClone.currentTimeMs;
178
192
  const sourceForDimensions = originalTimegroup ?? renderClone;
179
193
  const width = sourceForDimensions.offsetWidth || DEFAULT_WIDTH;
180
194
  const height = sourceForDimensions.offsetHeight || DEFAULT_HEIGHT;
181
- const dpr = window.devicePixelRatio || 1;
182
- const canvas = document.createElement("canvas");
183
- canvas.width = Math.floor(width * scale * dpr);
184
- canvas.height = Math.floor(height * scale * dpr);
185
- canvas.style.width = `${Math.floor(width * scale)}px`;
186
- canvas.style.height = `${Math.floor(height * scale)}px`;
187
- const ctx = canvas.getContext("2d");
188
- if (!ctx) throw new Error("Failed to get canvas 2d context");
189
- const timeMs = renderClone.currentTimeMs;
190
195
  if (contentReadyMode === "blocking") {
191
196
  const result = await waitForVideoContent(renderClone, timeMs, blockingTimeoutMs);
192
197
  if (!result.ready) throw new ContentNotReadyError(timeMs, blockingTimeoutMs, result.blankVideos);
193
198
  }
199
+ const effectiveCanvasMode = (() => {
200
+ if (!canvasMode) return "foreignObject";
201
+ if (canvasMode === "native" && !isNativeCanvasApiAvailable()) {
202
+ logger.debug("[captureFromClone] Native canvas mode requested but not available, falling back to foreignObject");
203
+ return "foreignObject";
204
+ }
205
+ return canvasMode;
206
+ })();
194
207
  const renderContext = new RenderContext();
195
208
  try {
196
- let image;
197
- if (getEffectiveRenderMode() === "native") {
198
- renderContainer.style.cssText = `
199
- position: fixed;
200
- left: 0;
201
- top: 0;
202
- width: ${width}px;
203
- height: ${height}px;
204
- pointer-events: none;
205
- overflow: hidden;
206
- `;
207
- image = await renderToImageNative(renderContainer, width, height, { skipDprScaling: true });
209
+ if (effectiveCanvasMode === "native") {
210
+ const t0 = performance.now();
211
+ const canvas = await renderToImageNative(renderClone, width, height, { skipDprScaling: true });
212
+ const renderTime = performance.now() - t0;
213
+ logger.debug(`[captureFromClone] native render=${renderTime.toFixed(0)}ms (canvasScale=${scale})`);
214
+ return canvas;
208
215
  } else {
209
216
  const t0 = performance.now();
210
- const { container: container$1, syncState } = buildCloneStructure(renderClone, timeMs);
211
- const buildTime = performance.now() - t0;
212
- const bgSource = originalTimegroup ?? renderClone;
213
- const previewContainer = createPreviewContainer({
214
- width,
215
- height,
216
- background: getComputedStyle(bgSource).background || "#000"
217
- });
218
- const t1 = performance.now();
219
- const styleEl = document.createElement("style");
220
- styleEl.textContent = collectDocumentStyles();
221
- const stylesTime = performance.now() - t1;
222
- previewContainer.appendChild(styleEl);
223
- previewContainer.appendChild(container$1);
224
- overrideRootCloneStyles(syncState, true);
225
- const t2 = performance.now();
226
- image = await renderToImage(previewContainer, width, height, {
227
- canvasScale: scale,
217
+ const dataUri = await captureTimelineToDataUri(renderClone, width, height, {
228
218
  renderContext,
229
- sourceMap: syncState.canvasSourceMap
219
+ canvasScale: scale,
220
+ timeMs
230
221
  });
231
- const renderTime = performance.now() - t2;
232
- logger.debug(`[captureFromClone] build=${buildTime.toFixed(0)}ms, styles=${stylesTime.toFixed(0)}ms, render=${renderTime.toFixed(0)}ms (canvasScale=${scale})`);
222
+ const serializeTime = performance.now() - t0;
223
+ const t1 = performance.now();
224
+ const image = await loadImageFromDataUri(dataUri);
225
+ const loadTime = performance.now() - t1;
226
+ logger.debug(`[captureFromClone] foreignObject serialize=${serializeTime.toFixed(0)}ms, load=${loadTime.toFixed(0)}ms (canvasScale=${scale})`);
227
+ return image;
233
228
  }
234
- const srcWidth = image.width;
235
- const srcHeight = image.height;
236
- ctx.drawImage(image, 0, 0, srcWidth, srcHeight, 0, 0, canvas.width, canvas.height);
237
- return canvas;
238
229
  } finally {
239
230
  renderContext.dispose();
240
231
  }
241
232
  }
242
233
  /**
243
234
  * Captures a single frame from a timegroup at a specific time.
244
- *
235
+ *
245
236
  * CLONE-TIMELINE ARCHITECTURE:
246
237
  * Creates an independent render clone, seeks it to the target time, and captures.
247
238
  * Prime-timeline is NEVER seeked - user can continue previewing/editing during capture.
248
- *
239
+ *
249
240
  * @param timegroup - The source timegroup
250
241
  * @param options - Capture options including timeMs, scale, contentReadyMode
251
242
  * @returns Canvas with the rendered frame
252
243
  * @throws ContentNotReadyError if blocking mode times out waiting for video content
253
244
  */
254
245
  async function captureTimegroupAtTime(timegroup, options) {
255
- const { timeMs, scale = DEFAULT_THUMBNAIL_SCALE, contentReadyMode = "immediate", blockingTimeoutMs = DEFAULT_BLOCKING_TIMEOUT_MS } = options;
246
+ const { timeMs, scale = DEFAULT_CAPTURE_SCALE, contentReadyMode = "immediate", blockingTimeoutMs = DEFAULT_BLOCKING_TIMEOUT_MS, canvasMode, skipClone = false } = options;
247
+ if (skipClone) {
248
+ const seekStart = performance.now();
249
+ await timegroup.seekForRender(timeMs);
250
+ const seekMs = performance.now() - seekStart;
251
+ const renderStart = performance.now();
252
+ const result = await captureFromClone(timegroup, timegroup.parentElement || document.body, {
253
+ scale,
254
+ contentReadyMode,
255
+ blockingTimeoutMs,
256
+ originalTimegroup: void 0,
257
+ canvasMode,
258
+ timeMs
259
+ });
260
+ const renderMs = performance.now() - renderStart;
261
+ if (typeof result === "object" && result !== null) result.__perfTiming = {
262
+ cloneMs: 0,
263
+ seekMs,
264
+ renderMs
265
+ };
266
+ return result;
267
+ }
268
+ const cloneStart = performance.now();
256
269
  const { clone: renderClone, container: renderContainer, cleanup: cleanupRenderClone } = await timegroup.createRenderClone();
270
+ const cloneMs = performance.now() - cloneStart;
257
271
  try {
272
+ const seekStart = performance.now();
258
273
  await renderClone.seekForRender(timeMs);
259
- return await captureFromClone(renderClone, renderContainer, {
274
+ const seekMs = performance.now() - seekStart;
275
+ const renderStart = performance.now();
276
+ const result = await captureFromClone(renderClone, renderContainer, {
260
277
  scale,
261
278
  contentReadyMode,
262
279
  blockingTimeoutMs,
263
- originalTimegroup: timegroup
280
+ originalTimegroup: timegroup,
281
+ canvasMode
264
282
  });
283
+ const renderMs = performance.now() - renderStart;
284
+ if (typeof result === "object" && result !== null) result.__perfTiming = {
285
+ cloneMs,
286
+ seekMs,
287
+ renderMs
288
+ };
289
+ return result;
290
+ } finally {
291
+ cleanupRenderClone();
292
+ }
293
+ }
294
+ /**
295
+ * Generate thumbnails using an existing render clone and mutable queue.
296
+ * The queue can be modified while generation is in progress.
297
+ *
298
+ * @param renderClone - Pre-created render clone to use
299
+ * @param renderContainer - Container for the render clone
300
+ * @param queue - Mutable queue that provides timestamps
301
+ * @param options - Capture options (scale, contentReadyMode, etc.)
302
+ * @yields Objects with { timeMs, canvas } for each captured thumbnail
303
+ *
304
+ * @example
305
+ * ```ts
306
+ * const queue = new MutableTimestampQueue();
307
+ * queue.reset([0, 100, 200]);
308
+ *
309
+ * for await (const { timeMs, canvas } of generateThumbnailsFromClone(clone, container, queue)) {
310
+ * cache.set(timeMs, canvas);
311
+ * // Queue can be modified here while generator continues
312
+ * }
313
+ * ```
314
+ */
315
+ async function* generateThumbnailsFromClone(renderClone, renderContainer, queue, options = {}) {
316
+ const { scale = DEFAULT_CAPTURE_SCALE, contentReadyMode = "immediate", blockingTimeoutMs = DEFAULT_BLOCKING_TIMEOUT_MS, signal } = options;
317
+ while (true) {
318
+ if (signal?.aborted) break;
319
+ const timeMs = queue.shift();
320
+ if (timeMs === void 0) break;
321
+ await renderClone.seekForRender(timeMs);
322
+ if (signal?.aborted) break;
323
+ yield {
324
+ timeMs,
325
+ canvas: await captureFromClone(renderClone, renderContainer, {
326
+ scale,
327
+ contentReadyMode,
328
+ blockingTimeoutMs,
329
+ timeMs
330
+ })
331
+ };
332
+ }
333
+ }
334
+ /**
335
+ * Generate thumbnails for multiple timestamps efficiently using a single render clone.
336
+ * This avoids the overhead of creating/destroying a clone for each thumbnail.
337
+ *
338
+ * @param timegroup - The timegroup to capture
339
+ * @param timestamps - Array of timestamps to capture (in milliseconds)
340
+ * @param options - Capture options (scale, contentReadyMode, etc.)
341
+ * @param signal - Optional AbortSignal to cancel generation
342
+ * @yields Objects with { timeMs, canvas } for each captured thumbnail
343
+ *
344
+ * @example
345
+ * ```ts
346
+ * for await (const { timeMs, canvas } of generateThumbnails(tg, [0, 100, 200])) {
347
+ * console.log(`Got thumbnail for ${timeMs}ms`);
348
+ * thumbnailCache.set(timeMs, canvas);
349
+ * }
350
+ * ```
351
+ */
352
+ async function* generateThumbnails(timegroup, timestamps, options = {}, signal) {
353
+ const { scale = DEFAULT_CAPTURE_SCALE, contentReadyMode = "immediate", blockingTimeoutMs = DEFAULT_BLOCKING_TIMEOUT_MS } = options;
354
+ const { clone: renderClone, container: renderContainer, cleanup: cleanupRenderClone } = await timegroup.createRenderClone();
355
+ try {
356
+ for (const timeMs of timestamps) {
357
+ signal?.throwIfAborted();
358
+ await renderClone.seekForRender(timeMs);
359
+ yield {
360
+ timeMs,
361
+ canvas: await captureFromClone(renderClone, renderContainer, {
362
+ scale,
363
+ contentReadyMode,
364
+ blockingTimeoutMs,
365
+ originalTimegroup: timegroup
366
+ })
367
+ };
368
+ }
265
369
  } finally {
266
370
  cleanupRenderClone();
267
371
  }
@@ -282,10 +386,10 @@ function toAbsoluteTime(timegroup, relativeTimeMs) {
282
386
  }
283
387
  /**
284
388
  * Renders a timegroup preview to a canvas using SVG foreignObject.
285
- *
389
+ *
286
390
  * Captures the prime timeline's current visual state including DOM changes
287
391
  * from frame tasks (SVG paths, canvas content, text updates, etc.).
288
- *
392
+ *
289
393
  * Optimized with:
290
394
  * - Passive clone structure rebuilt each frame from prime's current state
291
395
  * - Temporal bucketing for time-based culling
@@ -302,7 +406,7 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
302
406
  let currentResolutionScale = options.resolutionScale ?? DEFAULT_RESOLUTION_SCALE;
303
407
  const width = timegroup.offsetWidth || DEFAULT_WIDTH;
304
408
  const height = timegroup.offsetHeight || DEFAULT_HEIGHT;
305
- const dpr = window.devicePixelRatio || 1;
409
+ const dpr = (typeof window !== "undefined" ? window.devicePixelRatio : 1) || 1;
306
410
  let renderWidth = Math.floor(width * currentResolutionScale);
307
411
  let renderHeight = Math.floor(height * currentResolutionScale);
308
412
  const canvas = createDprCanvas({
@@ -313,11 +417,7 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
313
417
  fullHeight: height,
314
418
  dpr
315
419
  });
316
- const wrapperContainer = document.createElement("div");
317
- wrapperContainer.style.cssText = "position: relative; display: inline-block;";
318
- const debugLabel = createDebugLabel();
319
- wrapperContainer.appendChild(debugLabel);
320
- wrapperContainer.appendChild(canvas);
420
+ const wrapperContainer = canvas;
321
421
  const ctx = canvas.getContext("2d");
322
422
  if (!ctx) throw new Error("Failed to get canvas 2d context");
323
423
  let rendering = false;
@@ -325,16 +425,34 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
325
425
  let disposed = false;
326
426
  const renderContext = new RenderContext();
327
427
  const frameController = new FrameController(timegroup);
328
- const previewContainer = createPreviewContainer({
329
- width: renderWidth,
330
- height: renderHeight,
331
- background: getComputedStyle(timegroup).background || "#000"
332
- });
333
- const styleEl = document.createElement("style");
334
- styleEl.textContent = collectDocumentStyles();
335
- previewContainer.appendChild(styleEl);
336
428
  let hasLoggedScale = false;
337
429
  let pendingResolutionScale = null;
430
+ const useNative = getRenderMode() === "native" && isNativeCanvasApiAvailable();
431
+ let captureCanvas = null;
432
+ let captureCtx = null;
433
+ let originalParent = null;
434
+ let originalNextSibling = null;
435
+ let savedClipPath = "";
436
+ let savedPointerEvents = "";
437
+ if (useNative) {
438
+ captureCanvas = document.createElement("canvas");
439
+ captureCanvas.setAttribute("layoutsubtree", "");
440
+ captureCanvas.layoutSubtree = true;
441
+ captureCanvas.width = renderWidth;
442
+ captureCanvas.height = renderHeight;
443
+ captureCanvas.style.cssText = `position:fixed;left:0;top:0;width:${width}px;height:${height}px;opacity:0;pointer-events:none;z-index:-9999;`;
444
+ originalParent = timegroup.parentNode;
445
+ originalNextSibling = timegroup.nextSibling;
446
+ savedClipPath = timegroup.style.clipPath;
447
+ savedPointerEvents = timegroup.style.pointerEvents;
448
+ timegroup.style.clipPath = "";
449
+ timegroup.style.pointerEvents = "";
450
+ captureCanvas.appendChild(timegroup);
451
+ document.body.appendChild(captureCanvas);
452
+ captureCtx = captureCanvas.getContext("2d");
453
+ captureCanvas.offsetHeight;
454
+ timegroup.offsetHeight;
455
+ }
338
456
  /**
339
457
  * Apply pending resolution scale changes.
340
458
  * Called at the start of refresh() before rendering, so the old content
@@ -347,12 +465,10 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
347
465
  currentResolutionScale = newScale;
348
466
  renderWidth = Math.floor(width * currentResolutionScale);
349
467
  renderHeight = Math.floor(height * currentResolutionScale);
350
- previewContainer.style.width = `${renderWidth}px`;
351
- previewContainer.style.height = `${renderHeight}px`;
352
- if (currentResolutionScale < 1) {
353
- container.style.transform = `scale(${currentResolutionScale})`;
354
- container.style.transformOrigin = "top left";
355
- } else container.style.transform = "";
468
+ if (captureCanvas) {
469
+ captureCanvas.width = renderWidth;
470
+ captureCanvas.height = renderHeight;
471
+ }
356
472
  };
357
473
  /**
358
474
  * Dynamically change resolution scale without rebuilding clone structure.
@@ -366,52 +482,104 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
366
482
  lastTimeMs = -1;
367
483
  };
368
484
  const getResolutionScale = () => pendingResolutionScale ?? currentResolutionScale;
485
+ let frameCount = 0;
486
+ let totalFrameControllerMs = 0;
487
+ let totalCaptureMs = 0;
488
+ let totalCopyMs = 0;
489
+ let totalFrameMs = 0;
369
490
  const refresh = async () => {
370
- if (rendering || disposed) return;
491
+ if (disposed) return;
371
492
  const sourceTimeMs = timegroup.currentTimeMs ?? 0;
372
493
  const userTimeMs = timegroup.userTimeMs ?? 0;
373
494
  if (Math.abs(sourceTimeMs - userTimeMs) > TIME_EPSILON_MS) return;
374
495
  if (userTimeMs === lastTimeMs) return;
496
+ if (rendering) return;
375
497
  lastTimeMs = userTimeMs;
376
498
  rendering = true;
377
499
  applyPendingResolutionChange();
378
500
  if (!hasLoggedScale) {
379
501
  hasLoggedScale = true;
380
- const mode = getEffectiveRenderMode();
502
+ const mode = useNative ? "native" : "foreignObject";
381
503
  logger.debug(`[renderTimegroupToCanvas] Resolution scale: ${currentResolutionScale} (${width}x${height} → ${renderWidth}x${renderHeight}), canvas buffer: ${canvas.width}x${canvas.height}, CSS size: ${canvas.style.width}x${canvas.style.height}, renderMode: ${mode}`);
382
504
  }
383
505
  try {
384
- await frameController.renderFrame(userTimeMs);
385
- const { container: container$1, syncState } = buildCloneStructure(timegroup, toAbsoluteTime(timegroup, userTimeMs));
386
- if (currentResolutionScale < 1) {
387
- container$1.style.transform = `scale(${currentResolutionScale})`;
388
- container$1.style.transformOrigin = "top left";
389
- }
390
- while (previewContainer.firstChild !== styleEl && previewContainer.firstChild) previewContainer.removeChild(previewContainer.firstChild);
391
- previewContainer.appendChild(container$1);
392
- overrideRootCloneStyles(syncState);
393
- const removedNodes = removeHiddenNodesForSerialization(syncState);
394
- const t0 = performance.now();
395
- const image = await renderToImage(previewContainer, renderWidth, renderHeight, {
396
- canvasScale: currentResolutionScale,
397
- renderContext,
398
- sourceMap: syncState.canvasSourceMap
506
+ const tFrame = performance.now();
507
+ const tFC0 = performance.now();
508
+ await frameController.renderFrame(userTimeMs, {
509
+ waitForLitUpdate: false,
510
+ onAnimationsUpdate: (root) => {
511
+ updateAnimations(root);
512
+ }
399
513
  });
400
- const renderTime = performance.now() - t0;
401
- restoreHiddenNodes(removedNodes);
402
- const targetWidth = Math.floor(renderWidth * scale * dpr);
403
- const targetHeight = Math.floor(renderHeight * scale * dpr);
404
- if (canvas.width !== targetWidth || canvas.height !== targetHeight) {
405
- canvas.width = targetWidth;
406
- canvas.height = targetHeight;
407
- } else ctx.clearRect(0, 0, canvas.width, canvas.height);
408
- ctx.save();
409
- ctx.scale(dpr * scale, dpr * scale);
410
- ctx.drawImage(image, 0, 0);
411
- ctx.restore();
412
- defaultProfiler.incrementRenderCount();
413
- if (defaultProfiler.shouldLogByFrameCount(60)) logger.debug(`[renderTimegroupToCanvas] Frame render: ${renderTime.toFixed(1)}ms (resolutionScale=${currentResolutionScale}, image=${image.width}x${image.height})`);
414
- updateDebugLabel(debugLabel, renderWidth, renderHeight, currentResolutionScale);
514
+ const fcMs = performance.now() - tFC0;
515
+ const tCapture0 = performance.now();
516
+ if (useNative && captureCanvas && captureCtx) {
517
+ if (captureCanvas.width !== width || captureCanvas.height !== height) {
518
+ captureCtx.save();
519
+ captureCtx.scale(captureCanvas.width / width, captureCanvas.height / height);
520
+ captureCtx.drawElementImage(timegroup, 0, 0);
521
+ captureCtx.restore();
522
+ } else captureCtx.drawElementImage(timegroup, 0, 0);
523
+ const captureMs = performance.now() - tCapture0;
524
+ const tCopy0 = performance.now();
525
+ const targetWidth = Math.floor(renderWidth * scale * dpr);
526
+ const targetHeight = Math.floor(renderHeight * scale * dpr);
527
+ if (canvas.width !== targetWidth || canvas.height !== targetHeight) {
528
+ canvas.width = targetWidth;
529
+ canvas.height = targetHeight;
530
+ } else ctx.clearRect(0, 0, canvas.width, canvas.height);
531
+ ctx.drawImage(captureCanvas, 0, 0, canvas.width, canvas.height);
532
+ const copyMs = performance.now() - tCopy0;
533
+ const frameMs = performance.now() - tFrame;
534
+ frameCount++;
535
+ totalFrameControllerMs += fcMs;
536
+ totalCaptureMs += captureMs;
537
+ totalCopyMs += copyMs;
538
+ totalFrameMs += frameMs;
539
+ defaultProfiler.incrementRenderCount();
540
+ if (defaultProfiler.shouldLogByFrameCount(60)) {
541
+ frameCount = 0;
542
+ totalFrameControllerMs = 0;
543
+ totalCaptureMs = 0;
544
+ totalCopyMs = 0;
545
+ totalFrameMs = 0;
546
+ }
547
+ } else {
548
+ const absoluteTimeMs = toAbsoluteTime(timegroup, userTimeMs);
549
+ const dataUri = await captureTimelineToDataUri(timegroup, width, height, {
550
+ renderContext,
551
+ canvasScale: currentResolutionScale,
552
+ timeMs: absoluteTimeMs
553
+ });
554
+ const captureMs = performance.now() - tCapture0;
555
+ const tCopy0 = performance.now();
556
+ const image = await loadImageFromDataUri(dataUri);
557
+ const copyMs = performance.now() - tCopy0;
558
+ const targetWidth = Math.floor(renderWidth * scale * dpr);
559
+ const targetHeight = Math.floor(renderHeight * scale * dpr);
560
+ if (canvas.width !== targetWidth || canvas.height !== targetHeight) {
561
+ canvas.width = targetWidth;
562
+ canvas.height = targetHeight;
563
+ } else ctx.clearRect(0, 0, canvas.width, canvas.height);
564
+ ctx.save();
565
+ ctx.scale(dpr * scale, dpr * scale);
566
+ ctx.drawImage(image, 0, 0, renderWidth, renderHeight);
567
+ ctx.restore();
568
+ const frameMs = performance.now() - tFrame;
569
+ frameCount++;
570
+ totalFrameControllerMs += fcMs;
571
+ totalCaptureMs += captureMs;
572
+ totalCopyMs += copyMs;
573
+ totalFrameMs += frameMs;
574
+ defaultProfiler.incrementRenderCount();
575
+ if (defaultProfiler.shouldLogByFrameCount(60)) {
576
+ frameCount = 0;
577
+ totalFrameControllerMs = 0;
578
+ totalCaptureMs = 0;
579
+ totalCopyMs = 0;
580
+ totalFrameMs = 0;
581
+ }
582
+ }
415
583
  } catch (e) {
416
584
  logger.error("Canvas preview render failed:", e);
417
585
  } finally {
@@ -426,6 +594,13 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
426
594
  disposed = true;
427
595
  frameController.abort();
428
596
  renderContext.dispose();
597
+ if (useNative && originalParent) {
598
+ timegroup.style.clipPath = savedClipPath;
599
+ timegroup.style.pointerEvents = savedPointerEvents;
600
+ if (originalNextSibling) originalParent.insertBefore(timegroup, originalNextSibling);
601
+ else originalParent.appendChild(timegroup);
602
+ captureCanvas?.remove();
603
+ }
429
604
  };
430
605
  refresh();
431
606
  return {
@@ -439,5 +614,5 @@ function renderTimegroupToCanvas(timegroup, scaleOrOptions = DEFAULT_PREVIEW_SCA
439
614
  }
440
615
 
441
616
  //#endregion
442
- export { captureFromClone, captureTimegroupAtTime, renderTimegroupToCanvas, resetRenderState };
617
+ export { ContentNotReadyError, captureFromClone, captureTimegroupAtTime, clearInlineImageCache, generateThumbnails, generateThumbnailsFromClone, getCacheMetrics, getEffectiveRenderMode, getInlineImageCacheSize, getRenderState, isCanvas, isImage, loadImageFromDataUri, renderTimegroupToCanvas, resetCacheMetrics, resetRenderState, waitForVideoContent };
443
618
  //# sourceMappingURL=renderTimegroupToCanvas.js.map