@editframe/elements 0.30.1-beta.0 → 0.31.0-beta.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 (325) 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 +118 -56
  117. package/dist/elements/EFThumbnailStrip.js +522 -358
  118. package/dist/elements/EFThumbnailStrip.js.map +1 -1
  119. package/dist/elements/EFTimegroup.d.ts +223 -27
  120. package/dist/elements/EFTimegroup.js +851 -148
  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 +152 -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 +492 -109
  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 +11 -5
  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/EFPreview.d.ts +4 -4
  175. package/dist/gui/EFResizableBox.d.ts +12 -16
  176. package/dist/gui/EFResizableBox.js +109 -451
  177. package/dist/gui/EFResizableBox.js.map +1 -1
  178. package/dist/gui/EFScrubber.d.ts +30 -5
  179. package/dist/gui/EFScrubber.js +224 -31
  180. package/dist/gui/EFScrubber.js.map +1 -1
  181. package/dist/gui/EFTimeDisplay.d.ts +4 -4
  182. package/dist/gui/EFTimeDisplay.js +4 -1
  183. package/dist/gui/EFTimeDisplay.js.map +1 -1
  184. package/dist/gui/EFTimelineRuler.d.ts +71 -0
  185. package/dist/gui/EFTimelineRuler.js +320 -0
  186. package/dist/gui/EFTimelineRuler.js.map +1 -0
  187. package/dist/gui/EFToggleLoop.d.ts +4 -4
  188. package/dist/gui/EFTogglePlay.d.ts +4 -4
  189. package/dist/gui/EFTransformHandles.d.ts +91 -0
  190. package/dist/gui/EFTransformHandles.js +393 -0
  191. package/dist/gui/EFTransformHandles.js.map +1 -0
  192. package/dist/gui/EFWorkbench.d.ts +182 -4
  193. package/dist/gui/EFWorkbench.js +2067 -22
  194. package/dist/gui/EFWorkbench.js.map +1 -1
  195. package/dist/gui/FitScaleHelpers.d.ts +31 -0
  196. package/dist/gui/FitScaleHelpers.js +41 -0
  197. package/dist/gui/FitScaleHelpers.js.map +1 -0
  198. package/dist/gui/PlaybackController.d.ts +2 -1
  199. package/dist/gui/PlaybackController.js +46 -15
  200. package/dist/gui/PlaybackController.js.map +1 -1
  201. package/dist/gui/TWMixin.js +1 -1
  202. package/dist/gui/TWMixin.js.map +1 -1
  203. package/dist/gui/hierarchy/EFHierarchy.d.ts +65 -0
  204. package/dist/gui/hierarchy/EFHierarchy.js +338 -0
  205. package/dist/gui/hierarchy/EFHierarchy.js.map +1 -0
  206. package/dist/gui/hierarchy/EFHierarchyItem.d.ts +118 -0
  207. package/dist/gui/hierarchy/EFHierarchyItem.js +551 -0
  208. package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -0
  209. package/dist/gui/hierarchy/hierarchyContext.d.ts +38 -0
  210. package/dist/gui/hierarchy/hierarchyContext.js +8 -0
  211. package/dist/gui/hierarchy/hierarchyContext.js.map +1 -0
  212. package/dist/gui/icons.js +34 -0
  213. package/dist/gui/icons.js.map +1 -0
  214. package/dist/gui/panZoomTransformContext.js +12 -0
  215. package/dist/gui/panZoomTransformContext.js.map +1 -0
  216. package/dist/gui/previewSettingsContext.js +12 -0
  217. package/dist/gui/previewSettingsContext.js.map +1 -0
  218. package/dist/gui/timeline/EFTimeline.d.ts +270 -0
  219. package/dist/gui/timeline/EFTimeline.js +1369 -0
  220. package/dist/gui/timeline/EFTimeline.js.map +1 -0
  221. package/dist/gui/timeline/EFTimelineRow.js +374 -0
  222. package/dist/gui/timeline/EFTimelineRow.js.map +1 -0
  223. package/dist/gui/timeline/TrimHandles.d.ts +36 -0
  224. package/dist/gui/timeline/TrimHandles.js +204 -0
  225. package/dist/gui/timeline/TrimHandles.js.map +1 -0
  226. package/dist/gui/timeline/flattenHierarchy.js +31 -0
  227. package/dist/gui/timeline/flattenHierarchy.js.map +1 -0
  228. package/dist/gui/timeline/timelineStateContext.d.ts +26 -0
  229. package/dist/gui/timeline/timelineStateContext.js +42 -0
  230. package/dist/gui/timeline/timelineStateContext.js.map +1 -0
  231. package/dist/gui/timeline/tracks/AudioTrack.js +264 -0
  232. package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -0
  233. package/dist/gui/timeline/tracks/CaptionsTrack.js +595 -0
  234. package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -0
  235. package/dist/gui/timeline/tracks/HTMLTrack.js +19 -0
  236. package/dist/gui/timeline/tracks/HTMLTrack.js.map +1 -0
  237. package/dist/gui/timeline/tracks/ImageTrack.js +53 -0
  238. package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -0
  239. package/dist/gui/timeline/tracks/TextTrack.js +250 -0
  240. package/dist/gui/timeline/tracks/TextTrack.js.map +1 -0
  241. package/dist/gui/timeline/tracks/TimegroupTrack.js +143 -0
  242. package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -0
  243. package/dist/gui/timeline/tracks/TrackItem.js +269 -0
  244. package/dist/gui/timeline/tracks/TrackItem.js.map +1 -0
  245. package/dist/gui/timeline/tracks/VideoTrack.js +265 -0
  246. package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -0
  247. package/dist/gui/timeline/tracks/WaveformTrack.js +19 -0
  248. package/dist/gui/timeline/tracks/WaveformTrack.js.map +1 -0
  249. package/dist/gui/timeline/tracks/ensureTrackItemInit.js +1 -0
  250. package/dist/gui/timeline/tracks/preloadTracks.js +9 -0
  251. package/dist/gui/timeline/tracks/renderTrackChildren.js +119 -0
  252. package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -0
  253. package/dist/gui/timeline/tracks/waveformUtils.js +80 -0
  254. package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -0
  255. package/dist/gui/transformCalculations.js +217 -0
  256. package/dist/gui/transformCalculations.js.map +1 -0
  257. package/dist/gui/transformUtils.d.ts +37 -0
  258. package/dist/gui/transformUtils.js +77 -0
  259. package/dist/gui/transformUtils.js.map +1 -0
  260. package/dist/gui/tree/EFTree.d.ts +59 -0
  261. package/dist/gui/tree/EFTree.js +174 -0
  262. package/dist/gui/tree/EFTree.js.map +1 -0
  263. package/dist/gui/tree/EFTreeItem.d.ts +38 -0
  264. package/dist/gui/tree/EFTreeItem.js +146 -0
  265. package/dist/gui/tree/EFTreeItem.js.map +1 -0
  266. package/dist/gui/tree/treeContext.d.ts +60 -0
  267. package/dist/gui/tree/treeContext.js +23 -0
  268. package/dist/gui/tree/treeContext.js.map +1 -0
  269. package/dist/index.d.ts +32 -8
  270. package/dist/index.js +30 -6
  271. package/dist/index.js.map +1 -1
  272. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +688 -0
  273. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +1 -0
  274. package/dist/node_modules/react/cjs/react.development.js +1521 -0
  275. package/dist/node_modules/react/cjs/react.development.js.map +1 -0
  276. package/dist/node_modules/react/index.js +13 -0
  277. package/dist/node_modules/react/index.js.map +1 -0
  278. package/dist/node_modules/react/jsx-runtime.js +13 -0
  279. package/dist/node_modules/react/jsx-runtime.js.map +1 -0
  280. package/dist/preview/AdaptiveResolutionTracker.js +228 -0
  281. package/dist/preview/AdaptiveResolutionTracker.js.map +1 -0
  282. package/dist/preview/RenderProfiler.js +135 -0
  283. package/dist/preview/RenderProfiler.js.map +1 -0
  284. package/dist/preview/previewSettings.js +131 -0
  285. package/dist/preview/previewSettings.js.map +1 -0
  286. package/dist/preview/previewTypes.js +64 -0
  287. package/dist/preview/previewTypes.js.map +1 -0
  288. package/dist/preview/renderTimegroupPreview.js +656 -0
  289. package/dist/preview/renderTimegroupPreview.js.map +1 -0
  290. package/dist/preview/renderTimegroupToCanvas.d.ts +37 -0
  291. package/dist/preview/renderTimegroupToCanvas.js +840 -0
  292. package/dist/preview/renderTimegroupToCanvas.js.map +1 -0
  293. package/dist/preview/renderTimegroupToVideo.d.ts +39 -0
  294. package/dist/preview/renderTimegroupToVideo.js +274 -0
  295. package/dist/preview/renderTimegroupToVideo.js.map +1 -0
  296. package/dist/preview/renderers.js +16 -0
  297. package/dist/preview/renderers.js.map +1 -0
  298. package/dist/preview/statsTrackingStrategy.js +201 -0
  299. package/dist/preview/statsTrackingStrategy.js.map +1 -0
  300. package/dist/preview/thumbnailCacheSettings.js +52 -0
  301. package/dist/preview/thumbnailCacheSettings.js.map +1 -0
  302. package/dist/preview/workers/WorkerPool.js +178 -0
  303. package/dist/preview/workers/WorkerPool.js.map +1 -0
  304. package/dist/sandbox/PlaybackControls.js +10 -0
  305. package/dist/sandbox/PlaybackControls.js.map +1 -0
  306. package/dist/sandbox/ScenarioRunner.js +1 -0
  307. package/dist/sandbox/index.js +2 -0
  308. package/dist/style.css +66 -69
  309. package/dist/transcoding/types/index.d.ts +2 -1
  310. package/dist/transcoding/utils/UrlGenerator.d.ts +6 -1
  311. package/dist/transcoding/utils/UrlGenerator.js +12 -3
  312. package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
  313. package/dist/utils/LRUCache.js +1 -375
  314. package/dist/utils/LRUCache.js.map +1 -1
  315. package/dist/utils/frameTime.js +14 -0
  316. package/dist/utils/frameTime.js.map +1 -0
  317. package/package.json +3 -3
  318. package/test/profilingPlugin.ts +223 -0
  319. package/test/recordReplayProxyPlugin.js +22 -27
  320. package/test/thumbnail-performance-test.html +116 -0
  321. package/test/visualRegressionUtils.ts +286 -0
  322. package/types.json +1 -1
  323. package/dist/elements/TimegroupController.d.ts +0 -18
  324. package/dist/msToTimeCode.js +0 -17
  325. package/dist/msToTimeCode.js.map +0 -1
@@ -0,0 +1,269 @@
1
+ import { __decorate } from "../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js";
2
+ import { focusContext } from "../../focusContext.js";
3
+ import { focusedElementContext } from "../../focusedElementContext.js";
4
+ import { TWMixin } from "../../TWMixin2.js";
5
+ import { EFAudio } from "../../../elements/EFAudio.js";
6
+ import { EFVideo } from "../../../elements/EFVideo.js";
7
+ import { EFImage } from "../../../elements/EFImage.js";
8
+ import { EFText } from "../../../elements/EFText.js";
9
+ import { ICONS, phosphorIcon } from "../../icons.js";
10
+ import "../TrimHandles.js";
11
+ import { EFTimegroup } from "../../../elements/EFTimegroup.js";
12
+ import { consume } from "@lit/context";
13
+ import { LitElement, css, html, nothing } from "lit";
14
+ import { customElement, property } from "lit/decorators.js";
15
+ import { styleMap } from "lit/directives/style-map.js";
16
+
17
+ //#region src/gui/timeline/tracks/TrackItem.ts
18
+ var ElementTrackController = class {
19
+ constructor(host, track) {
20
+ this.host = host;
21
+ this.track = track;
22
+ this.lastDuration = 0;
23
+ this.checkDuration = () => {
24
+ const currentDuration = this.host.durationMs ?? 0;
25
+ if (currentDuration !== this.lastDuration) {
26
+ this.lastDuration = currentDuration;
27
+ this.track.requestUpdate();
28
+ }
29
+ if (currentDuration === 0) this.durationCheckFrame = requestAnimationFrame(this.checkDuration);
30
+ };
31
+ this.host.addController(this);
32
+ }
33
+ remove() {
34
+ this.host.removeController(this);
35
+ if (this.durationCheckFrame) cancelAnimationFrame(this.durationCheckFrame);
36
+ }
37
+ hostDisconnected() {
38
+ this.host.removeController(this);
39
+ if (this.durationCheckFrame) cancelAnimationFrame(this.durationCheckFrame);
40
+ }
41
+ hostConnected() {
42
+ this.lastDuration = this.host.durationMs ?? 0;
43
+ this.checkDuration();
44
+ }
45
+ hostUpdated() {}
46
+ };
47
+ let TrackItem = class TrackItem$1 extends TWMixin(LitElement) {
48
+ constructor(..._args) {
49
+ super(..._args);
50
+ this.element = new EFTimegroup();
51
+ this.pixelsPerMs = .04;
52
+ this.enableTrim = false;
53
+ this.useAbsolutePosition = false;
54
+ }
55
+ static {
56
+ this.styles = [css`
57
+ :host {
58
+ display: block;
59
+ }
60
+ .trim-container {
61
+ position: relative;
62
+ border-radius: 3px;
63
+ transition: background-color 0.15s ease, box-shadow 0.15s ease;
64
+ }
65
+
66
+ :host(:hover) .trim-container {
67
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.15);
68
+ }
69
+
70
+ :host([data-focused]) .trim-container {
71
+ box-shadow: inset 0 0 0 1px rgba(59, 130, 246, 0.6);
72
+ }
73
+ `];
74
+ }
75
+ get isFocused() {
76
+ return this.element && this.focusContext?.focusedElement === this.element;
77
+ }
78
+ /**
79
+ * Get element type for styling and icons
80
+ */
81
+ getElementType() {
82
+ if (this.element.tagName === "EF-CAPTIONS" || this.element.tagName?.toLowerCase() === "ef-captions") return "captions";
83
+ if (this.element instanceof EFVideo) return "video";
84
+ if (this.element instanceof EFAudio) return "audio";
85
+ if (this.element instanceof EFImage) return "image";
86
+ if (this.element instanceof EFText) return "text";
87
+ if (this.element instanceof EFTimegroup) return "timegroup";
88
+ return "unknown";
89
+ }
90
+ /**
91
+ * Get color for element type
92
+ */
93
+ getElementTypeColor() {
94
+ switch (this.getElementType()) {
95
+ case "video": return "rgb(59, 130, 246)";
96
+ case "audio": return "rgb(34, 197, 94)";
97
+ case "image": return "rgb(168, 85, 247)";
98
+ case "text": return "rgb(249, 115, 22)";
99
+ case "captions": return "rgb(34, 197, 94)";
100
+ case "timegroup": return "rgb(148, 163, 184)";
101
+ default: return "rgb(148, 163, 184)";
102
+ }
103
+ }
104
+ /**
105
+ * Get icon for element type
106
+ */
107
+ getElementIcon() {
108
+ switch (this.getElementType()) {
109
+ case "video": return phosphorIcon(ICONS.filmStrip, 14);
110
+ case "audio": return phosphorIcon(ICONS.speakerHigh, 14);
111
+ case "image": return phosphorIcon(ICONS.image, 14);
112
+ case "text": return phosphorIcon(ICONS.textT, 14);
113
+ case "captions": return phosphorIcon(ICONS.subtitles, 14);
114
+ case "timegroup": return phosphorIcon(ICONS.filmSlate, 14);
115
+ default: return nothing;
116
+ }
117
+ }
118
+ /**
119
+ * Format duration for display
120
+ */
121
+ formatDuration(ms) {
122
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
123
+ return `${(ms / 1e3).toFixed(1)}s`;
124
+ }
125
+ /**
126
+ * Get tooltip text with element info
127
+ */
128
+ getTooltipText() {
129
+ const elementId = this.element?.id || "";
130
+ const type = this.getElementType();
131
+ const duration = this.formatDuration(this.element.durationMs ?? 0);
132
+ const startTime = this.formatDuration(this.element.startTimeMs ?? 0);
133
+ const endTime = this.formatDuration((this.element.startTimeMs ?? 0) + (this.element.durationMs ?? 0));
134
+ const parts = [];
135
+ if (elementId) parts.push(elementId);
136
+ parts.push(`${type} • ${duration}`);
137
+ if (this.element.startTimeMs > 0) parts.push(`${startTime} → ${endTime}`);
138
+ if (type === "timegroup") {
139
+ const mode = this.element.mode || "fixed";
140
+ parts.push(`mode: ${mode}`);
141
+ }
142
+ return parts.join(" • ");
143
+ }
144
+ get gutterStyles() {
145
+ const startMs = this.useAbsolutePosition ? this.element.startTimeMs : this.element.startTimeWithinParentMs;
146
+ const leftOffset = this.useAbsolutePosition ? startMs : startMs - this.element.sourceStartMs;
147
+ return {
148
+ position: "relative",
149
+ left: `${this.pixelsPerMs * leftOffset}px`,
150
+ width: `${this.pixelsPerMs * (this.element.intrinsicDurationMs ?? this.element.durationMs)}px`
151
+ };
152
+ }
153
+ get trimPortionStyles() {
154
+ const leftOffset = this.useAbsolutePosition ? 0 : this.element.sourceStartMs;
155
+ return {
156
+ width: `${this.pixelsPerMs * this.element.durationMs}px`,
157
+ left: `${this.pixelsPerMs * leftOffset}px`
158
+ };
159
+ }
160
+ handleTrimChange(e) {
161
+ const { type, newValueMs } = e.detail;
162
+ if (type === "start") this.element.trimStartMs = newValueMs;
163
+ else this.element.trimEndMs = newValueMs;
164
+ this.dispatchEvent(new CustomEvent("track-trim-change", {
165
+ detail: {
166
+ elementId: this.element.id || "",
167
+ type,
168
+ newValueMs
169
+ },
170
+ bubbles: true,
171
+ composed: true
172
+ }));
173
+ }
174
+ contents() {
175
+ return nothing;
176
+ }
177
+ animations() {
178
+ return [];
179
+ }
180
+ renderChildren() {
181
+ return nothing;
182
+ }
183
+ render() {
184
+ const elementId = this.element.id || "";
185
+ const trimStartMs = this.element.trimStartMs ?? 0;
186
+ const trimEndMs = this.element.trimEndMs ?? 0;
187
+ const intrinsicDurationMs = this.element.intrinsicDurationMs ?? this.element.durationMs;
188
+ const typeColor = this.getElementTypeColor();
189
+ return html`<div style=${styleMap(this.gutterStyles)}>
190
+ <div
191
+ ?data-focused=${this.isFocused}
192
+ @mouseenter=${() => {
193
+ if (this.focusContext) this.focusContext.focusedElement = this.element;
194
+ }}
195
+ @mouseleave=${() => {
196
+ if (this.focusContext) this.focusContext.focusedElement = null;
197
+ }}
198
+ >
199
+ <div
200
+ ?data-focused=${this.isFocused}
201
+ class="trim-container"
202
+ style=${styleMap({
203
+ ...this.trimPortionStyles,
204
+ height: "var(--timeline-track-height, 22px)",
205
+ backgroundColor: this.isFocused ? "rgba(59, 130, 246, 0.25)" : "rgba(30, 41, 59, 0.8)",
206
+ borderLeft: `3px solid ${typeColor}`
207
+ })}
208
+ title="${this.getTooltipText()}"
209
+ >
210
+ ${this.animations()}
211
+ ${this.contents()}
212
+ ${this.enableTrim ? html`<ef-trim-handles
213
+ element-id=${elementId}
214
+ pixels-per-ms=${this.pixelsPerMs}
215
+ trim-start-ms=${trimStartMs}
216
+ trim-end-ms=${trimEndMs}
217
+ intrinsic-duration-ms=${intrinsicDurationMs}
218
+ @trim-change=${this.handleTrimChange}
219
+ ></ef-trim-handles>` : nothing}
220
+ </div>
221
+ </div>
222
+ ${this.renderChildren()}
223
+ </div>`;
224
+ }
225
+ update(changedProperties) {
226
+ if (changedProperties.has("element") && this.element instanceof LitElement) {
227
+ this.trackController?.remove();
228
+ this.trackController = new ElementTrackController(this.element, this);
229
+ }
230
+ super.update(changedProperties);
231
+ }
232
+ };
233
+ __decorate([consume({
234
+ context: focusContext,
235
+ subscribe: true
236
+ })], TrackItem.prototype, "focusContext", void 0);
237
+ __decorate([consume({
238
+ context: focusedElementContext,
239
+ subscribe: true
240
+ })], TrackItem.prototype, "focusedElement", void 0);
241
+ __decorate([property({
242
+ type: Object,
243
+ attribute: false
244
+ })], TrackItem.prototype, "element", void 0);
245
+ __decorate([property({
246
+ type: Number,
247
+ attribute: "pixels-per-ms"
248
+ })], TrackItem.prototype, "pixelsPerMs", void 0);
249
+ __decorate([property({
250
+ type: Boolean,
251
+ attribute: "enable-trim"
252
+ })], TrackItem.prototype, "enableTrim", void 0);
253
+ __decorate([property({
254
+ type: Array,
255
+ attribute: false
256
+ })], TrackItem.prototype, "hideSelectors", void 0);
257
+ __decorate([property({
258
+ type: Array,
259
+ attribute: false
260
+ })], TrackItem.prototype, "showSelectors", void 0);
261
+ __decorate([property({
262
+ type: Boolean,
263
+ attribute: "use-absolute-position"
264
+ })], TrackItem.prototype, "useAbsolutePosition", void 0);
265
+ TrackItem = __decorate([customElement("ef-track-item")], TrackItem);
266
+
267
+ //#endregion
268
+ export { TrackItem };
269
+ //# sourceMappingURL=TrackItem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TrackItem.js","names":["host: LitElement","track: TrackItem","TrackItem"],"sources":["../../../../src/gui/timeline/tracks/TrackItem.ts"],"sourcesContent":["import { consume } from \"@lit/context\";\nimport {\n css,\n html,\n LitElement,\n nothing,\n type PropertyValueMap,\n type ReactiveController,\n type TemplateResult,\n} from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { styleMap } from \"lit/directives/style-map.js\";\n\nimport {\n isEFTemporal,\n type TemporalMixinInterface,\n} from \"../../../elements/EFTemporal.js\";\nimport { EFTimegroup } from \"../../../elements/EFTimegroup.js\";\nimport { EFVideo } from \"../../../elements/EFVideo.js\";\nimport { EFAudio } from \"../../../elements/EFAudio.js\";\nimport { EFImage } from \"../../../elements/EFImage.js\";\nimport { EFText } from \"../../../elements/EFText.js\";\nimport { type FocusContext, focusContext } from \"../../focusContext.js\";\nimport { focusedElementContext } from \"../../focusedElementContext.js\";\nimport { TWMixin } from \"../../TWMixin.js\";\nimport { phosphorIcon, ICONS } from \"../../icons.js\";\nimport \"../TrimHandles.js\";\nimport type { TrimChangeDetail } from \"../TrimHandles.js\";\n\nclass ElementTrackController implements ReactiveController {\n private lastDuration = 0;\n private durationCheckFrame?: number;\n\n constructor(\n private host: LitElement,\n private track: TrackItem,\n ) {\n this.host.addController(this);\n }\n\n remove() {\n this.host.removeController(this);\n if (this.durationCheckFrame) {\n cancelAnimationFrame(this.durationCheckFrame);\n }\n }\n\n hostDisconnected() {\n this.host.removeController(this);\n if (this.durationCheckFrame) {\n cancelAnimationFrame(this.durationCheckFrame);\n }\n }\n\n hostConnected(): void {\n // Start watching for duration changes\n this.lastDuration = (this.host as any).durationMs ?? 0;\n this.checkDuration();\n }\n\n private checkDuration = () => {\n const currentDuration = (this.host as any).durationMs ?? 0;\n if (currentDuration !== this.lastDuration) {\n this.lastDuration = currentDuration;\n // Duration changed - trigger re-render of the track\n this.track.requestUpdate();\n }\n // Keep checking if duration is still 0 (waiting for media to load)\n if (currentDuration === 0) {\n this.durationCheckFrame = requestAnimationFrame(this.checkDuration);\n }\n };\n\n hostUpdated(): void {\n // TEMPORARILY DISABLED: This causes every TrackItem to re-render on every frame\n // during playback, even though TrackItem doesn't display currentTimeMs.\n // Duration changes are now handled separately via checkDuration()\n }\n}\n\nconst CommonEffectKeys = new Set([\n \"offset\",\n \"easing\",\n \"composite\",\n \"computedOffset\",\n]);\n\n@customElement(\"ef-track-item\")\nexport class TrackItem extends TWMixin(LitElement) {\n static styles = [\n css`\n :host {\n display: block;\n }\n .trim-container {\n position: relative;\n border-radius: 3px;\n transition: background-color 0.15s ease, box-shadow 0.15s ease;\n }\n \n :host(:hover) .trim-container {\n box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.15);\n }\n \n :host([data-focused]) .trim-container {\n box-shadow: inset 0 0 0 1px rgba(59, 130, 246, 0.6);\n }\n `,\n ];\n\n @consume({ context: focusContext, subscribe: true })\n focusContext?: FocusContext;\n\n @consume({ context: focusedElementContext, subscribe: true })\n focusedElement?: HTMLElement | null;\n\n get isFocused() {\n return this.element && this.focusContext?.focusedElement === this.element;\n }\n\n /**\n * Get element type for styling and icons\n */\n protected getElementType(): \"video\" | \"audio\" | \"image\" | \"text\" | \"timegroup\" | \"captions\" | \"unknown\" {\n // Check for captions element\n if ((this.element as any).tagName === \"EF-CAPTIONS\" || \n (this.element as any).tagName?.toLowerCase() === \"ef-captions\") {\n return \"captions\";\n }\n if (this.element instanceof EFVideo) return \"video\";\n if (this.element instanceof EFAudio) return \"audio\";\n if (this.element instanceof EFImage) return \"image\";\n if (this.element instanceof EFText) return \"text\";\n if (this.element instanceof EFTimegroup) return \"timegroup\";\n return \"unknown\";\n }\n\n /**\n * Get color for element type\n */\n protected getElementTypeColor(): string {\n const type = this.getElementType();\n switch (type) {\n case \"video\":\n return \"rgb(59, 130, 246)\"; // Blue\n case \"audio\":\n return \"rgb(34, 197, 94)\"; // Green\n case \"image\":\n return \"rgb(168, 85, 247)\"; // Purple\n case \"text\":\n return \"rgb(249, 115, 22)\"; // Orange\n case \"captions\":\n return \"rgb(34, 197, 94)\"; // Green (same as audio, but distinct usage)\n case \"timegroup\":\n return \"rgb(148, 163, 184)\"; // Gray\n default:\n return \"rgb(148, 163, 184)\"; // Gray\n }\n }\n\n /**\n * Get icon for element type\n */\n protected getElementIcon(): TemplateResult | typeof nothing {\n const type = this.getElementType();\n switch (type) {\n case \"video\":\n return phosphorIcon(ICONS.filmStrip, 14);\n case \"audio\":\n return phosphorIcon(ICONS.speakerHigh, 14);\n case \"image\":\n return phosphorIcon(ICONS.image, 14);\n case \"text\":\n return phosphorIcon(ICONS.textT, 14);\n case \"captions\":\n return phosphorIcon(ICONS.subtitles, 14);\n case \"timegroup\":\n return phosphorIcon(ICONS.filmSlate, 14);\n default:\n return nothing;\n }\n }\n\n /**\n * Format duration for display\n */\n protected formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)}ms`;\n }\n const seconds = (ms / 1000).toFixed(1);\n return `${seconds}s`;\n }\n\n /**\n * Get tooltip text with element info\n */\n protected getTooltipText(): string {\n const elementId = (this.element as HTMLElement)?.id || \"\";\n const type = this.getElementType();\n const duration = this.formatDuration(this.element.durationMs ?? 0);\n const startTime = this.formatDuration(this.element.startTimeMs ?? 0);\n const endTime = this.formatDuration((this.element.startTimeMs ?? 0) + (this.element.durationMs ?? 0));\n \n const parts = [];\n if (elementId) parts.push(elementId);\n parts.push(`${type} • ${duration}`);\n if (this.element.startTimeMs > 0) {\n parts.push(`${startTime} → ${endTime}`);\n }\n \n // Add composition mode for timegroups\n if (type === \"timegroup\") {\n const mode = (this.element as any).mode || \"fixed\";\n parts.push(`mode: ${mode}`);\n }\n \n return parts.join(\" • \");\n }\n\n @property({ type: Object, attribute: false })\n element: TemporalMixinInterface & LitElement = new EFTimegroup();\n\n @property({ type: Number, attribute: \"pixels-per-ms\" })\n pixelsPerMs = 0.04;\n\n @property({ type: Boolean, attribute: \"enable-trim\" })\n enableTrim = false;\n\n @property({ type: Array, attribute: false })\n hideSelectors?: string[];\n\n @property({ type: Array, attribute: false })\n showSelectors?: string[];\n\n /**\n * When true, positions the track at the element's absolute start time\n * (startTimeMs) rather than relative to parent (startTimeWithinParentMs).\n * Used for flat row architectures where each element gets its own row.\n */\n @property({ type: Boolean, attribute: \"use-absolute-position\" })\n useAbsolutePosition = false;\n\n get gutterStyles() {\n const startMs = this.useAbsolutePosition\n ? this.element.startTimeMs\n : this.element.startTimeWithinParentMs;\n // When using absolute positioning, don't subtract sourceStartMs - the track container\n // should be positioned at the element's absolute start time in the timeline.\n // When using relative positioning, sourceStartMs is already accounted for in startTimeWithinParentMs.\n const leftOffset = this.useAbsolutePosition\n ? startMs\n : startMs - this.element.sourceStartMs;\n return {\n position: \"relative\",\n left: `${this.pixelsPerMs * leftOffset}px`,\n width: `${this.pixelsPerMs * (this.element.intrinsicDurationMs ?? this.element.durationMs)}px`,\n };\n }\n\n get trimPortionStyles() {\n // When using absolute positioning, the trim container should start at 0\n // relative to the gutter (which is already positioned at startTimeMs).\n // When using relative positioning, we need to offset by sourceStartMs\n // to show the trimmed portion correctly.\n const leftOffset = this.useAbsolutePosition\n ? 0\n : this.element.sourceStartMs;\n return {\n width: `${this.pixelsPerMs * this.element.durationMs}px`,\n left: `${this.pixelsPerMs * leftOffset}px`,\n };\n }\n\n protected handleTrimChange(e: CustomEvent<TrimChangeDetail>): void {\n const { type, newValueMs } = e.detail;\n\n if (type === \"start\") {\n this.element.trimStartMs = newValueMs;\n } else {\n this.element.trimEndMs = newValueMs;\n }\n\n this.dispatchEvent(\n new CustomEvent(\"track-trim-change\", {\n detail: {\n elementId: this.element.id || \"\",\n type,\n newValueMs,\n },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n contents(): TemplateResult | typeof nothing {\n return nothing;\n }\n\n animations() {\n // TEMPORARILY DISABLED: getAnimations() is expensive and called on every render\n // TODO: Cache animations or only compute when element structure changes\n return [];\n \n // const animations = this.element.getAnimations();\n // return animations.map((animation) => {\n // const effect = animation.effect;\n // if (!(effect instanceof KeyframeEffect)) {\n // return nothing;\n // }\n // const start = effect.getTiming().delay ?? 0;\n // const duration = effect.getTiming().duration;\n // if (duration === null) {\n // return nothing;\n // }\n // const keyframes = effect.getKeyframes();\n // const firstKeyframe = keyframes[0];\n // if (!firstKeyframe) {\n // return nothing;\n // }\n // const properties = new Set(Object.keys(firstKeyframe));\n // for (const key of CommonEffectKeys) {\n // properties.delete(key);\n // }\n\n // return html`<div\n // class=\"relative h-[5px] opacity-50\"\n // label=\"animation\"\n // style=${styleMap({\n // left: `${this.pixelsPerMs * start}px`,\n // width: `${this.pixelsPerMs * Number(duration)}px`,\n // backgroundColor: \"var(--filmstrip-animation-bg)\",\n // })}\n // >\n // ${effect.getKeyframes().map((keyframe) => {\n // return html`<div\n // class=\"absolute top-0 h-full w-1\"\n // style=${styleMap({\n // left: `${\n // this.pixelsPerMs * keyframe.computedOffset * Number(duration)\n // }px`,\n // backgroundColor: \"var(--filmstrip-keyframe-bg)\",\n // })}\n // ></div>`;\n // })}\n // </div>`;\n // });\n }\n\n renderChildren(): Array<TemplateResult<1> | typeof nothing> | typeof nothing {\n return nothing;\n }\n\n render() {\n const elementId = (this.element as HTMLElement).id || \"\";\n const trimStartMs = this.element.trimStartMs ?? 0;\n const trimEndMs = this.element.trimEndMs ?? 0;\n const intrinsicDurationMs =\n this.element.intrinsicDurationMs ?? this.element.durationMs;\n\n const typeColor = this.getElementTypeColor();\n \n return html`<div style=${styleMap(this.gutterStyles)}>\n <div\n ?data-focused=${this.isFocused}\n @mouseenter=${() => {\n if (this.focusContext) {\n this.focusContext.focusedElement = this.element;\n }\n }}\n @mouseleave=${() => {\n if (this.focusContext) {\n this.focusContext.focusedElement = null;\n }\n }}\n >\n <div\n ?data-focused=${this.isFocused}\n class=\"trim-container\"\n style=${styleMap({\n ...this.trimPortionStyles,\n height: \"var(--timeline-track-height, 22px)\",\n backgroundColor: this.isFocused\n ? \"rgba(59, 130, 246, 0.25)\"\n : \"rgba(30, 41, 59, 0.8)\",\n borderLeft: `3px solid ${typeColor}`,\n })}\n title=\"${this.getTooltipText()}\"\n >\n ${this.animations()}\n ${this.contents()}\n ${\n this.enableTrim\n ? html`<ef-trim-handles\n element-id=${elementId}\n pixels-per-ms=${this.pixelsPerMs}\n trim-start-ms=${trimStartMs}\n trim-end-ms=${trimEndMs}\n intrinsic-duration-ms=${intrinsicDurationMs}\n @trim-change=${this.handleTrimChange}\n ></ef-trim-handles>`\n : nothing\n }\n </div>\n </div>\n ${this.renderChildren()}\n </div>`;\n }\n\n protected trackController?: ElementTrackController;\n\n update(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>) {\n if (\n changedProperties.has(\"element\") &&\n this.element instanceof LitElement\n ) {\n this.trackController?.remove();\n this.trackController = new ElementTrackController(\n this.element,\n this,\n );\n }\n super.update(changedProperties);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-track-item\": TrackItem;\n }\n}\n\n"],"mappings":"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"}
@@ -0,0 +1,265 @@
1
+ import { __decorate } from "../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js";
2
+ import { EFVideo } from "../../../elements/EFVideo.js";
3
+ import { TrackItem } from "./TrackItem.js";
4
+ import { extractWaveformData } from "./waveformUtils.js";
5
+ import { timelineStateContext } from "../timelineStateContext.js";
6
+ import "../../../elements/EFThumbnailStrip.js";
7
+ import { consume } from "@lit/context";
8
+ import { css, html, nothing } from "lit";
9
+ import { customElement, state } from "lit/decorators.js";
10
+ import { styleMap } from "lit/directives/style-map.js";
11
+ import { createRef, ref } from "lit/directives/ref.js";
12
+
13
+ //#region src/gui/timeline/tracks/VideoTrack.ts
14
+ /** Padding for virtual rendering */
15
+ const VIRTUAL_RENDER_PADDING_PX = 100;
16
+ /** Height of thumbnail section */
17
+ const THUMBNAIL_HEIGHT = 24;
18
+ /** Height of audio section when present */
19
+ const AUDIO_SECTION_HEIGHT = 14;
20
+ let EFVideoTrack = class EFVideoTrack$1 extends TrackItem {
21
+ constructor(..._args) {
22
+ super(..._args);
23
+ this.audioCanvasRef = createRef();
24
+ this._waveformData = null;
25
+ this._hasAudio = false;
26
+ }
27
+ static {
28
+ this.styles = [...TrackItem.styles, css`
29
+ .video-content {
30
+ display: flex;
31
+ flex-direction: column;
32
+ height: 100%;
33
+ }
34
+ .thumbnail-section {
35
+ position: relative;
36
+ flex: 0 0 ${THUMBNAIL_HEIGHT}px;
37
+ height: ${THUMBNAIL_HEIGHT}px;
38
+ }
39
+ ef-thumbnail-strip {
40
+ height: 100%;
41
+ border: none;
42
+ border-radius: 0;
43
+ background: transparent;
44
+ }
45
+ .audio-section {
46
+ position: relative;
47
+ flex: 0 0 ${AUDIO_SECTION_HEIGHT}px;
48
+ height: ${AUDIO_SECTION_HEIGHT}px;
49
+ background: rgba(0, 0, 0, 0.3);
50
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
51
+ overflow: hidden;
52
+ }
53
+ .audio-section-canvas {
54
+ position: absolute;
55
+ top: 0;
56
+ height: 100%;
57
+ }
58
+ `];
59
+ }
60
+ #lastSrc = null;
61
+ #abortController = null;
62
+ #renderRequested = false;
63
+ /**
64
+ * Check if video has audio and load waveform data
65
+ */
66
+ async #checkAndLoadAudioWaveform() {
67
+ const video = this.element;
68
+ const src = video?.src;
69
+ if (!src || src === this.#lastSrc) return;
70
+ this.#lastSrc = src;
71
+ this._hasAudio = false;
72
+ this._waveformData = null;
73
+ this.#abortController?.abort();
74
+ this.#abortController = new AbortController();
75
+ try {
76
+ if (video.mediaEngineTask) {
77
+ if ((await video.mediaEngineTask.taskComplete)?.audioRendition) {
78
+ this._hasAudio = true;
79
+ const waveformData = await extractWaveformData(src, this.#abortController.signal);
80
+ if (waveformData) {
81
+ this._waveformData = waveformData;
82
+ this.#scheduleRender();
83
+ }
84
+ }
85
+ }
86
+ } catch (error) {
87
+ if (!(error instanceof DOMException && error.name === "AbortError")) {}
88
+ }
89
+ }
90
+ #scheduleRender() {
91
+ if (this.#renderRequested) return;
92
+ this.#renderRequested = true;
93
+ requestAnimationFrame(() => {
94
+ this.#renderRequested = false;
95
+ this.#renderAudioOverlay();
96
+ });
97
+ }
98
+ #renderAudioOverlay() {
99
+ const canvas = this.audioCanvasRef.value;
100
+ const waveformData = this._waveformData;
101
+ if (!canvas || !waveformData || !this._hasAudio) return;
102
+ const video = this.element;
103
+ const durationMs = video.durationMs ?? 0;
104
+ if (durationMs === 0) return;
105
+ const pixelsPerMs = this._timelineState?.pixelsPerMs ?? this.pixelsPerMs;
106
+ const trackWidthPx = durationMs * pixelsPerMs;
107
+ const trackStartPx = (video.startTimeMs ?? 0) * pixelsPerMs;
108
+ const scrollLeft = this._timelineState?.viewportScrollLeft ?? 0;
109
+ const viewportWidth = this._timelineState?.viewportWidth ?? 800;
110
+ const visibleLeftPx = scrollLeft - VIRTUAL_RENDER_PADDING_PX;
111
+ const visibleRightPx = scrollLeft + viewportWidth + VIRTUAL_RENDER_PADDING_PX;
112
+ if (trackStartPx + trackWidthPx < visibleLeftPx || trackStartPx > visibleRightPx) {
113
+ canvas.style.display = "none";
114
+ return;
115
+ }
116
+ canvas.style.display = "block";
117
+ const visibleStartInTrack = Math.max(0, visibleLeftPx - trackStartPx);
118
+ const visibleEndInTrack = Math.min(trackWidthPx, visibleRightPx - trackStartPx);
119
+ const visibleWidthPx = visibleEndInTrack - visibleStartInTrack;
120
+ if (visibleWidthPx <= 0) return;
121
+ const height = AUDIO_SECTION_HEIGHT;
122
+ const dpr = window.devicePixelRatio || 1;
123
+ const targetWidth = Math.ceil(visibleWidthPx * dpr);
124
+ const targetHeight = Math.ceil(height * dpr);
125
+ if (canvas.width !== targetWidth || canvas.height !== targetHeight) {
126
+ canvas.width = targetWidth;
127
+ canvas.height = targetHeight;
128
+ }
129
+ canvas.style.left = `${visibleStartInTrack}px`;
130
+ canvas.style.width = `${visibleWidthPx}px`;
131
+ const ctx = canvas.getContext("2d");
132
+ if (!ctx) return;
133
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
134
+ ctx.clearRect(0, 0, visibleWidthPx, height);
135
+ const sourceInMs = video.sourceStartMs ?? 0;
136
+ const timeStartMs = sourceInMs + visibleStartInTrack / pixelsPerMs;
137
+ const timeEndMs = sourceInMs + visibleEndInTrack / pixelsPerMs;
138
+ this.#drawAudioWaveform(ctx, waveformData, visibleWidthPx, height, timeStartMs, timeEndMs);
139
+ }
140
+ #drawAudioWaveform(ctx, waveformData, width, height, startMs, endMs) {
141
+ const { peaks, samplesPerSecond } = waveformData;
142
+ const startSample = Math.floor(startMs / 1e3 * samplesPerSecond);
143
+ const sampleCount = Math.ceil(endMs / 1e3 * samplesPerSecond) - startSample;
144
+ if (sampleCount <= 0 || width <= 0) return;
145
+ const centerY = height / 2;
146
+ const halfHeight = height / 2 - 1;
147
+ const pixelsPerSample = width / sampleCount;
148
+ ctx.fillStyle = "rgb(74, 222, 128)";
149
+ ctx.globalAlpha = .9;
150
+ ctx.beginPath();
151
+ for (let i = 0; i <= sampleCount; i++) {
152
+ const peakIndex = (startSample + i) * 2;
153
+ if (peakIndex + 1 >= peaks.length) break;
154
+ const maxValue = peaks[peakIndex + 1] ?? 0;
155
+ const px = i * pixelsPerSample;
156
+ const py = centerY - maxValue * halfHeight;
157
+ if (i === 0) ctx.moveTo(px, py);
158
+ else ctx.lineTo(px, py);
159
+ }
160
+ for (let i = sampleCount; i >= 0; i--) {
161
+ const peakIndex = (startSample + i) * 2;
162
+ if (peakIndex >= peaks.length) continue;
163
+ const minValue = peaks[peakIndex] ?? 0;
164
+ const px = i * pixelsPerSample;
165
+ const py = centerY - minValue * halfHeight;
166
+ ctx.lineTo(px, py);
167
+ }
168
+ ctx.closePath();
169
+ ctx.fill();
170
+ ctx.globalAlpha = .3;
171
+ ctx.strokeStyle = "rgb(74, 222, 128)";
172
+ ctx.lineWidth = 1;
173
+ ctx.beginPath();
174
+ ctx.moveTo(0, centerY);
175
+ ctx.lineTo(width, centerY);
176
+ ctx.stroke();
177
+ ctx.globalAlpha = 1;
178
+ }
179
+ connectedCallback() {
180
+ super.connectedCallback();
181
+ this.#checkAndLoadAudioWaveform();
182
+ }
183
+ disconnectedCallback() {
184
+ super.disconnectedCallback();
185
+ this.#abortController?.abort();
186
+ }
187
+ updated(changedProperties) {
188
+ super.updated(changedProperties);
189
+ if (this.element?.src !== this.#lastSrc) this.#checkAndLoadAudioWaveform();
190
+ if (changedProperties.has("_timelineState") || changedProperties.has("_waveformData")) this.#scheduleRender();
191
+ if (this._waveformData) this.#scheduleRender();
192
+ }
193
+ /**
194
+ * Get the total track height based on whether audio is present
195
+ */
196
+ #getTrackHeight() {
197
+ if (this._hasAudio && this._waveformData) return THUMBNAIL_HEIGHT + AUDIO_SECTION_HEIGHT;
198
+ return THUMBNAIL_HEIGHT;
199
+ }
200
+ render() {
201
+ const video = this.element;
202
+ const elementId = this.element.id || "";
203
+ if (!(video instanceof EFVideo)) return html``;
204
+ const trimStartMs = this.element.trimStartMs ?? 0;
205
+ const trimEndMs = this.element.trimEndMs ?? 0;
206
+ const intrinsicDurationMs = this.element.intrinsicDurationMs ?? this.element.durationMs;
207
+ const trackHeight = this.#getTrackHeight();
208
+ const hasAudioSection = this._hasAudio && this._waveformData;
209
+ const typeColor = this.getElementTypeColor();
210
+ return html`<div style=${styleMap(this.gutterStyles)}>
211
+ <div
212
+ ?data-focused=${this.isFocused}
213
+ @mouseenter=${() => {
214
+ if (this.focusContext) this.focusContext.focusedElement = this.element;
215
+ }}
216
+ @mouseleave=${() => {
217
+ if (this.focusContext) this.focusContext.focusedElement = null;
218
+ }}
219
+ >
220
+ <div
221
+ ?data-focused=${this.isFocused}
222
+ class="trim-container"
223
+ style=${styleMap({
224
+ ...this.trimPortionStyles,
225
+ height: `${trackHeight}px`,
226
+ backgroundColor: this.isFocused ? "rgba(59, 130, 246, 0.25)" : "rgba(30, 41, 59, 0.8)",
227
+ borderLeft: `3px solid ${typeColor}`,
228
+ borderRadius: "3px"
229
+ })}
230
+ >
231
+ <div class="video-content">
232
+ <div class="thumbnail-section">
233
+ <ef-thumbnail-strip
234
+ .targetElement=${video}
235
+ .useIntrinsicDuration=${true}
236
+ ></ef-thumbnail-strip>
237
+ </div>
238
+ ${hasAudioSection ? html`<div class="audio-section">
239
+ <canvas ${ref(this.audioCanvasRef)} class="audio-section-canvas"></canvas>
240
+ </div>` : nothing}
241
+ </div>
242
+ ${this.enableTrim ? html`<ef-trim-handles
243
+ element-id=${elementId}
244
+ pixels-per-ms=${this.pixelsPerMs}
245
+ trim-start-ms=${trimStartMs}
246
+ trim-end-ms=${trimEndMs}
247
+ intrinsic-duration-ms=${intrinsicDurationMs}
248
+ @trim-change=${this.handleTrimChange}
249
+ ></ef-trim-handles>` : nothing}
250
+ </div>
251
+ </div>
252
+ ${this.renderChildren()}
253
+ </div>`;
254
+ }
255
+ };
256
+ __decorate([consume({
257
+ context: timelineStateContext,
258
+ subscribe: true
259
+ }), state()], EFVideoTrack.prototype, "_timelineState", void 0);
260
+ __decorate([state()], EFVideoTrack.prototype, "_waveformData", void 0);
261
+ __decorate([state()], EFVideoTrack.prototype, "_hasAudio", void 0);
262
+ EFVideoTrack = __decorate([customElement("ef-video-track")], EFVideoTrack);
263
+
264
+ //#endregion
265
+ //# sourceMappingURL=VideoTrack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VideoTrack.js","names":["EFVideoTrack","#checkAndLoadAudioWaveform","#lastSrc","#abortController","#scheduleRender","#renderRequested","#renderAudioOverlay","#drawAudioWaveform","#getTrackHeight"],"sources":["../../../../src/gui/timeline/tracks/VideoTrack.ts"],"sourcesContent":["import { consume } from \"@lit/context\";\nimport { css, html, nothing } from \"lit\";\nimport { customElement, state } from \"lit/decorators.js\";\nimport { createRef, ref } from \"lit/directives/ref.js\";\nimport { styleMap } from \"lit/directives/style-map.js\";\nimport { EFVideo } from \"../../../elements/EFVideo.js\";\nimport \"../../../elements/EFThumbnailStrip.js\";\n// TrackItem must be pre-loaded before this module is imported\n// See preloadTracks.ts for the initialization sequence\nimport { TrackItem } from \"./TrackItem.js\";\nimport {\n extractWaveformData,\n type WaveformData,\n} from \"./waveformUtils.js\";\nimport {\n timelineStateContext,\n type TimelineState,\n} from \"../timelineStateContext.js\";\n\n/** Padding for virtual rendering */\nconst VIRTUAL_RENDER_PADDING_PX = 100;\n\n/** Height of thumbnail section */\nconst THUMBNAIL_HEIGHT = 24;\n/** Height of audio section when present */\nconst AUDIO_SECTION_HEIGHT = 14;\n\n@customElement(\"ef-video-track\")\nexport class EFVideoTrack extends TrackItem {\n static override styles = [\n ...TrackItem.styles,\n css`\n .video-content {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n .thumbnail-section {\n position: relative;\n flex: 0 0 ${THUMBNAIL_HEIGHT}px;\n height: ${THUMBNAIL_HEIGHT}px;\n }\n ef-thumbnail-strip {\n height: 100%;\n border: none;\n border-radius: 0;\n background: transparent;\n }\n .audio-section {\n position: relative;\n flex: 0 0 ${AUDIO_SECTION_HEIGHT}px;\n height: ${AUDIO_SECTION_HEIGHT}px;\n background: rgba(0, 0, 0, 0.3);\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n overflow: hidden;\n }\n .audio-section-canvas {\n position: absolute;\n top: 0;\n height: 100%;\n }\n `,\n ];\n\n audioCanvasRef = createRef<HTMLCanvasElement>();\n\n @consume({ context: timelineStateContext, subscribe: true })\n @state()\n private _timelineState?: TimelineState;\n\n @state()\n private _waveformData: WaveformData | null = null;\n\n @state()\n private _hasAudio = false;\n\n #lastSrc: string | null = null;\n #abortController: AbortController | null = null;\n #renderRequested = false;\n\n /**\n * Check if video has audio and load waveform data\n */\n async #checkAndLoadAudioWaveform(): Promise<void> {\n const video = this.element as EFVideo;\n const src = video?.src;\n\n if (!src || src === this.#lastSrc) {\n return;\n }\n\n this.#lastSrc = src;\n this._hasAudio = false;\n this._waveformData = null;\n\n // Cancel any in-progress load\n this.#abortController?.abort();\n this.#abortController = new AbortController();\n\n try {\n // Wait for media engine to determine if video has audio\n if (video.mediaEngineTask) {\n const mediaEngine = await video.mediaEngineTask.taskComplete;\n if (mediaEngine?.audioRendition) {\n this._hasAudio = true;\n\n // Load waveform data\n const waveformData = await extractWaveformData(\n src,\n this.#abortController.signal,\n );\n\n if (waveformData) {\n this._waveformData = waveformData;\n this.#scheduleRender();\n }\n }\n }\n } catch (error) {\n if (!(error instanceof DOMException && error.name === \"AbortError\")) {\n // Silently fail - audio overlay is optional\n }\n }\n }\n\n #scheduleRender(): void {\n if (this.#renderRequested) return;\n this.#renderRequested = true;\n\n requestAnimationFrame(() => {\n this.#renderRequested = false;\n this.#renderAudioOverlay();\n });\n }\n\n #renderAudioOverlay(): void {\n const canvas = this.audioCanvasRef.value;\n const waveformData = this._waveformData;\n\n if (!canvas || !waveformData || !this._hasAudio) return;\n\n const video = this.element as EFVideo;\n const durationMs = video.durationMs ?? 0;\n if (durationMs === 0) return;\n\n const pixelsPerMs = this._timelineState?.pixelsPerMs ?? this.pixelsPerMs;\n const trackWidthPx = durationMs * pixelsPerMs;\n const trackStartMs = video.startTimeMs ?? 0;\n const trackStartPx = trackStartMs * pixelsPerMs;\n\n // Get scroll/viewport info\n const scrollLeft = this._timelineState?.viewportScrollLeft ?? 0;\n const viewportWidth = this._timelineState?.viewportWidth ?? 800;\n\n // Calculate visible region\n const visibleLeftPx = scrollLeft - VIRTUAL_RENDER_PADDING_PX;\n const visibleRightPx = scrollLeft + viewportWidth + VIRTUAL_RENDER_PADDING_PX;\n const trackEndPx = trackStartPx + trackWidthPx;\n\n // Check visibility\n if (trackEndPx < visibleLeftPx || trackStartPx > visibleRightPx) {\n canvas.style.display = \"none\";\n return;\n }\n canvas.style.display = \"block\";\n\n // Calculate visible portion within track\n const visibleStartInTrack = Math.max(0, visibleLeftPx - trackStartPx);\n const visibleEndInTrack = Math.min(trackWidthPx, visibleRightPx - trackStartPx);\n const visibleWidthPx = visibleEndInTrack - visibleStartInTrack;\n\n if (visibleWidthPx <= 0) return;\n\n const height = AUDIO_SECTION_HEIGHT;\n const dpr = window.devicePixelRatio || 1;\n\n // Set canvas size\n const targetWidth = Math.ceil(visibleWidthPx * dpr);\n const targetHeight = Math.ceil(height * dpr);\n\n if (canvas.width !== targetWidth || canvas.height !== targetHeight) {\n canvas.width = targetWidth;\n canvas.height = targetHeight;\n }\n\n canvas.style.left = `${visibleStartInTrack}px`;\n canvas.style.width = `${visibleWidthPx}px`;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, visibleWidthPx, height);\n\n // Calculate time range to render\n const sourceInMs = video.sourceStartMs ?? 0;\n const timeStartMs = sourceInMs + visibleStartInTrack / pixelsPerMs;\n const timeEndMs = sourceInMs + visibleEndInTrack / pixelsPerMs;\n\n // Draw waveform in dedicated section\n this.#drawAudioWaveform(ctx, waveformData, visibleWidthPx, height, timeStartMs, timeEndMs);\n }\n\n #drawAudioWaveform(\n ctx: CanvasRenderingContext2D,\n waveformData: WaveformData,\n width: number,\n height: number,\n startMs: number,\n endMs: number,\n ): void {\n const { peaks, samplesPerSecond } = waveformData;\n\n const startSample = Math.floor((startMs / 1000) * samplesPerSecond);\n const endSample = Math.ceil((endMs / 1000) * samplesPerSecond);\n const sampleCount = endSample - startSample;\n\n if (sampleCount <= 0 || width <= 0) return;\n\n const centerY = height / 2;\n const halfHeight = (height / 2) - 1;\n const pixelsPerSample = width / sampleCount;\n\n // Draw filled waveform\n ctx.fillStyle = \"rgb(74, 222, 128)\";\n ctx.globalAlpha = 0.9;\n ctx.beginPath();\n\n // Draw top half (max values) left to right\n for (let i = 0; i <= sampleCount; i++) {\n const sampleIndex = startSample + i;\n const peakIndex = sampleIndex * 2;\n if (peakIndex + 1 >= peaks.length) break;\n\n const maxValue = peaks[peakIndex + 1] ?? 0;\n const px = i * pixelsPerSample;\n const py = centerY - maxValue * halfHeight;\n\n if (i === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n }\n\n // Draw bottom half (min values) right to left\n for (let i = sampleCount; i >= 0; i--) {\n const sampleIndex = startSample + i;\n const peakIndex = sampleIndex * 2;\n if (peakIndex >= peaks.length) continue;\n\n const minValue = peaks[peakIndex] ?? 0;\n const px = i * pixelsPerSample;\n const py = centerY - minValue * halfHeight;\n\n ctx.lineTo(px, py);\n }\n\n ctx.closePath();\n ctx.fill();\n\n // Draw center line\n ctx.globalAlpha = 0.3;\n ctx.strokeStyle = \"rgb(74, 222, 128)\";\n ctx.lineWidth = 1;\n ctx.beginPath();\n ctx.moveTo(0, centerY);\n ctx.lineTo(width, centerY);\n ctx.stroke();\n\n ctx.globalAlpha = 1;\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n this.#checkAndLoadAudioWaveform();\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this.#abortController?.abort();\n }\n\n updated(changedProperties: Map<string | number | symbol, unknown>): void {\n super.updated(changedProperties);\n\n const video = this.element as EFVideo;\n if (video?.src !== this.#lastSrc) {\n this.#checkAndLoadAudioWaveform();\n }\n\n if (changedProperties.has(\"_timelineState\") || changedProperties.has(\"_waveformData\")) {\n this.#scheduleRender();\n }\n\n // Always schedule render after update\n if (this._waveformData) {\n this.#scheduleRender();\n }\n }\n\n /**\n * Get the total track height based on whether audio is present\n */\n #getTrackHeight(): number {\n if (this._hasAudio && this._waveformData) {\n return THUMBNAIL_HEIGHT + AUDIO_SECTION_HEIGHT;\n }\n return THUMBNAIL_HEIGHT;\n }\n\n override render() {\n const video = this.element as EFVideo;\n const elementId = (this.element as HTMLElement).id || \"\";\n\n // Don't render thumbnail strip until we have a valid EFVideo element\n if (!(video instanceof EFVideo)) {\n return html``;\n }\n const trimStartMs = this.element.trimStartMs ?? 0;\n const trimEndMs = this.element.trimEndMs ?? 0;\n const intrinsicDurationMs =\n this.element.intrinsicDurationMs ?? this.element.durationMs;\n\n const trackHeight = this.#getTrackHeight();\n const hasAudioSection = this._hasAudio && this._waveformData;\n\n const typeColor = this.getElementTypeColor();\n \n return html`<div style=${styleMap(this.gutterStyles)}>\n <div\n ?data-focused=${this.isFocused}\n @mouseenter=${() => {\n if (this.focusContext) {\n this.focusContext.focusedElement = this.element;\n }\n }}\n @mouseleave=${() => {\n if (this.focusContext) {\n this.focusContext.focusedElement = null;\n }\n }}\n >\n <div\n ?data-focused=${this.isFocused}\n class=\"trim-container\"\n style=${styleMap({\n ...this.trimPortionStyles,\n height: `${trackHeight}px`,\n backgroundColor: this.isFocused\n ? \"rgba(59, 130, 246, 0.25)\"\n : \"rgba(30, 41, 59, 0.8)\",\n borderLeft: `3px solid ${typeColor}`,\n borderRadius: \"3px\",\n })}\n >\n <div class=\"video-content\">\n <div class=\"thumbnail-section\">\n <ef-thumbnail-strip\n .targetElement=${video}\n .useIntrinsicDuration=${true}\n ></ef-thumbnail-strip>\n </div>\n ${hasAudioSection\n ? html`<div class=\"audio-section\">\n <canvas ${ref(this.audioCanvasRef)} class=\"audio-section-canvas\"></canvas>\n </div>`\n : nothing\n }\n </div>\n ${\n this.enableTrim\n ? html`<ef-trim-handles\n element-id=${elementId}\n pixels-per-ms=${this.pixelsPerMs}\n trim-start-ms=${trimStartMs}\n trim-end-ms=${trimEndMs}\n intrinsic-duration-ms=${intrinsicDurationMs}\n @trim-change=${this.handleTrimChange}\n ></ef-trim-handles>`\n : nothing\n }\n </div>\n </div>\n ${this.renderChildren()}\n </div>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-video-track\": EFVideoTrack;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { __decorate } from "../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js";
2
+ import { ICONS, phosphorIcon } from "../../icons.js";
3
+ import { TrackItem } from "./TrackItem.js";
4
+ import { nothing } from "lit";
5
+ import { customElement } from "lit/decorators.js";
6
+
7
+ //#region src/gui/timeline/tracks/WaveformTrack.ts
8
+ let EFWaveformTrack = class EFWaveformTrack$1 extends TrackItem {
9
+ contents() {
10
+ return phosphorIcon(ICONS.waveform);
11
+ }
12
+ renderChildren() {
13
+ return nothing;
14
+ }
15
+ };
16
+ EFWaveformTrack = __decorate([customElement("ef-waveform-track")], EFWaveformTrack);
17
+
18
+ //#endregion
19
+ //# sourceMappingURL=WaveformTrack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WaveformTrack.js","names":["EFWaveformTrack"],"sources":["../../../../src/gui/timeline/tracks/WaveformTrack.ts"],"sourcesContent":["import { html, nothing, type TemplateResult } from \"lit\";\nimport { customElement } from \"lit/decorators.js\";\nimport { phosphorIcon, ICONS } from \"../../icons.js\";\n// TrackItem must be pre-loaded before this module is imported\n// See preloadTracks.ts for the initialization sequence\nimport { TrackItem } from \"./TrackItem.js\";\n\n@customElement(\"ef-waveform-track\")\nexport class EFWaveformTrack extends TrackItem {\n contents() {\n return phosphorIcon(ICONS.waveform);\n }\n\n renderChildren(): Array<TemplateResult<1> | typeof nothing> | typeof nothing {\n return nothing;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-waveform-track\": EFWaveformTrack;\n }\n}\n\n"],"mappings":";;;;;;;AAQO,4BAAMA,0BAAwB,UAAU;CAC7C,WAAW;AACT,SAAO,aAAa,MAAM,SAAS;;CAGrC,iBAA6E;AAC3E,SAAO;;;8BAPV,cAAc,oBAAoB"}
@@ -0,0 +1 @@
1
+ import { TrackItem } from "./TrackItem.js";
@@ -0,0 +1,9 @@
1
+ import "./ensureTrackItemInit.js";
2
+ import "./AudioTrack.js";
3
+ import "./VideoTrack.js";
4
+ import "./ImageTrack.js";
5
+ import "./TimegroupTrack.js";
6
+ import "./TextTrack.js";
7
+ import "./HTMLTrack.js";
8
+ import "./CaptionsTrack.js";
9
+ import "./WaveformTrack.js";