@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,731 +0,0 @@
1
- import { __decorate } from "../_virtual/_@oxc-project_runtime@0.95.0/helpers/decorate.js";
2
- import { TargetController } from "./TargetController.js";
3
- import { findRootTemporal } from "./findRootTemporal.js";
4
- import { timelineStateContext } from "../gui/timeline/timelineStateContext.js";
5
- import { getCacheKey, sessionThumbnailCache } from "./SessionThumbnailCache.js";
6
- import { consume } from "@lit/context";
7
- import { LitElement, css, html } from "lit";
8
- import { customElement, property, state } from "lit/decorators.js";
9
- import { createRef, ref } from "lit/directives/ref.js";
10
-
11
- //#region src/elements/EFThumbnailStrip.ts
12
- /** Type guard to check if element is EFVideo */
13
- function isEFVideo(element) {
14
- return element?.tagName.toLowerCase() === "ef-video";
15
- }
16
- /** Type guard to check if element is EFTimegroup */
17
- function isEFTimegroup(element) {
18
- return element?.tagName.toLowerCase() === "ef-timegroup";
19
- }
20
- /**
21
- * Get identifiers for cache key generation.
22
- * Returns rootId (for cache isolation), elementId (for element-specific caching), and epoch (for content versioning).
23
- */
24
- function getCacheIdentifiers(element) {
25
- const rootTemporal = findRootTemporal(element);
26
- const rootTimegroup = rootTemporal && isEFTimegroup(rootTemporal) ? rootTemporal : null;
27
- const rootId = rootTimegroup?.id || "default";
28
- const epoch = rootTimegroup?.contentEpoch ?? 0;
29
- return {
30
- rootId,
31
- elementId: isEFVideo(element) ? element.src || element.id || "video" : element.id || "timegroup",
32
- epoch
33
- };
34
- }
35
- /** Padding in pixels for virtual rendering (render extra thumbnails beyond viewport) */
36
- const VIRTUAL_RENDER_PADDING_PX = 200;
37
- /** Default gap between thumbnails */
38
- const DEFAULT_GAP = 4;
39
- /** Default aspect ratio if unknown */
40
- const DEFAULT_ASPECT_RATIO = 16 / 9;
41
- /** Max canvas width for thumbnail captures */
42
- const MAX_CAPTURE_WIDTH = 480;
43
- /** Thumbnails to capture per batch */
44
- const BATCH_SIZE = 10;
45
- let EFThumbnailStrip = class EFThumbnailStrip$1 extends LitElement {
46
- constructor(..._args) {
47
- super(..._args);
48
- this.canvasRef = createRef();
49
- this.target = "";
50
- this.thumbnailWidth = 0;
51
- this.gap = DEFAULT_GAP;
52
- this.useIntrinsicDuration = false;
53
- this.pixelsPerMs = .1;
54
- this._targetController = new TargetController(this);
55
- this._targetElement = null;
56
- this._width = 0;
57
- this._height = 0;
58
- this._scrollContainer = null;
59
- this._currentScrollLeft = 0;
60
- this._trackLeftOffset = 0;
61
- this._thumbnailSlots = [];
62
- this._captureInProgress = false;
63
- this._renderRequested = false;
64
- this._hasLoadedThumbnails = false;
65
- this._lastLoadedEpoch = null;
66
- this._lastLayoutParams = null;
67
- this._onScroll = () => {
68
- if (!this._scrollContainer) return;
69
- this._currentScrollLeft = this._scrollContainer.scrollLeft;
70
- this._drawCanvas();
71
- if (!this._scrollFrame) this._scrollFrame = requestAnimationFrame(() => {
72
- this._scrollFrame = void 0;
73
- this._loadVisibleThumbnails();
74
- });
75
- };
76
- }
77
- static {
78
- this.styles = [css`
79
- :host {
80
- display: block;
81
- position: relative;
82
- background: #1a1a2e;
83
- overflow: hidden;
84
- width: 100%;
85
- height: 100%;
86
- }
87
- canvas {
88
- display: block;
89
- /* Absolute positioning - we manually position at visible region */
90
- position: absolute;
91
- top: 0;
92
- /* Left and width set programmatically based on visible portion */
93
- height: 100%;
94
- image-rendering: auto;
95
- }
96
- `];
97
- }
98
- get targetElement() {
99
- return this._targetElement;
100
- }
101
- set targetElement(value) {
102
- const oldValue = this._targetElement;
103
- this._targetElement = value;
104
- this._mutationObserver?.disconnect();
105
- if (value !== oldValue) {
106
- this._hasLoadedThumbnails = false;
107
- this._lastLoadedEpoch = null;
108
- this._lastLayoutParams = null;
109
- }
110
- if (value && value !== oldValue) this._setupTargetObserver(value);
111
- this.requestUpdate("targetElement", oldValue);
112
- }
113
- connectedCallback() {
114
- super.connectedCallback();
115
- this._resizeObserver = new ResizeObserver((entries) => {
116
- for (const entry of entries) {
117
- const box = entry.borderBoxSize?.[0];
118
- this._width = box?.inlineSize ?? entry.contentRect.width;
119
- this._height = box?.blockSize ?? entry.contentRect.height;
120
- this._calculateTrackOffset();
121
- this._scheduleRender();
122
- }
123
- });
124
- this._resizeObserver.observe(this);
125
- this.updateComplete.then(() => {
126
- this._findScrollContainer();
127
- this._scheduleRender();
128
- });
129
- }
130
- disconnectedCallback() {
131
- super.disconnectedCallback();
132
- this._resizeObserver?.disconnect();
133
- this._mutationObserver?.disconnect();
134
- this._detachScrollListener();
135
- if (this._scrollFrame) cancelAnimationFrame(this._scrollFrame);
136
- }
137
- updated(changedProperties) {
138
- super.updated(changedProperties);
139
- if (changedProperties.has("thumbnailWidth") || changedProperties.has("gap") || changedProperties.has("startTimeMs") || changedProperties.has("endTimeMs") || changedProperties.has("useIntrinsicDuration") || changedProperties.has("pixelsPerMs") || changedProperties.has("targetElement")) this._scheduleRender();
140
- if (changedProperties.has("_timelineState")) this._onContextScroll();
141
- }
142
- _findScrollContainer() {
143
- let node = this.parentNode;
144
- while (node) {
145
- if (node instanceof HTMLElement) {
146
- const style = getComputedStyle(node);
147
- if (style.overflowX === "auto" || style.overflowX === "scroll") {
148
- this._scrollContainer = node;
149
- this._calculateTrackOffset();
150
- this._attachScrollListener();
151
- return;
152
- }
153
- }
154
- if (node.parentNode) node = node.parentNode;
155
- else if (node instanceof ShadowRoot) node = node.host;
156
- else break;
157
- }
158
- }
159
- /**
160
- * Calculate the horizontal offset from scroll container's left edge to this element's track.
161
- * This accounts for sticky labels or other elements that precede the track area.
162
- *
163
- * We look for our specific timeline elements (ef-timeline-row) and measure their label width.
164
- */
165
- _calculateTrackOffset() {
166
- if (!this._scrollContainer) {
167
- this._trackLeftOffset = 0;
168
- return;
169
- }
170
- const timelineRow = this._findTimelineRow();
171
- if (timelineRow) {
172
- const labelWidth = this._getTimelineRowLabelWidth(timelineRow);
173
- if (labelWidth > 0) {
174
- this._trackLeftOffset = labelWidth;
175
- return;
176
- }
177
- }
178
- this._trackLeftOffset = 0;
179
- }
180
- /**
181
- * Find the ef-timeline-row ancestor by walking up through shadow DOM boundaries.
182
- */
183
- _findTimelineRow() {
184
- let node = this;
185
- while (node) {
186
- if (node instanceof Element && node.tagName.toLowerCase() === "ef-timeline-row") return node;
187
- const parentNode = node.parentNode;
188
- if (parentNode instanceof ShadowRoot) node = parentNode.host;
189
- else node = parentNode;
190
- }
191
- return null;
192
- }
193
- /**
194
- * Get the label width from an ef-timeline-row element.
195
- * Queries the shadow root for .row-label and returns its width.
196
- */
197
- _getTimelineRowLabelWidth(timelineRow) {
198
- const shadowRoot = timelineRow.shadowRoot;
199
- if (!shadowRoot) return 0;
200
- const rowLabel = shadowRoot.querySelector(".row-label");
201
- if (!rowLabel) return 0;
202
- return rowLabel.getBoundingClientRect().width;
203
- }
204
- /**
205
- * Get this strip's absolute position in the timeline (pixels from timeline origin).
206
- * Uses the target element's startTimeMs to determine position.
207
- */
208
- _getStripTimelinePosition() {
209
- const target = this._targetElement;
210
- if (!target) return 0;
211
- if (isEFVideo(target)) return (target.startTimeMs ?? 0) * (this._timelineState?.pixelsPerMs ?? .1);
212
- return 0;
213
- }
214
- _attachScrollListener() {
215
- if (!this._scrollContainer) return;
216
- this._scrollContainer.addEventListener("scroll", this._onScroll, { passive: true });
217
- this._currentScrollLeft = this._scrollContainer.scrollLeft;
218
- }
219
- _detachScrollListener() {
220
- if (this._scrollContainer) {
221
- this._scrollContainer.removeEventListener("scroll", this._onScroll);
222
- this._scrollContainer = null;
223
- }
224
- }
225
- _onContextScroll() {
226
- if (!this._timelineState || this._scrollContainer) return;
227
- this._currentScrollLeft = this._timelineState.viewportScrollLeft;
228
- this._drawCanvas();
229
- }
230
- get _viewportWidth() {
231
- if (this._timelineState?.viewportWidth) return this._timelineState.viewportWidth;
232
- if (this._scrollContainer) return this._scrollContainer.clientWidth - this._trackLeftOffset;
233
- return this._width;
234
- }
235
- /**
236
- * Watch for async content loading from child media elements.
237
- * When media finishes loading, increment the epoch to invalidate cached thumbnails.
238
- */
239
- _watchChildContentLoading(target) {
240
- const mediaElements = target.querySelectorAll("ef-video, ef-image, ef-audio");
241
- for (const el of mediaElements) {
242
- const mediaEngine = el.mediaEngineTask;
243
- if (mediaEngine?.taskComplete) mediaEngine.taskComplete.then(() => {
244
- if (this._targetElement === target) {
245
- target.incrementContentEpoch();
246
- this._lastLayoutParams = null;
247
- this._scheduleRender();
248
- }
249
- }).catch(() => {});
250
- const fetchTask = el.fetchImage;
251
- if (fetchTask?.taskComplete) fetchTask.taskComplete.then(() => {
252
- if (this._targetElement === target) {
253
- target.incrementContentEpoch();
254
- this._lastLayoutParams = null;
255
- this._scheduleRender();
256
- }
257
- }).catch(() => {});
258
- }
259
- }
260
- _setupTargetObserver(target) {
261
- if (isEFVideo(target)) {
262
- this._mutationObserver = new MutationObserver(() => this._scheduleRender());
263
- this._mutationObserver.observe(target, {
264
- attributes: true,
265
- attributeFilter: [
266
- "trimstart",
267
- "trimend",
268
- "sourcein",
269
- "sourceout",
270
- "src"
271
- ]
272
- });
273
- target.updateComplete.then(() => {
274
- if (this._targetElement !== target) return;
275
- target.mediaEngineTask?.taskComplete.then(() => {
276
- if (this._targetElement !== target) return;
277
- this._scheduleRender();
278
- });
279
- });
280
- } else if (isEFTimegroup(target)) {
281
- this._mutationObserver = new MutationObserver((mutations) => {
282
- if (mutations.some((mutation) => {
283
- if (mutation.type === "childList") return mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0;
284
- if (mutation.type === "attributes") {
285
- const attrName = mutation.attributeName;
286
- if (attrName === "currenttime" || attrName === "current-time" || attrName === "playing" || attrName === "loop") return false;
287
- return attrName === "src" || attrName === "asset-id" || attrName === "style" || attrName === "transform";
288
- }
289
- return false;
290
- })) {
291
- const epochBefore = target.contentEpoch;
292
- target.incrementContentEpoch();
293
- if (target.contentEpoch !== epochBefore) {
294
- this._lastLayoutParams = null;
295
- if (mutations.some((m) => m.addedNodes.length > 0)) this._watchChildContentLoading(target);
296
- this._scheduleRender();
297
- }
298
- }
299
- });
300
- this._mutationObserver.observe(target, {
301
- childList: true,
302
- subtree: true,
303
- attributes: true,
304
- attributeFilter: [
305
- "src",
306
- "asset-id",
307
- "style",
308
- "transform"
309
- ]
310
- });
311
- this._watchChildContentLoading(target);
312
- if (target.durationMs === 0) {
313
- const checkDuration = () => {
314
- if (this._targetElement !== target) return;
315
- if (target.durationMs > 0) this._scheduleRender();
316
- else requestAnimationFrame(checkDuration);
317
- };
318
- requestAnimationFrame(checkDuration);
319
- }
320
- }
321
- }
322
- _scheduleRender() {
323
- if (this._renderRequested) return;
324
- this._renderRequested = true;
325
- requestAnimationFrame(() => {
326
- this._renderRequested = false;
327
- this._calculateLayout();
328
- this._checkCache();
329
- this._drawCanvas();
330
- this._loadVisibleThumbnails();
331
- this._checkAndDispatchReady();
332
- });
333
- }
334
- /**
335
- * Check if thumbnails are ready and dispatch event if not already done.
336
- */
337
- _checkAndDispatchReady() {
338
- if (this._hasLoadedThumbnails) return;
339
- const hasLayout = this._thumbnailSlots.length > 0;
340
- const hasAnyCached = this._thumbnailSlots.some((s) => s.status === "cached");
341
- const hasPending = this._thumbnailSlots.some((s) => s.status === "pending");
342
- if (hasLayout && (hasAnyCached || !hasPending)) {
343
- this._hasLoadedThumbnails = true;
344
- this.dispatchEvent(new CustomEvent("thumbnails-ready", { bubbles: true }));
345
- }
346
- }
347
- /**
348
- * Calculate thumbnail layout based on current dimensions and time range.
349
- * Only recreates slots if layout parameters have actually changed.
350
- */
351
- _calculateLayout() {
352
- if (this._width <= 0 || this._height <= 0 || !this._targetElement) {
353
- this._thumbnailSlots = [];
354
- this._lastLayoutParams = null;
355
- return;
356
- }
357
- const timeRange = this._getTimeRange();
358
- if (timeRange.endMs <= timeRange.startMs) {
359
- this._thumbnailSlots = [];
360
- this._lastLayoutParams = null;
361
- return;
362
- }
363
- const thumbWidth = this._getEffectiveThumbnailWidth();
364
- const gap = this.gap;
365
- const currentParams = {
366
- width: this._width,
367
- height: this._height,
368
- startTimeMs: timeRange.startMs,
369
- endTimeMs: timeRange.endMs,
370
- thumbWidth,
371
- gap
372
- };
373
- if (this._lastLayoutParams && this._lastLayoutParams.width === currentParams.width && this._lastLayoutParams.height === currentParams.height && this._lastLayoutParams.startTimeMs === currentParams.startTimeMs && this._lastLayoutParams.endTimeMs === currentParams.endTimeMs && this._lastLayoutParams.thumbWidth === currentParams.thumbWidth && this._lastLayoutParams.gap === currentParams.gap) return;
374
- this._lastLayoutParams = currentParams;
375
- const count = Math.max(1, Math.floor((this._width + gap) / (thumbWidth + gap)));
376
- const pitch = count > 1 ? (this._width - thumbWidth) / (count - 1) : 0;
377
- const slots = [];
378
- const duration = timeRange.endMs - timeRange.startMs;
379
- for (let i = 0; i < count; i++) {
380
- const timeMs = count === 1 ? (timeRange.startMs + timeRange.endMs) / 2 : timeRange.startMs + i * duration / (count - 1);
381
- slots.push({
382
- timeMs,
383
- x: Math.round(i * pitch),
384
- width: thumbWidth,
385
- status: "pending"
386
- });
387
- }
388
- this._thumbnailSlots = slots;
389
- }
390
- /**
391
- * Get effective time range for thumbnails.
392
- */
393
- _getTimeRange() {
394
- const target = this._targetElement;
395
- if (!target) return {
396
- startMs: 0,
397
- endMs: 0
398
- };
399
- if (isEFVideo(target)) {
400
- if (this.useIntrinsicDuration) return {
401
- startMs: this.startTimeMs ?? 0,
402
- endMs: this.endTimeMs ?? target.intrinsicDurationMs ?? 0
403
- };
404
- const sourceStart = target.sourceStartMs ?? 0;
405
- const trimmedDuration = target.durationMs ?? 0;
406
- return {
407
- startMs: this.startTimeMs !== void 0 ? sourceStart + this.startTimeMs : sourceStart,
408
- endMs: this.endTimeMs !== void 0 ? sourceStart + this.endTimeMs : sourceStart + trimmedDuration
409
- };
410
- }
411
- return {
412
- startMs: this.startTimeMs ?? 0,
413
- endMs: this.endTimeMs && this.endTimeMs > 0 ? this.endTimeMs : target.durationMs ?? 0
414
- };
415
- }
416
- /**
417
- * Calculate effective thumbnail width (auto or specified).
418
- */
419
- _getEffectiveThumbnailWidth() {
420
- if (this.thumbnailWidth > 0) return this.thumbnailWidth;
421
- const target = this._targetElement;
422
- let aspectRatio = DEFAULT_ASPECT_RATIO;
423
- if (isEFVideo(target)) aspectRatio = (target.videoWidth || 1920) / (target.videoHeight || 1080);
424
- else if (isEFTimegroup(target)) aspectRatio = (target.offsetWidth || 1920) / (target.offsetHeight || 1080);
425
- return Math.round(this._height * aspectRatio);
426
- }
427
- /**
428
- * Check cache for existing thumbnails.
429
- */
430
- _checkCache() {
431
- if (!this._targetElement) return;
432
- const { rootId, elementId, epoch } = getCacheIdentifiers(this._targetElement);
433
- for (const slot of this._thumbnailSlots) {
434
- const key = getCacheKey(rootId, elementId, slot.timeMs, epoch);
435
- if (sessionThumbnailCache.has(key)) {
436
- slot.imageData = sessionThumbnailCache.get(key);
437
- slot.status = "cached";
438
- } else slot.status = "pending";
439
- }
440
- }
441
- /**
442
- * Draw the canvas with current thumbnail state.
443
- * Canvas is absolutely positioned at the visible portion of the strip.
444
- * Uses virtual rendering - only draws thumbnails in the visible region.
445
- */
446
- _drawCanvas() {
447
- const canvas = this.canvasRef.value;
448
- if (!canvas) return;
449
- const ctx = canvas.getContext("2d", { willReadFrequently: true });
450
- if (!ctx) return;
451
- const stripWidth = this._width;
452
- const height = this._height;
453
- if (stripWidth <= 0 || height <= 0) return;
454
- const dpr = window.devicePixelRatio || 1;
455
- const scrollLeft = this._currentScrollLeft;
456
- const viewportWidth = this._viewportWidth;
457
- const stripStartPx = this._getStripTimelinePosition();
458
- const stripEndPx = stripStartPx + stripWidth;
459
- const visibleLeftPx = scrollLeft - VIRTUAL_RENDER_PADDING_PX;
460
- const visibleRightPx = scrollLeft + viewportWidth + VIRTUAL_RENDER_PADDING_PX;
461
- if (stripEndPx < visibleLeftPx || stripStartPx > visibleRightPx) {
462
- canvas.style.display = "none";
463
- return;
464
- }
465
- canvas.style.display = "block";
466
- const visibleStartInStrip = Math.max(0, visibleLeftPx - stripStartPx);
467
- const visibleEndInStrip = Math.min(stripWidth, visibleRightPx - stripStartPx);
468
- const visibleWidthPx = visibleEndInStrip - visibleStartInStrip;
469
- if (visibleWidthPx <= 0) {
470
- canvas.style.display = "none";
471
- return;
472
- }
473
- const targetWidth = Math.ceil(visibleWidthPx * dpr);
474
- const targetHeight = Math.ceil(height * dpr);
475
- if (canvas.width !== targetWidth || canvas.height !== targetHeight) {
476
- canvas.width = targetWidth;
477
- canvas.height = targetHeight;
478
- }
479
- canvas.style.left = `${visibleStartInStrip}px`;
480
- canvas.style.width = `${visibleWidthPx}px`;
481
- canvas.style.height = `${height}px`;
482
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
483
- ctx.fillStyle = "#1a1a2e";
484
- ctx.fillRect(0, 0, visibleWidthPx, height);
485
- for (const slot of this._thumbnailSlots) {
486
- if (slot.x + slot.width < visibleStartInStrip || slot.x > visibleEndInStrip) continue;
487
- const drawX = slot.x - visibleStartInStrip;
488
- if (drawX + slot.width < 0 || drawX > visibleWidthPx) continue;
489
- if (slot.imageData) this._drawThumbnailImage(ctx, slot.imageData, drawX, slot.width, height);
490
- else {
491
- ctx.fillStyle = slot.status === "loading" ? "#2d2d50" : "#2d2d44";
492
- ctx.fillRect(drawX, 0, slot.width, height);
493
- if (slot.status === "loading") {
494
- ctx.fillStyle = "rgba(59, 130, 246, 0.3)";
495
- ctx.fillRect(drawX, 0, slot.width, 2);
496
- }
497
- }
498
- }
499
- }
500
- /**
501
- * Draw a thumbnail image with cover mode scaling.
502
- */
503
- _drawThumbnailImage(ctx, imageData, x, width, height) {
504
- const tempCanvas = document.createElement("canvas");
505
- tempCanvas.width = imageData.width;
506
- tempCanvas.height = imageData.height;
507
- const tempCtx = tempCanvas.getContext("2d");
508
- if (!tempCtx) return;
509
- tempCtx.putImageData(imageData, 0, 0);
510
- const srcAspect = imageData.width / imageData.height;
511
- const dstAspect = width / height;
512
- let srcX = 0, srcY = 0, srcW = imageData.width, srcH = imageData.height;
513
- if (srcAspect > dstAspect) {
514
- srcW = imageData.height * dstAspect;
515
- srcX = (imageData.width - srcW) / 2;
516
- } else {
517
- srcH = imageData.width / dstAspect;
518
- srcY = (imageData.height - srcH) / 2;
519
- }
520
- ctx.drawImage(tempCanvas, srcX, srcY, srcW, srcH, x, 0, width, height);
521
- }
522
- /**
523
- * Load thumbnails that are visible in the current viewport.
524
- * Skips loading if the epoch hasn't changed since last load.
525
- */
526
- async _loadVisibleThumbnails() {
527
- if (this._captureInProgress || !this._targetElement) return;
528
- if (isEFTimegroup(this._targetElement)) {
529
- const currentEpoch = this._targetElement.contentEpoch;
530
- if (this._lastLoadedEpoch !== null && this._lastLoadedEpoch === currentEpoch) {
531
- if (!this._thumbnailSlots.some((s) => s.status === "pending")) return;
532
- }
533
- }
534
- const viewportWidth = this._viewportWidth;
535
- const scrollOffset = this._currentScrollLeft;
536
- const stripWidth = this._width;
537
- const stripStartPx = this._getStripTimelinePosition();
538
- const visibleLeftPx = scrollOffset - VIRTUAL_RENDER_PADDING_PX;
539
- const visibleRightPx = scrollOffset + viewportWidth + VIRTUAL_RENDER_PADDING_PX;
540
- const visibleStartInStrip = Math.max(0, visibleLeftPx - stripStartPx);
541
- const visibleEndInStrip = Math.min(stripWidth, visibleRightPx - stripStartPx);
542
- const pending = this._thumbnailSlots.filter((slot) => {
543
- if (slot.status !== "pending") return false;
544
- return slot.x + slot.width >= visibleStartInStrip && slot.x <= visibleEndInStrip;
545
- });
546
- if (pending.length === 0) return;
547
- this._captureInProgress = true;
548
- for (const slot of pending) slot.status = "loading";
549
- this._drawCanvas();
550
- try {
551
- if (isEFTimegroup(this._targetElement)) await this._captureTimegroupThumbnails(pending);
552
- else if (isEFVideo(this._targetElement)) await this._captureVideoThumbnails(pending);
553
- } catch (error) {
554
- console.warn("Failed to capture thumbnails:", error);
555
- for (const slot of pending) if (slot.status === "loading") slot.status = "pending";
556
- } finally {
557
- this._captureInProgress = false;
558
- this._drawCanvas();
559
- if (isEFTimegroup(this._targetElement)) this._lastLoadedEpoch = this._targetElement.contentEpoch;
560
- if (this._thumbnailSlots.some((s) => s.status === "cached") && !this._hasLoadedThumbnails) {
561
- this._hasLoadedThumbnails = true;
562
- this.dispatchEvent(new CustomEvent("thumbnails-ready", { bubbles: true }));
563
- }
564
- }
565
- }
566
- /**
567
- * Capture thumbnails from a timegroup target.
568
- */
569
- async _captureTimegroupThumbnails(slots) {
570
- const target = this._targetElement;
571
- const { rootId, elementId, epoch } = getCacheIdentifiers(target);
572
- const timegroupWidth = target.offsetWidth || 1920;
573
- const timegroupHeight = target.offsetHeight || 1080;
574
- const scale = Math.min(1, this._height / timegroupHeight, MAX_CAPTURE_WIDTH / timegroupWidth);
575
- for (let i = 0; i < slots.length; i += BATCH_SIZE) {
576
- const batch = slots.slice(i, i + BATCH_SIZE);
577
- const timestamps = batch.map((s) => s.timeMs);
578
- try {
579
- const canvases = await target.captureBatch(timestamps, {
580
- scale,
581
- contentReadyMode: "immediate"
582
- });
583
- for (let j = 0; j < batch.length; j++) {
584
- const slot = batch[j];
585
- const canvas = canvases[j];
586
- if (canvas) {
587
- const imageData = this._canvasToImageData(canvas);
588
- if (imageData) {
589
- const key = getCacheKey(rootId, elementId, slot.timeMs, epoch);
590
- sessionThumbnailCache.set(key, imageData, slot.timeMs, elementId);
591
- slot.imageData = imageData;
592
- slot.status = "cached";
593
- }
594
- }
595
- }
596
- this._drawCanvas();
597
- if (i + BATCH_SIZE < slots.length) await new Promise((r) => requestAnimationFrame(r));
598
- } catch (error) {
599
- console.warn("Batch capture failed:", error);
600
- }
601
- }
602
- }
603
- /**
604
- * Capture thumbnails from a video target using MediaEngine.
605
- */
606
- async _captureVideoThumbnails(slots) {
607
- const target = this._targetElement;
608
- const { rootId, elementId, epoch } = getCacheIdentifiers(target);
609
- if (target.mediaEngineTask) await target.mediaEngineTask.taskComplete;
610
- const mediaEngine = target.mediaEngineTask?.value;
611
- if (!mediaEngine) return;
612
- const videoRendition = mediaEngine.getVideoRendition();
613
- const scrubRendition = mediaEngine.getScrubVideoRendition();
614
- if (!videoRendition && !scrubRendition) return;
615
- const timestamps = slots.map((s) => s.timeMs);
616
- const abortController = new AbortController();
617
- try {
618
- const results = await mediaEngine.extractThumbnails(timestamps, abortController.signal);
619
- for (let i = 0; i < slots.length; i++) {
620
- const slot = slots[i];
621
- const result = results[i];
622
- if (result?.thumbnail) {
623
- const imageData = this._canvasToImageData(result.thumbnail);
624
- if (imageData) {
625
- const key = getCacheKey(rootId, elementId, slot.timeMs, epoch);
626
- sessionThumbnailCache.set(key, imageData, slot.timeMs, elementId);
627
- slot.imageData = imageData;
628
- slot.status = "cached";
629
- }
630
- }
631
- }
632
- } catch (error) {
633
- abortController.abort();
634
- console.warn("Video thumbnail extraction failed:", error);
635
- }
636
- }
637
- /**
638
- * Convert canvas to ImageData.
639
- */
640
- _canvasToImageData(canvas) {
641
- const ctx = canvas.getContext("2d", { willReadFrequently: true });
642
- if (!ctx) return null;
643
- return ctx.getImageData(0, 0, canvas.width, canvas.height);
644
- }
645
- /**
646
- * Returns a promise that resolves when thumbnails are ready.
647
- * Resolves immediately if thumbnails are already loaded.
648
- */
649
- whenReady() {
650
- if (this._hasLoadedThumbnails) return Promise.resolve();
651
- return new Promise((resolve) => {
652
- this.addEventListener("thumbnails-ready", () => resolve(), { once: true });
653
- });
654
- }
655
- /**
656
- * Check if thumbnails have been loaded.
657
- */
658
- get isReady() {
659
- return this._hasLoadedThumbnails;
660
- }
661
- /**
662
- * Invalidate cached thumbnails for this element within a time range.
663
- * Call this when content changes at specific times.
664
- */
665
- invalidateTimeRange(startTimeMs, endTimeMs) {
666
- if (!this._targetElement) return;
667
- const { rootId, elementId } = getCacheIdentifiers(this._targetElement);
668
- sessionThumbnailCache.invalidateTimeRange(rootId, elementId, startTimeMs, endTimeMs);
669
- for (const slot of this._thumbnailSlots) if (slot.timeMs >= startTimeMs && slot.timeMs <= endTimeMs) {
670
- slot.imageData = void 0;
671
- slot.status = "pending";
672
- }
673
- this._scheduleRender();
674
- }
675
- /**
676
- * Invalidate all cached thumbnails for this element.
677
- */
678
- invalidateAll() {
679
- if (!this._targetElement) return;
680
- const { rootId, elementId } = getCacheIdentifiers(this._targetElement);
681
- sessionThumbnailCache.invalidateElement(rootId, elementId);
682
- for (const slot of this._thumbnailSlots) {
683
- slot.imageData = void 0;
684
- slot.status = "pending";
685
- }
686
- this._scheduleRender();
687
- }
688
- render() {
689
- return html`<canvas ${ref(this.canvasRef)}></canvas>`;
690
- }
691
- };
692
- __decorate([property({ type: String })], EFThumbnailStrip.prototype, "target", void 0);
693
- __decorate([property({
694
- type: Number,
695
- attribute: "thumbnail-width"
696
- })], EFThumbnailStrip.prototype, "thumbnailWidth", void 0);
697
- __decorate([property({
698
- type: Number,
699
- attribute: "gap"
700
- })], EFThumbnailStrip.prototype, "gap", void 0);
701
- __decorate([property({
702
- type: Number,
703
- attribute: "start-time-ms"
704
- })], EFThumbnailStrip.prototype, "startTimeMs", void 0);
705
- __decorate([property({
706
- type: Number,
707
- attribute: "end-time-ms"
708
- })], EFThumbnailStrip.prototype, "endTimeMs", void 0);
709
- __decorate([property({
710
- type: Boolean,
711
- attribute: "use-intrinsic-duration",
712
- reflect: true,
713
- converter: {
714
- fromAttribute: (value) => value === "true",
715
- toAttribute: (value) => value ? "true" : null
716
- }
717
- })], EFThumbnailStrip.prototype, "useIntrinsicDuration", void 0);
718
- __decorate([property({
719
- type: Number,
720
- attribute: "pixels-per-ms"
721
- })], EFThumbnailStrip.prototype, "pixelsPerMs", void 0);
722
- __decorate([consume({
723
- context: timelineStateContext,
724
- subscribe: true
725
- }), state()], EFThumbnailStrip.prototype, "_timelineState", void 0);
726
- __decorate([state()], EFThumbnailStrip.prototype, "targetElement", null);
727
- EFThumbnailStrip = __decorate([customElement("ef-thumbnail-strip")], EFThumbnailStrip);
728
-
729
- //#endregion
730
- export { EFThumbnailStrip };
731
- //# sourceMappingURL=EFThumbnailStrip.js.map