@editframe/elements 0.37.2-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 +5 -6
  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 +0,0 @@
1
- {"version":3,"file":"PlaybackControls.js","names":[],"sources":["../../src/sandbox/PlaybackControls.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport type { EFTimegroup } from \"../elements/EFTimegroup.js\";\n\nexport interface PlaybackControlsProps {\n /**\n * The timegroup element to control.\n * If null, controls are disabled.\n */\n timegroup: EFTimegroup | null;\n \n /**\n * Playback mode:\n * - \"auto\": Play/pause with continuous scrubbing\n * - \"step\": Discrete keyframe navigation\n */\n mode?: \"auto\" | \"step\";\n \n /**\n * Callback when mode changes.\n */\n onModeChange?: (mode: \"auto\" | \"step\") => void;\n \n /**\n * Keyframes for step mode (array of time values in ms).\n * If not provided, step mode divides duration into 10 equal steps.\n */\n keyframes?: number[];\n}\n\nfunction formatTime(ms: number): string {\n const totalSeconds = Math.floor(ms / 1000);\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n const centiseconds = Math.floor((ms % 1000) / 10);\n return `${minutes}:${seconds.toString().padStart(2, \"0\")}.${centiseconds.toString().padStart(2, \"0\")}`;\n}\n\nexport function PlaybackControls({\n timegroup,\n mode = \"auto\",\n onModeChange,\n keyframes,\n}: PlaybackControlsProps) {\n const [isPlaying, setIsPlaying] = useState(false);\n const [isLooping, setIsLooping] = useState(false);\n const [currentTimeMs, setCurrentTimeMs] = useState(0);\n const [durationMs, setDurationMs] = useState(0);\n const [isDragging, setIsDragging] = useState(false);\n const [currentStepIndex, setCurrentStepIndex] = useState(0);\n \n const scrubberRef = useRef<HTMLDivElement>(null);\n const updateIntervalRef = useRef<number | null>(null);\n\n // Calculate step keyframes\n const steps = React.useMemo(() => {\n if (keyframes && keyframes.length > 0) {\n return keyframes;\n }\n // Default: 11 steps (0%, 10%, 20%, ..., 100%)\n const stepCount = 11;\n const stepSize = durationMs / (stepCount - 1);\n return Array.from({ length: stepCount }, (_, i) => i * stepSize);\n }, [keyframes, durationMs]);\n\n // Update duration when timegroup changes\n useEffect(() => {\n if (timegroup) {\n setDurationMs(timegroup.durationMs);\n setCurrentTimeMs(timegroup.currentTimeMs);\n } else {\n setDurationMs(0);\n setCurrentTimeMs(0);\n }\n }, [timegroup]);\n\n // Update current time periodically\n useEffect(() => {\n if (!timegroup) return;\n\n const updateTime = () => {\n if (!isDragging) {\n setCurrentTimeMs(timegroup.currentTimeMs);\n setIsPlaying(timegroup.playbackController?.playing ?? false);\n }\n };\n\n updateIntervalRef.current = window.setInterval(updateTime, 50);\n\n return () => {\n if (updateIntervalRef.current) {\n window.clearInterval(updateIntervalRef.current);\n }\n };\n }, [timegroup, isDragging]);\n\n // Update step index when time changes in step mode\n useEffect(() => {\n if (mode === \"step\" && steps.length > 0) {\n // Find closest step\n let closestIndex = 0;\n const firstStep = steps[0];\n if (firstStep !== undefined) {\n let closestDiff = Math.abs(currentTimeMs - firstStep);\n for (let i = 1; i < steps.length; i++) {\n const step = steps[i];\n if (step !== undefined) {\n const diff = Math.abs(currentTimeMs - step);\n if (diff < closestDiff) {\n closestDiff = diff;\n closestIndex = i;\n }\n }\n }\n setCurrentStepIndex(closestIndex);\n }\n }\n }, [currentTimeMs, mode, steps]);\n\n const handlePlayPause = useCallback(async () => {\n if (!timegroup) return;\n \n if (isPlaying) {\n timegroup.pause();\n setIsPlaying(false);\n } else {\n // Handle AudioContext for mobile devices\n if (timegroup.playbackController) {\n try {\n const audioContext = new AudioContext({ latencyHint: \"playback\" });\n audioContext.resume();\n timegroup.playbackController.setPendingAudioContext(audioContext);\n } catch (error) {\n console.warn(\"Failed to create/resume AudioContext:\", error);\n }\n }\n await timegroup.play();\n setIsPlaying(true);\n }\n }, [timegroup, isPlaying]);\n\n const handleLoopToggle = useCallback(() => {\n if (!timegroup?.playbackController) return;\n \n const newLooping = !isLooping;\n // @ts-expect-error - loop property is read-only in types but writable at runtime\n timegroup.playbackController.loop = newLooping;\n setIsLooping(newLooping);\n }, [timegroup, isLooping]);\n\n const handleScrubStart = useCallback((e: React.MouseEvent | React.TouchEvent) => {\n if (!timegroup || !scrubberRef.current) return;\n \n setIsDragging(true);\n handleScrub(e);\n }, [timegroup]);\n\n const handleScrub = useCallback((e: React.MouseEvent | React.TouchEvent | MouseEvent | TouchEvent) => {\n if (!timegroup || !scrubberRef.current || !isDragging) return;\n \n const scrubber = scrubberRef.current;\n if (!scrubber) return;\n \n const rect = scrubber.getBoundingClientRect();\n const touches = \"touches\" in e ? e.touches : null;\n const clientX = touches && touches[0] \n ? touches[0].clientX \n : (e as MouseEvent).clientX;\n const x = clientX - rect.left;\n const percent = Math.max(0, Math.min(1, x / rect.width));\n const targetTimeMs = percent * durationMs;\n \n timegroup.currentTime = targetTimeMs / 1000;\n setCurrentTimeMs(targetTimeMs);\n }, [timegroup, durationMs, isDragging]);\n\n const handleScrubEnd = useCallback(() => {\n setIsDragging(false);\n }, []);\n\n // Global mouse/touch events for scrubbing\n useEffect(() => {\n if (isDragging) {\n const handleMove = (e: MouseEvent | TouchEvent) => handleScrub(e);\n const handleUp = () => handleScrubEnd();\n \n document.addEventListener(\"mousemove\", handleMove);\n document.addEventListener(\"mouseup\", handleUp);\n document.addEventListener(\"touchmove\", handleMove);\n document.addEventListener(\"touchend\", handleUp);\n \n return () => {\n document.removeEventListener(\"mousemove\", handleMove);\n document.removeEventListener(\"mouseup\", handleUp);\n document.removeEventListener(\"touchmove\", handleMove);\n document.removeEventListener(\"touchend\", handleUp);\n };\n }\n }, [isDragging, handleScrub, handleScrubEnd]);\n\n const handleStepPrevious = useCallback(() => {\n if (!timegroup || steps.length === 0) return;\n \n const newIndex = Math.max(0, currentStepIndex - 1);\n const step = steps[newIndex];\n if (step !== undefined) {\n timegroup.currentTime = step / 1000;\n setCurrentStepIndex(newIndex);\n setCurrentTimeMs(step);\n }\n }, [timegroup, steps, currentStepIndex]);\n\n const handleStepNext = useCallback(() => {\n if (!timegroup || steps.length === 0) return;\n \n const newIndex = Math.min(steps.length - 1, currentStepIndex + 1);\n const step = steps[newIndex];\n if (step !== undefined) {\n timegroup.currentTime = step / 1000;\n setCurrentStepIndex(newIndex);\n setCurrentTimeMs(step);\n }\n }, [timegroup, steps, currentStepIndex]);\n\n const progress = durationMs > 0 ? (currentTimeMs / durationMs) * 100 : 0;\n const isDisabled = !timegroup;\n\n return (\n <div style={styles.container}>\n {/* Mode Toggle */}\n <div style={styles.modeToggle}>\n <button\n style={{\n ...styles.modeButton,\n ...(mode === \"auto\" ? styles.modeButtonActive : {}),\n }}\n onClick={() => onModeChange?.(\"auto\")}\n disabled={isDisabled}\n >\n Auto\n </button>\n <button\n style={{\n ...styles.modeButton,\n ...(mode === \"step\" ? styles.modeButtonActive : {}),\n }}\n onClick={() => onModeChange?.(\"step\")}\n disabled={isDisabled}\n >\n Step\n </button>\n </div>\n\n {/* Controls */}\n <div style={styles.controls}>\n {mode === \"auto\" ? (\n <>\n {/* Play/Pause */}\n <button\n style={styles.button}\n onClick={handlePlayPause}\n disabled={isDisabled}\n title={isPlaying ? \"Pause\" : \"Play\"}\n >\n {isPlaying ? \"⏸\" : \"▶\"}\n </button>\n\n {/* Loop */}\n <button\n style={{\n ...styles.button,\n ...(isLooping ? styles.buttonActive : {}),\n }}\n onClick={handleLoopToggle}\n disabled={isDisabled}\n title=\"Loop\"\n >\n 🔁\n </button>\n </>\n ) : (\n <>\n {/* Previous Step */}\n <button\n style={styles.button}\n onClick={handleStepPrevious}\n disabled={isDisabled || currentStepIndex === 0}\n title=\"Previous Step\"\n >\n ⏮\n </button>\n\n {/* Step indicator */}\n <span style={styles.stepIndicator}>\n {currentStepIndex + 1} / {steps.length}\n </span>\n\n {/* Next Step */}\n <button\n style={styles.button}\n onClick={handleStepNext}\n disabled={isDisabled || currentStepIndex === steps.length - 1}\n title=\"Next Step\"\n >\n ⏭\n </button>\n </>\n )}\n </div>\n\n {/* Scrubber */}\n <div style={styles.scrubberContainer}>\n <span style={styles.time}>{formatTime(currentTimeMs)}</span>\n <div\n ref={scrubberRef}\n style={{\n ...styles.scrubberTrack,\n ...(isDragging ? styles.scrubberTrackDragging : {}),\n }}\n onMouseDown={handleScrubStart}\n onTouchStart={handleScrubStart}\n >\n <div\n style={{\n ...styles.scrubberProgress,\n width: `${progress}%`,\n }}\n >\n <div style={styles.scrubberHandle} />\n </div>\n </div>\n <span style={styles.time}>{formatTime(durationMs)}</span>\n </div>\n </div>\n );\n}\n\nconst styles: Record<string, React.CSSProperties> = {\n container: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"8px\",\n padding: \"8px\",\n background: \"#1f2937\",\n borderTop: \"1px solid #374151\",\n },\n modeToggle: {\n display: \"flex\",\n gap: \"4px\",\n justifyContent: \"center\",\n },\n modeButton: {\n padding: \"4px 12px\",\n fontSize: \"11px\",\n background: \"#374151\",\n border: \"1px solid #4b5563\",\n borderRadius: \"4px\",\n color: \"#d1d5db\",\n cursor: \"pointer\",\n },\n modeButtonActive: {\n background: \"#3b82f6\",\n borderColor: \"#3b82f6\",\n color: \"white\",\n },\n controls: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: \"8px\",\n },\n button: {\n width: \"32px\",\n height: \"32px\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: \"transparent\",\n border: \"none\",\n color: \"#e5e7eb\",\n fontSize: \"16px\",\n cursor: \"pointer\",\n borderRadius: \"4px\",\n },\n buttonActive: {\n color: \"#22c55e\",\n },\n stepIndicator: {\n fontSize: \"12px\",\n color: \"#9ca3af\",\n minWidth: \"50px\",\n textAlign: \"center\",\n },\n scrubberContainer: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"8px\",\n },\n time: {\n fontSize: \"11px\",\n color: \"#9ca3af\",\n fontFamily: \"'SF Mono', 'Monaco', 'Consolas', monospace\",\n minWidth: \"60px\",\n },\n scrubberTrack: {\n flex: 1,\n height: \"4px\",\n background: \"rgba(255, 255, 255, 0.2)\",\n borderRadius: \"2px\",\n cursor: \"pointer\",\n position: \"relative\",\n },\n scrubberTrackDragging: {\n cursor: \"grabbing\",\n },\n scrubberProgress: {\n height: \"100%\",\n background: \"#3b82f6\",\n borderRadius: \"2px\",\n position: \"relative\",\n transition: \"width 0.05s linear\",\n },\n scrubberHandle: {\n position: \"absolute\",\n right: \"-6px\",\n top: \"50%\",\n transform: \"translateY(-50%)\",\n width: \"12px\",\n height: \"12px\",\n background: \"white\",\n borderRadius: \"50%\",\n boxShadow: \"0 2px 4px rgba(0, 0, 0, 0.3)\",\n },\n};\n"],"mappings":""}
@@ -1 +0,0 @@
1
- import "./index.js";
@@ -1 +0,0 @@
1
- import { nothing } from "lit";
@@ -1 +0,0 @@
1
- import { TemplateResult } from "lit";
@@ -1,3 +0,0 @@
1
- import "./defineSandbox.js";
2
- import "./PlaybackControls.js";
3
- import "./ScenarioRunner.js";
@@ -1,2 +0,0 @@
1
- import "./PlaybackControls.js";
2
- import "./ScenarioRunner.js";
@@ -1,80 +0,0 @@
1
- import { html, render } from "lit";
2
- import { afterEach, beforeEach, describe } from "vitest";
3
- import { test as baseTest } from "../test/useMSW.js";
4
- import "../src/elements/EFVideo.js";
5
- import type { EFVideo } from "../src/elements/EFVideo.js";
6
- import "../src/elements/EFVideo.js";
7
- import { assetMSWHandlers } from "./useAssetMSW.js";
8
- import "../src/elements/EFTimegroup.js";
9
-
10
- // Status values match the old TaskStatus enum: INITIAL=0, PENDING=1, COMPLETE=2, ERROR=3
11
- const TaskStatus = {
12
- INITIAL: 0,
13
- PENDING: 1,
14
- COMPLETE: 2,
15
- ERROR: 3,
16
- };
17
-
18
- const test = baseTest.extend({
19
- setupAssetHandlers: [
20
- async ({ worker }, use) => {
21
- // Set up centralized MSW handlers to proxy requests to test assets
22
- worker.use(...assetMSWHandlers);
23
- await use(undefined);
24
- },
25
- { auto: true },
26
- ],
27
- });
28
-
29
- describe("EFVideo Frame Generation", () => {
30
- let container: HTMLDivElement;
31
- let video: EFVideo;
32
-
33
- beforeEach(() => {
34
- // MSW setup is now handled by test fixtures
35
-
36
- // Clean up DOM
37
- container = document.createElement("div");
38
- document.body.appendChild(container);
39
-
40
- render(
41
- html`
42
- <ef-video
43
- src="media/bars-n-tone2.mp4"
44
- mode="asset"
45
- current-time-ms="80"
46
- ></ef-video>
47
- `,
48
- container,
49
- );
50
-
51
- video = document.querySelector("ef-video") as EFVideo;
52
- });
53
-
54
- afterEach(() => container.remove());
55
-
56
- test("initializes with duration of 0", async ({ expect }) => {
57
- expect(video.durationMs).toBe(0);
58
- });
59
-
60
- test("fragmentIndexTask is pending", ({ expect }) => {
61
- expect(video.fragmentIndexTask.status).toEqual(TaskStatus.INITIAL);
62
- });
63
-
64
- // Note: Timing-dependent tests disabled due to seek range issues
65
- // The test asset data starts at 80ms but component initializes at 0ms
66
- // These tests validate task completion after data loading but fail on seek timing
67
- describe.skip("after frametask settles (disabled - timing issues)", () => {
68
- beforeEach(async () => {
69
- await video.frameTask.taskComplete;
70
- });
71
-
72
- test("duration is 10000", async ({ expect }) => {
73
- expect(video.durationMs).toBeCloseTo(10_085, 0);
74
- });
75
-
76
- test("fragmentIndexTask is fulfilled", ({ expect }) => {
77
- expect(video.fragmentIndexTask.status).toEqual(TaskStatus.COMPLETE);
78
- });
79
- });
80
- });
@@ -1,116 +0,0 @@
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>