@editframe/elements 0.30.2-beta.0 → 0.31.0-beta.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 (326) hide show
  1. package/dist/EF_FRAMEGEN.d.ts +5 -0
  2. package/dist/EF_FRAMEGEN.js +20 -4
  3. package/dist/EF_FRAMEGEN.js.map +1 -1
  4. package/dist/EF_INTERACTIVE.js.map +1 -1
  5. package/dist/_virtual/rolldown_runtime.js +27 -0
  6. package/dist/canvas/EFCanvas.d.ts +311 -0
  7. package/dist/canvas/EFCanvas.js +1089 -0
  8. package/dist/canvas/EFCanvas.js.map +1 -0
  9. package/dist/canvas/EFCanvasItem.d.ts +55 -0
  10. package/dist/canvas/EFCanvasItem.js +72 -0
  11. package/dist/canvas/EFCanvasItem.js.map +1 -0
  12. package/dist/canvas/api/CanvasAPI.d.ts +115 -0
  13. package/dist/canvas/api/CanvasAPI.js +182 -0
  14. package/dist/canvas/api/CanvasAPI.js.map +1 -0
  15. package/dist/canvas/api/types.d.ts +42 -0
  16. package/dist/canvas/coordinateTransform.js +90 -0
  17. package/dist/canvas/coordinateTransform.js.map +1 -0
  18. package/dist/canvas/getElementBounds.js +40 -0
  19. package/dist/canvas/getElementBounds.js.map +1 -0
  20. package/dist/canvas/overlays/SelectionOverlay.js +265 -0
  21. package/dist/canvas/overlays/SelectionOverlay.js.map +1 -0
  22. package/dist/canvas/overlays/overlayState.js +153 -0
  23. package/dist/canvas/overlays/overlayState.js.map +1 -0
  24. package/dist/canvas/selection/SelectionController.js +105 -0
  25. package/dist/canvas/selection/SelectionController.js.map +1 -0
  26. package/dist/canvas/selection/SelectionModel.d.ts +98 -0
  27. package/dist/canvas/selection/SelectionModel.js +229 -0
  28. package/dist/canvas/selection/SelectionModel.js.map +1 -0
  29. package/dist/canvas/selection/selectionContext.d.ts +31 -0
  30. package/dist/canvas/selection/selectionContext.js +12 -0
  31. package/dist/canvas/selection/selectionContext.js.map +1 -0
  32. package/dist/elements/ContainerInfo.d.ts +29 -0
  33. package/dist/elements/ContainerInfo.js +30 -0
  34. package/dist/elements/ContainerInfo.js.map +1 -0
  35. package/dist/elements/EFAudio.d.ts +13 -3
  36. package/dist/elements/EFAudio.js +64 -10
  37. package/dist/elements/EFAudio.js.map +1 -1
  38. package/dist/elements/EFCaptions.d.ts +18 -16
  39. package/dist/elements/EFCaptions.js +110 -19
  40. package/dist/elements/EFCaptions.js.map +1 -1
  41. package/dist/elements/EFImage.d.ts +16 -6
  42. package/dist/elements/EFImage.js +79 -9
  43. package/dist/elements/EFImage.js.map +1 -1
  44. package/dist/elements/EFMedia/AssetIdMediaEngine.js +51 -4
  45. package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +1 -1
  46. package/dist/elements/EFMedia/AssetMediaEngine.js +125 -52
  47. package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
  48. package/dist/elements/EFMedia/BaseMediaEngine.js +24 -6
  49. package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
  50. package/dist/elements/EFMedia/JitMediaEngine.js +12 -8
  51. package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
  52. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +46 -7
  53. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js.map +1 -1
  54. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +98 -73
  55. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js.map +1 -1
  56. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +28 -5
  57. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js.map +1 -1
  58. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +18 -6
  59. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js.map +1 -1
  60. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +8 -2
  61. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js.map +1 -1
  62. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +31 -6
  63. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js.map +1 -1
  64. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +28 -5
  65. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js.map +1 -1
  66. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +97 -72
  67. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js.map +1 -1
  68. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -1
  69. package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
  70. package/dist/elements/EFMedia/shared/BufferUtils.js +1 -1
  71. package/dist/elements/EFMedia/shared/BufferUtils.js.map +1 -1
  72. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +25 -14
  73. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
  74. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +47 -16
  75. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js.map +1 -1
  76. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +37 -19
  77. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
  78. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +65 -21
  79. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
  80. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +8 -3
  81. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js.map +1 -1
  82. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js +32 -9
  83. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js.map +1 -1
  84. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +33 -10
  85. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js.map +1 -1
  86. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +23 -8
  87. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js.map +1 -1
  88. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js +34 -10
  89. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js.map +1 -1
  90. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +31 -8
  91. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js.map +1 -1
  92. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +31 -114
  93. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js.map +1 -1
  94. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +44 -8
  95. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js.map +1 -1
  96. package/dist/elements/EFMedia.d.ts +18 -7
  97. package/dist/elements/EFMedia.js +23 -3
  98. package/dist/elements/EFMedia.js.map +1 -1
  99. package/dist/elements/EFPanZoom.d.ts +96 -0
  100. package/dist/elements/EFPanZoom.js +290 -0
  101. package/dist/elements/EFPanZoom.js.map +1 -0
  102. package/dist/elements/EFSourceMixin.js +7 -6
  103. package/dist/elements/EFSourceMixin.js.map +1 -1
  104. package/dist/elements/EFSurface.d.ts +6 -6
  105. package/dist/elements/EFSurface.js +7 -2
  106. package/dist/elements/EFSurface.js.map +1 -1
  107. package/dist/elements/EFTemporal.d.ts +2 -1
  108. package/dist/elements/EFTemporal.js +192 -71
  109. package/dist/elements/EFTemporal.js.map +1 -1
  110. package/dist/elements/EFText.d.ts +5 -4
  111. package/dist/elements/EFText.js +102 -13
  112. package/dist/elements/EFText.js.map +1 -1
  113. package/dist/elements/EFTextSegment.d.ts +32 -6
  114. package/dist/elements/EFTextSegment.js +53 -15
  115. package/dist/elements/EFTextSegment.js.map +1 -1
  116. package/dist/elements/EFThumbnailStrip.d.ts +129 -56
  117. package/dist/elements/EFThumbnailStrip.js +605 -359
  118. package/dist/elements/EFThumbnailStrip.js.map +1 -1
  119. package/dist/elements/EFTimegroup.d.ts +233 -25
  120. package/dist/elements/EFTimegroup.js +865 -144
  121. package/dist/elements/EFTimegroup.js.map +1 -1
  122. package/dist/elements/EFVideo.d.ts +42 -5
  123. package/dist/elements/EFVideo.js +165 -11
  124. package/dist/elements/EFVideo.js.map +1 -1
  125. package/dist/elements/EFWaveform.d.ts +6 -6
  126. package/dist/elements/EFWaveform.js +2 -1
  127. package/dist/elements/EFWaveform.js.map +1 -1
  128. package/dist/elements/ElementPositionInfo.d.ts +35 -0
  129. package/dist/elements/ElementPositionInfo.js +49 -0
  130. package/dist/elements/ElementPositionInfo.js.map +1 -0
  131. package/dist/elements/FetchMixin.js +16 -1
  132. package/dist/elements/FetchMixin.js.map +1 -1
  133. package/dist/elements/SessionThumbnailCache.js +154 -0
  134. package/dist/elements/SessionThumbnailCache.js.map +1 -0
  135. package/dist/elements/TargetController.js +3 -1
  136. package/dist/elements/TargetController.js.map +1 -1
  137. package/dist/elements/TimegroupController.js +9 -3
  138. package/dist/elements/TimegroupController.js.map +1 -1
  139. package/dist/elements/findRootTemporal.js +30 -0
  140. package/dist/elements/findRootTemporal.js.map +1 -0
  141. package/dist/elements/renderTemporalAudio.js +18 -5
  142. package/dist/elements/renderTemporalAudio.js.map +1 -1
  143. package/dist/elements/updateAnimations.js +171 -28
  144. package/dist/elements/updateAnimations.js.map +1 -1
  145. package/dist/getRenderInfo.d.ts +2 -2
  146. package/dist/gui/ContextMixin.js +4 -2
  147. package/dist/gui/ContextMixin.js.map +1 -1
  148. package/dist/gui/Controllable.js +74 -1
  149. package/dist/gui/Controllable.js.map +1 -1
  150. package/dist/gui/EFActiveRootTemporal.d.ts +50 -0
  151. package/dist/gui/EFActiveRootTemporal.js +94 -0
  152. package/dist/gui/EFActiveRootTemporal.js.map +1 -0
  153. package/dist/gui/EFConfiguration.d.ts +7 -1
  154. package/dist/gui/EFConfiguration.js.map +1 -1
  155. package/dist/gui/EFControls.d.ts +2 -2
  156. package/dist/gui/EFControls.js +109 -13
  157. package/dist/gui/EFControls.js.map +1 -1
  158. package/dist/gui/EFDial.d.ts +4 -4
  159. package/dist/gui/EFFilmstrip.d.ts +11 -214
  160. package/dist/gui/EFFilmstrip.js +53 -1152
  161. package/dist/gui/EFFilmstrip.js.map +1 -1
  162. package/dist/gui/EFFitScale.d.ts +3 -3
  163. package/dist/gui/EFFitScale.js +39 -12
  164. package/dist/gui/EFFitScale.js.map +1 -1
  165. package/dist/gui/EFFocusOverlay.d.ts +4 -4
  166. package/dist/gui/EFOverlayItem.d.ts +48 -0
  167. package/dist/gui/EFOverlayItem.js +97 -0
  168. package/dist/gui/EFOverlayItem.js.map +1 -0
  169. package/dist/gui/EFOverlayLayer.d.ts +70 -0
  170. package/dist/gui/EFOverlayLayer.js +104 -0
  171. package/dist/gui/EFOverlayLayer.js.map +1 -0
  172. package/dist/gui/EFPause.d.ts +4 -4
  173. package/dist/gui/EFPlay.d.ts +4 -4
  174. package/dist/gui/EFResizableBox.d.ts +12 -16
  175. package/dist/gui/EFResizableBox.js +109 -451
  176. package/dist/gui/EFResizableBox.js.map +1 -1
  177. package/dist/gui/EFScrubber.d.ts +30 -5
  178. package/dist/gui/EFScrubber.js +224 -31
  179. package/dist/gui/EFScrubber.js.map +1 -1
  180. package/dist/gui/EFTimeDisplay.d.ts +4 -4
  181. package/dist/gui/EFTimeDisplay.js +4 -1
  182. package/dist/gui/EFTimeDisplay.js.map +1 -1
  183. package/dist/gui/EFTimelineRuler.d.ts +71 -0
  184. package/dist/gui/EFTimelineRuler.js +320 -0
  185. package/dist/gui/EFTimelineRuler.js.map +1 -0
  186. package/dist/gui/EFToggleLoop.d.ts +4 -4
  187. package/dist/gui/EFTogglePlay.d.ts +4 -4
  188. package/dist/gui/EFTransformHandles.d.ts +91 -0
  189. package/dist/gui/EFTransformHandles.js +393 -0
  190. package/dist/gui/EFTransformHandles.js.map +1 -0
  191. package/dist/gui/EFWorkbench.d.ts +178 -0
  192. package/dist/gui/EFWorkbench.js +2067 -22
  193. package/dist/gui/EFWorkbench.js.map +1 -1
  194. package/dist/gui/FitScaleHelpers.d.ts +31 -0
  195. package/dist/gui/FitScaleHelpers.js +41 -0
  196. package/dist/gui/FitScaleHelpers.js.map +1 -0
  197. package/dist/gui/PlaybackController.d.ts +2 -1
  198. package/dist/gui/PlaybackController.js +46 -15
  199. package/dist/gui/PlaybackController.js.map +1 -1
  200. package/dist/gui/TWMixin.js +1 -1
  201. package/dist/gui/TWMixin.js.map +1 -1
  202. package/dist/gui/hierarchy/EFHierarchy.d.ts +65 -0
  203. package/dist/gui/hierarchy/EFHierarchy.js +338 -0
  204. package/dist/gui/hierarchy/EFHierarchy.js.map +1 -0
  205. package/dist/gui/hierarchy/EFHierarchyItem.d.ts +118 -0
  206. package/dist/gui/hierarchy/EFHierarchyItem.js +551 -0
  207. package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -0
  208. package/dist/gui/hierarchy/hierarchyContext.d.ts +38 -0
  209. package/dist/gui/hierarchy/hierarchyContext.js +8 -0
  210. package/dist/gui/hierarchy/hierarchyContext.js.map +1 -0
  211. package/dist/gui/icons.js +34 -0
  212. package/dist/gui/icons.js.map +1 -0
  213. package/dist/gui/panZoomTransformContext.js +12 -0
  214. package/dist/gui/panZoomTransformContext.js.map +1 -0
  215. package/dist/gui/previewSettingsContext.js +12 -0
  216. package/dist/gui/previewSettingsContext.js.map +1 -0
  217. package/dist/gui/timeline/EFTimeline.d.ts +270 -0
  218. package/dist/gui/timeline/EFTimeline.js +1369 -0
  219. package/dist/gui/timeline/EFTimeline.js.map +1 -0
  220. package/dist/gui/timeline/EFTimelineRow.js +374 -0
  221. package/dist/gui/timeline/EFTimelineRow.js.map +1 -0
  222. package/dist/gui/timeline/TrimHandles.d.ts +36 -0
  223. package/dist/gui/timeline/TrimHandles.js +204 -0
  224. package/dist/gui/timeline/TrimHandles.js.map +1 -0
  225. package/dist/gui/timeline/flattenHierarchy.js +31 -0
  226. package/dist/gui/timeline/flattenHierarchy.js.map +1 -0
  227. package/dist/gui/timeline/timelineStateContext.d.ts +26 -0
  228. package/dist/gui/timeline/timelineStateContext.js +42 -0
  229. package/dist/gui/timeline/timelineStateContext.js.map +1 -0
  230. package/dist/gui/timeline/tracks/AudioTrack.js +264 -0
  231. package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -0
  232. package/dist/gui/timeline/tracks/CaptionsTrack.js +595 -0
  233. package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -0
  234. package/dist/gui/timeline/tracks/HTMLTrack.js +19 -0
  235. package/dist/gui/timeline/tracks/HTMLTrack.js.map +1 -0
  236. package/dist/gui/timeline/tracks/ImageTrack.js +53 -0
  237. package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -0
  238. package/dist/gui/timeline/tracks/TextTrack.js +250 -0
  239. package/dist/gui/timeline/tracks/TextTrack.js.map +1 -0
  240. package/dist/gui/timeline/tracks/TimegroupTrack.js +143 -0
  241. package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -0
  242. package/dist/gui/timeline/tracks/TrackItem.js +269 -0
  243. package/dist/gui/timeline/tracks/TrackItem.js.map +1 -0
  244. package/dist/gui/timeline/tracks/VideoTrack.js +265 -0
  245. package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -0
  246. package/dist/gui/timeline/tracks/WaveformTrack.js +19 -0
  247. package/dist/gui/timeline/tracks/WaveformTrack.js.map +1 -0
  248. package/dist/gui/timeline/tracks/ensureTrackItemInit.js +1 -0
  249. package/dist/gui/timeline/tracks/preloadTracks.js +9 -0
  250. package/dist/gui/timeline/tracks/renderTrackChildren.js +119 -0
  251. package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -0
  252. package/dist/gui/timeline/tracks/waveformUtils.js +80 -0
  253. package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -0
  254. package/dist/gui/transformCalculations.js +217 -0
  255. package/dist/gui/transformCalculations.js.map +1 -0
  256. package/dist/gui/transformUtils.d.ts +37 -0
  257. package/dist/gui/transformUtils.js +77 -0
  258. package/dist/gui/transformUtils.js.map +1 -0
  259. package/dist/gui/tree/EFTree.d.ts +59 -0
  260. package/dist/gui/tree/EFTree.js +174 -0
  261. package/dist/gui/tree/EFTree.js.map +1 -0
  262. package/dist/gui/tree/EFTreeItem.d.ts +38 -0
  263. package/dist/gui/tree/EFTreeItem.js +146 -0
  264. package/dist/gui/tree/EFTreeItem.js.map +1 -0
  265. package/dist/gui/tree/treeContext.d.ts +60 -0
  266. package/dist/gui/tree/treeContext.js +23 -0
  267. package/dist/gui/tree/treeContext.js.map +1 -0
  268. package/dist/index.d.ts +32 -8
  269. package/dist/index.js +30 -6
  270. package/dist/index.js.map +1 -1
  271. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +688 -0
  272. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +1 -0
  273. package/dist/node_modules/react/cjs/react.development.js +1521 -0
  274. package/dist/node_modules/react/cjs/react.development.js.map +1 -0
  275. package/dist/node_modules/react/index.js +13 -0
  276. package/dist/node_modules/react/index.js.map +1 -0
  277. package/dist/node_modules/react/jsx-runtime.js +13 -0
  278. package/dist/node_modules/react/jsx-runtime.js.map +1 -0
  279. package/dist/preview/AdaptiveResolutionTracker.js +228 -0
  280. package/dist/preview/AdaptiveResolutionTracker.js.map +1 -0
  281. package/dist/preview/RenderProfiler.js +135 -0
  282. package/dist/preview/RenderProfiler.js.map +1 -0
  283. package/dist/preview/previewSettings.js +131 -0
  284. package/dist/preview/previewSettings.js.map +1 -0
  285. package/dist/preview/previewTypes.js +64 -0
  286. package/dist/preview/previewTypes.js.map +1 -0
  287. package/dist/preview/renderTimegroupPreview.js +656 -0
  288. package/dist/preview/renderTimegroupPreview.js.map +1 -0
  289. package/dist/preview/renderTimegroupToCanvas.d.ts +37 -0
  290. package/dist/preview/renderTimegroupToCanvas.js +833 -0
  291. package/dist/preview/renderTimegroupToCanvas.js.map +1 -0
  292. package/dist/preview/renderTimegroupToVideo.d.ts +39 -0
  293. package/dist/preview/renderTimegroupToVideo.js +274 -0
  294. package/dist/preview/renderTimegroupToVideo.js.map +1 -0
  295. package/dist/preview/renderers.js +16 -0
  296. package/dist/preview/renderers.js.map +1 -0
  297. package/dist/preview/statsTrackingStrategy.js +201 -0
  298. package/dist/preview/statsTrackingStrategy.js.map +1 -0
  299. package/dist/preview/thumbnailCacheSettings.js +52 -0
  300. package/dist/preview/thumbnailCacheSettings.js.map +1 -0
  301. package/dist/preview/workers/WorkerPool.js +178 -0
  302. package/dist/preview/workers/WorkerPool.js.map +1 -0
  303. package/dist/preview/workers/encoderWorkerInline.js +103 -0
  304. package/dist/preview/workers/encoderWorkerInline.js.map +1 -0
  305. package/dist/sandbox/PlaybackControls.js +10 -0
  306. package/dist/sandbox/PlaybackControls.js.map +1 -0
  307. package/dist/sandbox/ScenarioRunner.js +1 -0
  308. package/dist/sandbox/index.js +2 -0
  309. package/dist/style.css +71 -67
  310. package/dist/transcoding/types/index.d.ts +2 -1
  311. package/dist/transcoding/utils/UrlGenerator.d.ts +6 -1
  312. package/dist/transcoding/utils/UrlGenerator.js +12 -3
  313. package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
  314. package/dist/utils/LRUCache.js +1 -375
  315. package/dist/utils/LRUCache.js.map +1 -1
  316. package/dist/utils/frameTime.js +14 -0
  317. package/dist/utils/frameTime.js.map +1 -0
  318. package/package.json +3 -3
  319. package/test/profilingPlugin.ts +223 -0
  320. package/test/recordReplayProxyPlugin.js +22 -27
  321. package/test/thumbnail-performance-test.html +116 -0
  322. package/test/visualRegressionUtils.ts +286 -0
  323. package/types.json +1 -1
  324. package/dist/elements/TimegroupController.d.ts +0 -18
  325. package/dist/msToTimeCode.js +0 -17
  326. package/dist/msToTimeCode.js.map +0 -1
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Vitest Browser Profiling Plugin
3
+ *
4
+ * Captures Chrome DevTools CPU profiles during browser tests.
5
+ * Enable with VITEST_PROFILE=1 environment variable.
6
+ *
7
+ * Usage:
8
+ * VITEST_PROFILE=1 ./scripts/browsertest <test-file>
9
+ * ./scripts/browsertest --profile <test-file>
10
+ *
11
+ * Output:
12
+ * Creates ./browsertest-profile.cpuprofile in the elements directory
13
+ */
14
+
15
+ import type { Plugin } from "vite";
16
+ import * as fs from "node:fs";
17
+ import * as path from "node:path";
18
+
19
+ interface ProfileNode {
20
+ id: number;
21
+ callFrame: {
22
+ functionName: string;
23
+ scriptId: string;
24
+ url: string;
25
+ lineNumber: number;
26
+ columnNumber: number;
27
+ };
28
+ hitCount?: number;
29
+ children?: number[];
30
+ positionTicks?: { line: number; ticks: number }[];
31
+ }
32
+
33
+ interface CPUProfile {
34
+ nodes: ProfileNode[];
35
+ startTime: number;
36
+ endTime: number;
37
+ samples: number[];
38
+ timeDeltas: number[];
39
+ }
40
+
41
+ // Global state for CDP session and profile
42
+ let cdpSession: any = null;
43
+ let profilingStartTime: number = 0;
44
+
45
+ export function profilingPlugin(): Plugin {
46
+ const isProfilingEnabled = process.env.VITEST_PROFILE === "1";
47
+
48
+ if (!isProfilingEnabled) {
49
+ return { name: "vitest-profiling-disabled" };
50
+ }
51
+
52
+ console.log("\n🔬 CPU Profiling enabled for browser tests\n");
53
+
54
+ return {
55
+ name: "vitest-browser-profiling",
56
+
57
+ // Hook into server configuration to get access to the browser
58
+ configureServer(server) {
59
+ // We need to hook into the Vitest browser lifecycle
60
+ // This is tricky because Vitest manages the browser connection
61
+
62
+ // Add an endpoint that tests can call to start/stop profiling
63
+ server.middlewares.use("/__vitest_profile__/start", async (_req, res) => {
64
+ try {
65
+ // Get the CDP session from the connected browser
66
+ // This requires access to the Playwright page, which Vitest manages
67
+ res.writeHead(200, { "Content-Type": "application/json" });
68
+ res.end(JSON.stringify({ status: "profiling_start_requested" }));
69
+ } catch (error) {
70
+ res.writeHead(500, { "Content-Type": "application/json" });
71
+ res.end(JSON.stringify({ error: String(error) }));
72
+ }
73
+ });
74
+
75
+ server.middlewares.use("/__vitest_profile__/stop", async (_req, res) => {
76
+ try {
77
+ res.writeHead(200, { "Content-Type": "application/json" });
78
+ res.end(JSON.stringify({ status: "profiling_stop_requested" }));
79
+ } catch (error) {
80
+ res.writeHead(500, { "Content-Type": "application/json" });
81
+ res.end(JSON.stringify({ error: String(error) }));
82
+ }
83
+ });
84
+ },
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Start CDP profiling on a Playwright page
90
+ * Call this from test setup or beforeAll
91
+ */
92
+ export async function startProfiling(page: any): Promise<void> {
93
+ if (process.env.VITEST_PROFILE !== "1") return;
94
+
95
+ try {
96
+ // Get CDP session from Playwright page
97
+ // In Playwright, we access CDP via page.context().newCDPSession(page)
98
+ const context = page.context();
99
+ cdpSession = await context.newCDPSession(page);
100
+
101
+ await cdpSession.send("Profiler.enable");
102
+ await cdpSession.send("Profiler.setSamplingInterval", { interval: 100 }); // 100µs
103
+ await cdpSession.send("Profiler.start");
104
+
105
+ profilingStartTime = Date.now();
106
+ console.log("🎬 CPU profiling started");
107
+ } catch (error) {
108
+ console.error("Failed to start profiling:", error);
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Stop CDP profiling and save the profile
114
+ * Call this from test teardown or afterAll
115
+ */
116
+ export async function stopProfiling(
117
+ outputPath?: string,
118
+ ): Promise<CPUProfile | null> {
119
+ if (process.env.VITEST_PROFILE !== "1" || !cdpSession) return null;
120
+
121
+ try {
122
+ const { profile } = (await cdpSession.send("Profiler.stop")) as {
123
+ profile: CPUProfile;
124
+ };
125
+ await cdpSession.send("Profiler.disable");
126
+
127
+ const duration = Date.now() - profilingStartTime;
128
+ console.log(`⏱️ CPU profiling stopped after ${duration}ms`);
129
+
130
+ // Save profile
131
+ const finalPath = outputPath || "./browsertest-profile.cpuprofile";
132
+ const profileJson = JSON.stringify(profile, null, 2);
133
+ fs.writeFileSync(finalPath, profileJson);
134
+ console.log(`💾 Profile saved to: ${finalPath}`);
135
+ console.log(` Load in Chrome DevTools → Performance → Load profile`);
136
+
137
+ // Print summary
138
+ printProfileSummary(profile, duration);
139
+
140
+ cdpSession = null;
141
+ return profile;
142
+ } catch (error) {
143
+ console.error("Failed to stop profiling:", error);
144
+ return null;
145
+ }
146
+ }
147
+
148
+ function printProfileSummary(profile: CPUProfile, wallClockMs: number): void {
149
+ const hitCounts = new Map<number, number>();
150
+ for (const sample of profile.samples) {
151
+ hitCounts.set(sample, (hitCounts.get(sample) || 0) + 1);
152
+ }
153
+
154
+ const sampleIntervalUs =
155
+ profile.timeDeltas.length > 0
156
+ ? profile.timeDeltas.reduce((a, b) => a + b, 0) /
157
+ profile.timeDeltas.length
158
+ : 1000;
159
+
160
+ const totalSamples = profile.samples.length;
161
+ const profileTimeMs = (totalSamples * sampleIntervalUs) / 1000;
162
+
163
+ // Build hotspots
164
+ const hotspots: { name: string; file: string; timeMs: number }[] = [];
165
+ for (const node of profile.nodes) {
166
+ const hitCount = hitCounts.get(node.id) || 0;
167
+ if (hitCount === 0) continue;
168
+
169
+ const selfTimeMs = (hitCount * sampleIntervalUs) / 1000;
170
+ const file =
171
+ node.callFrame.url?.split("/").slice(-1)[0]?.split("?")[0] || "(native)";
172
+
173
+ hotspots.push({
174
+ name: node.callFrame.functionName || "(anonymous)",
175
+ file,
176
+ timeMs: selfTimeMs,
177
+ });
178
+ }
179
+
180
+ hotspots.sort((a, b) => b.timeMs - a.timeMs);
181
+
182
+ // Group by file
183
+ const byFile = new Map<string, number>();
184
+ for (const h of hotspots) {
185
+ byFile.set(h.file, (byFile.get(h.file) || 0) + h.timeMs);
186
+ }
187
+ const sortedFiles = Array.from(byFile.entries()).sort((a, b) => b[1] - a[1]);
188
+
189
+ console.log(
190
+ `\n📊 Profile Summary (${wallClockMs}ms wall clock, ${profileTimeMs.toFixed(1)}ms profile time)`,
191
+ );
192
+ console.log(`\n Top files:`);
193
+ for (const [file, time] of sortedFiles.slice(0, 10)) {
194
+ const pct = ((time / profileTimeMs) * 100).toFixed(1);
195
+ console.log(
196
+ ` ${time.toFixed(1).padStart(8)}ms (${pct.padStart(5)}%) ${file}`,
197
+ );
198
+ }
199
+
200
+ // Our code
201
+ const ourCode = hotspots.filter(
202
+ (h) =>
203
+ h.file.includes(".ts") &&
204
+ !h.file.includes("node_modules") &&
205
+ (h.file.includes("render") ||
206
+ h.file.includes("preview") ||
207
+ h.file.includes("element") ||
208
+ h.file.includes("Timegroup") ||
209
+ h.file.includes("clone") ||
210
+ h.file.includes("sync")),
211
+ );
212
+
213
+ if (ourCode.length > 0) {
214
+ console.log(`\n Top functions in our code:`);
215
+ for (const h of ourCode.slice(0, 10)) {
216
+ console.log(
217
+ ` ${h.timeMs.toFixed(1).padStart(8)}ms ${h.name.slice(0, 40).padEnd(40)} ${h.file}`,
218
+ );
219
+ }
220
+ }
221
+
222
+ console.log();
223
+ }
@@ -3,8 +3,11 @@ import { existsSync } from "node:fs";
3
3
  import { mkdir, readFile, writeFile } from "node:fs/promises";
4
4
  import { dirname, join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
+ import debug from "debug";
6
7
  import { TEST_SERVER_PORT } from "./constants.js";
7
8
 
9
+ const log = debug("ef:recordReplayProxyPlugin");
10
+
8
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
9
12
  const CACHE_DIR = join(__dirname, "__cache__");
10
13
  const TARGET_HOST = "host.docker.internal";
@@ -43,7 +46,7 @@ export function recordReplayProxyPlugin() {
43
46
  name: "record-replay-proxy",
44
47
 
45
48
  configureServer(server) {
46
- console.log(
49
+ log(
47
50
  `[Proxy Plugin] Configuring record-replay proxy middleware... ${CACHE_ONLY_MODE ? "(CACHE-ONLY MODE)" : ""}`,
48
51
  );
49
52
 
@@ -60,10 +63,10 @@ export function recordReplayProxyPlugin() {
60
63
  await handleProxyRequest(req, res, next);
61
64
  });
62
65
 
63
- console.log("[Proxy Plugin] Proxy middleware configured");
64
- console.log(`[Proxy Plugin] Cache directory: ${CACHE_DIR}`);
66
+ log("[Proxy Plugin] Proxy middleware configured");
67
+ log(`[Proxy Plugin] Cache directory: ${CACHE_DIR}`);
65
68
  if (CACHE_ONLY_MODE) {
66
- console.log(
69
+ log(
67
70
  "[Proxy Plugin] ⚠️ Running in CACHE-ONLY mode - no remote fetching",
68
71
  );
69
72
  }
@@ -122,9 +125,7 @@ export function recordReplayProxyPlugin() {
122
125
  responseHeaders["content-length"] = body.length.toString();
123
126
  }
124
127
 
125
- console.log(
126
- `[Proxy] ✓ Rewrote cached URLs: ${originalHost} → ${proxyHost}`,
127
- );
128
+ log(`[Proxy] ✓ Rewrote cached URLs: ${originalHost} → ${proxyHost}`);
128
129
  } catch (error) {
129
130
  console.warn(
130
131
  `[Proxy] Failed to rewrite cached URLs: ${error.message}`,
@@ -199,7 +200,7 @@ export function recordReplayProxyPlugin() {
199
200
  const dataFile = join(cacheDir, "data.bin");
200
201
  await writeFile(dataFile, body); // Write raw binary data
201
202
 
202
- console.log("[Proxy] ✓ Cached response");
203
+ log("[Proxy] ✓ Cached response");
203
204
  } catch (error) {
204
205
  console.warn(`[Proxy] Failed to cache: ${error.message}`);
205
206
  }
@@ -226,9 +227,9 @@ export function recordReplayProxyPlugin() {
226
227
  }
227
228
 
228
229
  const fullPath = apiPath;
229
- console.log(`[Proxy] → ${req.method} ${fullPath}`);
230
+ log(`[Proxy] → ${req.method} ${fullPath}`);
230
231
  if (req.headers.range) {
231
- console.log(`[Proxy] Range: ${req.headers.range}`);
232
+ log(`[Proxy] Range: ${req.headers.range}`);
232
233
  }
233
234
 
234
235
  // Set CORS headers
@@ -257,9 +258,7 @@ export function recordReplayProxyPlugin() {
257
258
  try {
258
259
  const metadataFile = join(cacheDir, "metadata.json");
259
260
  if (existsSync(metadataFile)) {
260
- console.log(
261
- `[Proxy] ✓ CACHE-ONLY: Serving from cache: ${cacheKey}`,
262
- );
261
+ log(`[Proxy] ✓ CACHE-ONLY: Serving from cache: ${cacheKey}`);
263
262
  await serveCachedResponse(res, cacheDir, req);
264
263
  return;
265
264
  }
@@ -268,7 +267,7 @@ export function recordReplayProxyPlugin() {
268
267
  }
269
268
  }
270
269
 
271
- console.log(`[Proxy] ✗ CACHE-ONLY: No cache available for ${cacheKey}`);
270
+ log(`[Proxy] ✗ CACHE-ONLY: No cache available for ${cacheKey}`);
272
271
  res.writeHead(404, { "Content-Type": "application/json" });
273
272
  res.end(
274
273
  JSON.stringify({
@@ -339,9 +338,7 @@ export function recordReplayProxyPlugin() {
339
338
  responseHeaders["content-length"] = body.length.toString();
340
339
  }
341
340
 
342
- console.log(
343
- `[Proxy] ✓ Rewrote URLs: ${originalHost} → ${proxyHost}`,
344
- );
341
+ log(`[Proxy] ✓ Rewrote URLs: ${originalHost} → ${proxyHost}`);
345
342
  } catch (error) {
346
343
  console.warn(`[Proxy] Failed to rewrite URLs: ${error.message}`);
347
344
  // Continue with original body on error
@@ -350,8 +347,8 @@ export function recordReplayProxyPlugin() {
350
347
 
351
348
  // If we get a 404, try to serve from cache first
352
349
  if (response.status === 404) {
353
- console.log("[Proxy] ✗ Target server returned 404");
354
- console.log(`[Proxy] Checking cache: ${cacheKey}`);
350
+ log("[Proxy] ✗ Target server returned 404");
351
+ log(`[Proxy] Checking cache: ${cacheKey}`);
355
352
 
356
353
  if (existsSync(cacheDir)) {
357
354
  try {
@@ -360,7 +357,7 @@ export function recordReplayProxyPlugin() {
360
357
  const metadata = JSON.parse(
361
358
  await readFile(metadataFile, "utf-8"),
362
359
  );
363
- console.log(
360
+ log(
364
361
  `[Proxy] ✓ Serving from cache instead of 404 (${metadata.timestamp})`,
365
362
  );
366
363
  await serveCachedResponse(res, cacheDir, req);
@@ -373,7 +370,7 @@ export function recordReplayProxyPlugin() {
373
370
  }
374
371
  }
375
372
 
376
- console.log("[Proxy] ✗ No cache available, passing through 404");
373
+ log("[Proxy] ✗ No cache available, passing through 404");
377
374
  }
378
375
 
379
376
  res.writeHead(response.status, responseHeaders);
@@ -391,8 +388,8 @@ export function recordReplayProxyPlugin() {
391
388
  );
392
389
  }
393
390
  } catch (err) {
394
- console.log(`[Proxy] ✗ Real server failed: ${err.message}`);
395
- console.log(`[Proxy] Checking cache: ${cacheKey}`);
391
+ log(`[Proxy] ✗ Real server failed: ${err.message}`);
392
+ log(`[Proxy] Checking cache: ${cacheKey}`);
396
393
 
397
394
  if (existsSync(cacheDir)) {
398
395
  try {
@@ -401,9 +398,7 @@ export function recordReplayProxyPlugin() {
401
398
  const metadata = JSON.parse(
402
399
  await readFile(metadataFile, "utf-8"),
403
400
  );
404
- console.log(
405
- `[Proxy] ✓ Serving from cache (${metadata.timestamp})`,
406
- );
401
+ log(`[Proxy] ✓ Serving from cache (${metadata.timestamp})`);
407
402
  await serveCachedResponse(res, cacheDir, req);
408
403
  return;
409
404
  }
@@ -414,7 +409,7 @@ export function recordReplayProxyPlugin() {
414
409
  }
415
410
  }
416
411
 
417
- console.log("[Proxy] ✗ No cache available, failing request");
412
+ log("[Proxy] ✗ No cache available, failing request");
418
413
  res.writeHead(500, { "Content-Type": "application/json" });
419
414
  res.end(
420
415
  JSON.stringify({
@@ -0,0 +1,116 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Thumbnail Performance Test</title>
5
+ <style>
6
+ body {
7
+ margin: 0;
8
+ padding: 20px;
9
+ font-family: system-ui;
10
+ }
11
+ ef-timegroup {
12
+ width: 1920px;
13
+ height: 1080px;
14
+ background: #000;
15
+ }
16
+ #results {
17
+ margin-top: 20px;
18
+ font-family: monospace;
19
+ white-space: pre-wrap;
20
+ }
21
+ </style>
22
+ </head>
23
+ <body>
24
+ <h1>Thumbnail Capture Performance Test</h1>
25
+ <p>Testing captureBatch with yield between captures and size limits</p>
26
+
27
+ <ef-timegroup id="test-timegroup" mode="contain" fps="30" duration="10000">
28
+ <ef-video src="test-assets/bars-n-tone.mp4" start-time="0" duration="10000"></ef-video>
29
+ </ef-timegroup>
30
+
31
+ <div id="results"></div>
32
+
33
+ <script type="module">
34
+ import '/packages/elements/src/elements/EFTimegroup.js';
35
+ import '/packages/elements/src/elements/EFVideo.js';
36
+
37
+ const timegroup = document.querySelector('ef-timegroup');
38
+ const results = document.getElementById('results');
39
+
40
+ async function runTest() {
41
+ results.textContent = 'Waiting for timegroup to initialize...\n';
42
+
43
+ // Wait for media to load
44
+ await timegroup.updateComplete;
45
+ if (timegroup.waitForMediaDurations) {
46
+ await timegroup.waitForMediaDurations();
47
+ }
48
+
49
+ // Generate timestamps for 15 thumbnails
50
+ const duration = timegroup.durationMs;
51
+ const timestamps = [];
52
+ for (let i = 0; i < 15; i++) {
53
+ timestamps.push((i * duration) / 14);
54
+ }
55
+
56
+ results.textContent += `Testing captureBatch with ${timestamps.length} thumbnails...\n`;
57
+ results.textContent += `Duration: ${duration}ms\n`;
58
+ results.textContent += `Timestamps: ${timestamps.map(t => Math.round(t)).join(', ')}ms\n\n`;
59
+
60
+ // Measure captureBatch performance
61
+ const startTime = performance.now();
62
+ const canvases = await timegroup.captureBatch(timestamps, {
63
+ scale: 0.25,
64
+ contentReadyMode: 'immediate'
65
+ });
66
+ const endTime = performance.now();
67
+ const elapsed = endTime - startTime;
68
+
69
+ results.textContent += `\n✅ Capture completed!\n`;
70
+ results.textContent += `Total time: ${elapsed.toFixed(2)}ms\n`;
71
+ results.textContent += `Average per thumbnail: ${(elapsed / timestamps.length).toFixed(2)}ms\n`;
72
+ results.textContent += `Canvases captured: ${canvases.length}\n`;
73
+
74
+ // Check canvas dimensions
75
+ if (canvases.length > 0) {
76
+ const firstCanvas = canvases[0];
77
+ results.textContent += `\nCanvas dimensions:\n`;
78
+ results.textContent += ` Width: ${firstCanvas.width}px (logical: ${firstCanvas.style.width})\n`;
79
+ results.textContent += ` Height: ${firstCanvas.height}px (logical: ${firstCanvas.style.height})\n`;
80
+
81
+ // Verify max size limit (480px max width)
82
+ const logicalWidth = parseInt(firstCanvas.style.width);
83
+ if (logicalWidth <= 480) {
84
+ results.textContent += ` ✅ Size limit enforced (${logicalWidth}px <= 480px)\n`;
85
+ } else {
86
+ results.textContent += ` ⚠️ Size limit NOT enforced (${logicalWidth}px > 480px)\n`;
87
+ }
88
+ }
89
+
90
+ // Test responsiveness - try to interact during capture
91
+ results.textContent += `\nTesting responsiveness during capture...\n`;
92
+ let interactionCount = 0;
93
+ const interactionInterval = setInterval(() => {
94
+ interactionCount++;
95
+ results.textContent += ` Interaction ${interactionCount} processed\n`;
96
+ }, 10);
97
+
98
+ const startTime2 = performance.now();
99
+ const canvases2 = await timegroup.captureBatch(timestamps.slice(0, 5), {
100
+ scale: 0.25,
101
+ contentReadyMode: 'immediate'
102
+ });
103
+ const endTime2 = performance.now();
104
+ clearInterval(interactionInterval);
105
+
106
+ results.textContent += `\n✅ Responsiveness test completed!\n`;
107
+ results.textContent += `Capture time: ${(endTime2 - startTime2).toFixed(2)}ms\n`;
108
+ results.textContent += `Interactions processed: ${interactionCount}\n`;
109
+ results.textContent += ` (Higher is better - shows browser stayed responsive)\n`;
110
+ }
111
+
112
+ // Run test after a short delay to ensure everything is loaded
113
+ setTimeout(runTest, 1000);
114
+ </script>
115
+ </body>
116
+ </html>