@editframe/elements 0.30.2-beta.0 → 0.31.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/dist/EF_FRAMEGEN.d.ts +5 -0
  2. package/dist/EF_FRAMEGEN.js +20 -4
  3. package/dist/EF_FRAMEGEN.js.map +1 -1
  4. package/dist/EF_INTERACTIVE.js.map +1 -1
  5. package/dist/_virtual/rolldown_runtime.js +27 -0
  6. package/dist/canvas/EFCanvas.d.ts +311 -0
  7. package/dist/canvas/EFCanvas.js +1089 -0
  8. package/dist/canvas/EFCanvas.js.map +1 -0
  9. package/dist/canvas/EFCanvasItem.d.ts +55 -0
  10. package/dist/canvas/EFCanvasItem.js +72 -0
  11. package/dist/canvas/EFCanvasItem.js.map +1 -0
  12. package/dist/canvas/api/CanvasAPI.d.ts +115 -0
  13. package/dist/canvas/api/CanvasAPI.js +182 -0
  14. package/dist/canvas/api/CanvasAPI.js.map +1 -0
  15. package/dist/canvas/api/types.d.ts +42 -0
  16. package/dist/canvas/coordinateTransform.js +90 -0
  17. package/dist/canvas/coordinateTransform.js.map +1 -0
  18. package/dist/canvas/getElementBounds.js +40 -0
  19. package/dist/canvas/getElementBounds.js.map +1 -0
  20. package/dist/canvas/overlays/SelectionOverlay.js +265 -0
  21. package/dist/canvas/overlays/SelectionOverlay.js.map +1 -0
  22. package/dist/canvas/overlays/overlayState.js +153 -0
  23. package/dist/canvas/overlays/overlayState.js.map +1 -0
  24. package/dist/canvas/selection/SelectionController.js +105 -0
  25. package/dist/canvas/selection/SelectionController.js.map +1 -0
  26. package/dist/canvas/selection/SelectionModel.d.ts +98 -0
  27. package/dist/canvas/selection/SelectionModel.js +229 -0
  28. package/dist/canvas/selection/SelectionModel.js.map +1 -0
  29. package/dist/canvas/selection/selectionContext.d.ts +31 -0
  30. package/dist/canvas/selection/selectionContext.js +12 -0
  31. package/dist/canvas/selection/selectionContext.js.map +1 -0
  32. package/dist/elements/ContainerInfo.d.ts +29 -0
  33. package/dist/elements/ContainerInfo.js +30 -0
  34. package/dist/elements/ContainerInfo.js.map +1 -0
  35. package/dist/elements/EFAudio.d.ts +13 -3
  36. package/dist/elements/EFAudio.js +64 -10
  37. package/dist/elements/EFAudio.js.map +1 -1
  38. package/dist/elements/EFCaptions.d.ts +18 -16
  39. package/dist/elements/EFCaptions.js +110 -19
  40. package/dist/elements/EFCaptions.js.map +1 -1
  41. package/dist/elements/EFImage.d.ts +16 -6
  42. package/dist/elements/EFImage.js +79 -9
  43. package/dist/elements/EFImage.js.map +1 -1
  44. package/dist/elements/EFMedia/AssetIdMediaEngine.js +51 -4
  45. package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +1 -1
  46. package/dist/elements/EFMedia/AssetMediaEngine.js +125 -52
  47. package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
  48. package/dist/elements/EFMedia/BaseMediaEngine.js +24 -6
  49. package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
  50. package/dist/elements/EFMedia/JitMediaEngine.js +12 -8
  51. package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
  52. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +46 -7
  53. package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js.map +1 -1
  54. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +98 -73
  55. package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js.map +1 -1
  56. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +28 -5
  57. package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js.map +1 -1
  58. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +18 -6
  59. package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js.map +1 -1
  60. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +8 -2
  61. package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js.map +1 -1
  62. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +31 -6
  63. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js.map +1 -1
  64. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +28 -5
  65. package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js.map +1 -1
  66. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +97 -72
  67. package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js.map +1 -1
  68. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -1
  69. package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
  70. package/dist/elements/EFMedia/shared/BufferUtils.js +1 -1
  71. package/dist/elements/EFMedia/shared/BufferUtils.js.map +1 -1
  72. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +25 -14
  73. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
  74. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +47 -16
  75. package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js.map +1 -1
  76. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +37 -19
  77. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
  78. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +65 -21
  79. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
  80. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +8 -3
  81. package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js.map +1 -1
  82. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js +32 -9
  83. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js.map +1 -1
  84. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +33 -10
  85. package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js.map +1 -1
  86. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +23 -8
  87. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js.map +1 -1
  88. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js +34 -10
  89. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js.map +1 -1
  90. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +31 -8
  91. package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js.map +1 -1
  92. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +31 -114
  93. package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js.map +1 -1
  94. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +44 -8
  95. package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js.map +1 -1
  96. package/dist/elements/EFMedia.d.ts +18 -7
  97. package/dist/elements/EFMedia.js +23 -3
  98. package/dist/elements/EFMedia.js.map +1 -1
  99. package/dist/elements/EFPanZoom.d.ts +96 -0
  100. package/dist/elements/EFPanZoom.js +290 -0
  101. package/dist/elements/EFPanZoom.js.map +1 -0
  102. package/dist/elements/EFSourceMixin.js +7 -6
  103. package/dist/elements/EFSourceMixin.js.map +1 -1
  104. package/dist/elements/EFSurface.d.ts +6 -6
  105. package/dist/elements/EFSurface.js +7 -2
  106. package/dist/elements/EFSurface.js.map +1 -1
  107. package/dist/elements/EFTemporal.d.ts +2 -1
  108. package/dist/elements/EFTemporal.js +192 -71
  109. package/dist/elements/EFTemporal.js.map +1 -1
  110. package/dist/elements/EFText.d.ts +5 -4
  111. package/dist/elements/EFText.js +102 -13
  112. package/dist/elements/EFText.js.map +1 -1
  113. package/dist/elements/EFTextSegment.d.ts +32 -6
  114. package/dist/elements/EFTextSegment.js +53 -15
  115. package/dist/elements/EFTextSegment.js.map +1 -1
  116. package/dist/elements/EFThumbnailStrip.d.ts +129 -56
  117. package/dist/elements/EFThumbnailStrip.js +605 -359
  118. package/dist/elements/EFThumbnailStrip.js.map +1 -1
  119. package/dist/elements/EFTimegroup.d.ts +233 -25
  120. package/dist/elements/EFTimegroup.js +865 -144
  121. package/dist/elements/EFTimegroup.js.map +1 -1
  122. package/dist/elements/EFVideo.d.ts +42 -5
  123. package/dist/elements/EFVideo.js +165 -11
  124. package/dist/elements/EFVideo.js.map +1 -1
  125. package/dist/elements/EFWaveform.d.ts +6 -6
  126. package/dist/elements/EFWaveform.js +2 -1
  127. package/dist/elements/EFWaveform.js.map +1 -1
  128. package/dist/elements/ElementPositionInfo.d.ts +35 -0
  129. package/dist/elements/ElementPositionInfo.js +49 -0
  130. package/dist/elements/ElementPositionInfo.js.map +1 -0
  131. package/dist/elements/FetchMixin.js +16 -1
  132. package/dist/elements/FetchMixin.js.map +1 -1
  133. package/dist/elements/SessionThumbnailCache.js +154 -0
  134. package/dist/elements/SessionThumbnailCache.js.map +1 -0
  135. package/dist/elements/TargetController.js +3 -1
  136. package/dist/elements/TargetController.js.map +1 -1
  137. package/dist/elements/TimegroupController.js +9 -3
  138. package/dist/elements/TimegroupController.js.map +1 -1
  139. package/dist/elements/findRootTemporal.js +30 -0
  140. package/dist/elements/findRootTemporal.js.map +1 -0
  141. package/dist/elements/renderTemporalAudio.js +18 -5
  142. package/dist/elements/renderTemporalAudio.js.map +1 -1
  143. package/dist/elements/updateAnimations.js +171 -28
  144. package/dist/elements/updateAnimations.js.map +1 -1
  145. package/dist/getRenderInfo.d.ts +2 -2
  146. package/dist/gui/ContextMixin.js +4 -2
  147. package/dist/gui/ContextMixin.js.map +1 -1
  148. package/dist/gui/Controllable.js +74 -1
  149. package/dist/gui/Controllable.js.map +1 -1
  150. package/dist/gui/EFActiveRootTemporal.d.ts +50 -0
  151. package/dist/gui/EFActiveRootTemporal.js +94 -0
  152. package/dist/gui/EFActiveRootTemporal.js.map +1 -0
  153. package/dist/gui/EFConfiguration.d.ts +7 -1
  154. package/dist/gui/EFConfiguration.js.map +1 -1
  155. package/dist/gui/EFControls.d.ts +2 -2
  156. package/dist/gui/EFControls.js +109 -13
  157. package/dist/gui/EFControls.js.map +1 -1
  158. package/dist/gui/EFDial.d.ts +4 -4
  159. package/dist/gui/EFFilmstrip.d.ts +11 -214
  160. package/dist/gui/EFFilmstrip.js +53 -1152
  161. package/dist/gui/EFFilmstrip.js.map +1 -1
  162. package/dist/gui/EFFitScale.d.ts +3 -3
  163. package/dist/gui/EFFitScale.js +39 -12
  164. package/dist/gui/EFFitScale.js.map +1 -1
  165. package/dist/gui/EFFocusOverlay.d.ts +4 -4
  166. package/dist/gui/EFOverlayItem.d.ts +48 -0
  167. package/dist/gui/EFOverlayItem.js +97 -0
  168. package/dist/gui/EFOverlayItem.js.map +1 -0
  169. package/dist/gui/EFOverlayLayer.d.ts +70 -0
  170. package/dist/gui/EFOverlayLayer.js +104 -0
  171. package/dist/gui/EFOverlayLayer.js.map +1 -0
  172. package/dist/gui/EFPause.d.ts +4 -4
  173. package/dist/gui/EFPlay.d.ts +4 -4
  174. package/dist/gui/EFResizableBox.d.ts +12 -16
  175. package/dist/gui/EFResizableBox.js +109 -451
  176. package/dist/gui/EFResizableBox.js.map +1 -1
  177. package/dist/gui/EFScrubber.d.ts +30 -5
  178. package/dist/gui/EFScrubber.js +224 -31
  179. package/dist/gui/EFScrubber.js.map +1 -1
  180. package/dist/gui/EFTimeDisplay.d.ts +4 -4
  181. package/dist/gui/EFTimeDisplay.js +4 -1
  182. package/dist/gui/EFTimeDisplay.js.map +1 -1
  183. package/dist/gui/EFTimelineRuler.d.ts +71 -0
  184. package/dist/gui/EFTimelineRuler.js +320 -0
  185. package/dist/gui/EFTimelineRuler.js.map +1 -0
  186. package/dist/gui/EFToggleLoop.d.ts +4 -4
  187. package/dist/gui/EFTogglePlay.d.ts +4 -4
  188. package/dist/gui/EFTransformHandles.d.ts +91 -0
  189. package/dist/gui/EFTransformHandles.js +393 -0
  190. package/dist/gui/EFTransformHandles.js.map +1 -0
  191. package/dist/gui/EFWorkbench.d.ts +178 -0
  192. package/dist/gui/EFWorkbench.js +2067 -22
  193. package/dist/gui/EFWorkbench.js.map +1 -1
  194. package/dist/gui/FitScaleHelpers.d.ts +31 -0
  195. package/dist/gui/FitScaleHelpers.js +41 -0
  196. package/dist/gui/FitScaleHelpers.js.map +1 -0
  197. package/dist/gui/PlaybackController.d.ts +2 -1
  198. package/dist/gui/PlaybackController.js +46 -15
  199. package/dist/gui/PlaybackController.js.map +1 -1
  200. package/dist/gui/TWMixin.js +1 -1
  201. package/dist/gui/TWMixin.js.map +1 -1
  202. package/dist/gui/hierarchy/EFHierarchy.d.ts +65 -0
  203. package/dist/gui/hierarchy/EFHierarchy.js +338 -0
  204. package/dist/gui/hierarchy/EFHierarchy.js.map +1 -0
  205. package/dist/gui/hierarchy/EFHierarchyItem.d.ts +118 -0
  206. package/dist/gui/hierarchy/EFHierarchyItem.js +551 -0
  207. package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -0
  208. package/dist/gui/hierarchy/hierarchyContext.d.ts +38 -0
  209. package/dist/gui/hierarchy/hierarchyContext.js +8 -0
  210. package/dist/gui/hierarchy/hierarchyContext.js.map +1 -0
  211. package/dist/gui/icons.js +34 -0
  212. package/dist/gui/icons.js.map +1 -0
  213. package/dist/gui/panZoomTransformContext.js +12 -0
  214. package/dist/gui/panZoomTransformContext.js.map +1 -0
  215. package/dist/gui/previewSettingsContext.js +12 -0
  216. package/dist/gui/previewSettingsContext.js.map +1 -0
  217. package/dist/gui/timeline/EFTimeline.d.ts +270 -0
  218. package/dist/gui/timeline/EFTimeline.js +1369 -0
  219. package/dist/gui/timeline/EFTimeline.js.map +1 -0
  220. package/dist/gui/timeline/EFTimelineRow.js +374 -0
  221. package/dist/gui/timeline/EFTimelineRow.js.map +1 -0
  222. package/dist/gui/timeline/TrimHandles.d.ts +36 -0
  223. package/dist/gui/timeline/TrimHandles.js +204 -0
  224. package/dist/gui/timeline/TrimHandles.js.map +1 -0
  225. package/dist/gui/timeline/flattenHierarchy.js +31 -0
  226. package/dist/gui/timeline/flattenHierarchy.js.map +1 -0
  227. package/dist/gui/timeline/timelineStateContext.d.ts +26 -0
  228. package/dist/gui/timeline/timelineStateContext.js +42 -0
  229. package/dist/gui/timeline/timelineStateContext.js.map +1 -0
  230. package/dist/gui/timeline/tracks/AudioTrack.js +264 -0
  231. package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -0
  232. package/dist/gui/timeline/tracks/CaptionsTrack.js +595 -0
  233. package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -0
  234. package/dist/gui/timeline/tracks/HTMLTrack.js +19 -0
  235. package/dist/gui/timeline/tracks/HTMLTrack.js.map +1 -0
  236. package/dist/gui/timeline/tracks/ImageTrack.js +53 -0
  237. package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -0
  238. package/dist/gui/timeline/tracks/TextTrack.js +250 -0
  239. package/dist/gui/timeline/tracks/TextTrack.js.map +1 -0
  240. package/dist/gui/timeline/tracks/TimegroupTrack.js +143 -0
  241. package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -0
  242. package/dist/gui/timeline/tracks/TrackItem.js +269 -0
  243. package/dist/gui/timeline/tracks/TrackItem.js.map +1 -0
  244. package/dist/gui/timeline/tracks/VideoTrack.js +265 -0
  245. package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -0
  246. package/dist/gui/timeline/tracks/WaveformTrack.js +19 -0
  247. package/dist/gui/timeline/tracks/WaveformTrack.js.map +1 -0
  248. package/dist/gui/timeline/tracks/ensureTrackItemInit.js +1 -0
  249. package/dist/gui/timeline/tracks/preloadTracks.js +9 -0
  250. package/dist/gui/timeline/tracks/renderTrackChildren.js +119 -0
  251. package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -0
  252. package/dist/gui/timeline/tracks/waveformUtils.js +80 -0
  253. package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -0
  254. package/dist/gui/transformCalculations.js +217 -0
  255. package/dist/gui/transformCalculations.js.map +1 -0
  256. package/dist/gui/transformUtils.d.ts +37 -0
  257. package/dist/gui/transformUtils.js +77 -0
  258. package/dist/gui/transformUtils.js.map +1 -0
  259. package/dist/gui/tree/EFTree.d.ts +59 -0
  260. package/dist/gui/tree/EFTree.js +174 -0
  261. package/dist/gui/tree/EFTree.js.map +1 -0
  262. package/dist/gui/tree/EFTreeItem.d.ts +38 -0
  263. package/dist/gui/tree/EFTreeItem.js +146 -0
  264. package/dist/gui/tree/EFTreeItem.js.map +1 -0
  265. package/dist/gui/tree/treeContext.d.ts +60 -0
  266. package/dist/gui/tree/treeContext.js +23 -0
  267. package/dist/gui/tree/treeContext.js.map +1 -0
  268. package/dist/index.d.ts +32 -8
  269. package/dist/index.js +30 -6
  270. package/dist/index.js.map +1 -1
  271. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +688 -0
  272. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +1 -0
  273. package/dist/node_modules/react/cjs/react.development.js +1521 -0
  274. package/dist/node_modules/react/cjs/react.development.js.map +1 -0
  275. package/dist/node_modules/react/index.js +13 -0
  276. package/dist/node_modules/react/index.js.map +1 -0
  277. package/dist/node_modules/react/jsx-runtime.js +13 -0
  278. package/dist/node_modules/react/jsx-runtime.js.map +1 -0
  279. package/dist/preview/AdaptiveResolutionTracker.js +228 -0
  280. package/dist/preview/AdaptiveResolutionTracker.js.map +1 -0
  281. package/dist/preview/RenderProfiler.js +135 -0
  282. package/dist/preview/RenderProfiler.js.map +1 -0
  283. package/dist/preview/previewSettings.js +131 -0
  284. package/dist/preview/previewSettings.js.map +1 -0
  285. package/dist/preview/previewTypes.js +64 -0
  286. package/dist/preview/previewTypes.js.map +1 -0
  287. package/dist/preview/renderTimegroupPreview.js +656 -0
  288. package/dist/preview/renderTimegroupPreview.js.map +1 -0
  289. package/dist/preview/renderTimegroupToCanvas.d.ts +37 -0
  290. package/dist/preview/renderTimegroupToCanvas.js +833 -0
  291. package/dist/preview/renderTimegroupToCanvas.js.map +1 -0
  292. package/dist/preview/renderTimegroupToVideo.d.ts +39 -0
  293. package/dist/preview/renderTimegroupToVideo.js +274 -0
  294. package/dist/preview/renderTimegroupToVideo.js.map +1 -0
  295. package/dist/preview/renderers.js +16 -0
  296. package/dist/preview/renderers.js.map +1 -0
  297. package/dist/preview/statsTrackingStrategy.js +201 -0
  298. package/dist/preview/statsTrackingStrategy.js.map +1 -0
  299. package/dist/preview/thumbnailCacheSettings.js +52 -0
  300. package/dist/preview/thumbnailCacheSettings.js.map +1 -0
  301. package/dist/preview/workers/WorkerPool.js +178 -0
  302. package/dist/preview/workers/WorkerPool.js.map +1 -0
  303. package/dist/preview/workers/encoderWorkerInline.js +103 -0
  304. package/dist/preview/workers/encoderWorkerInline.js.map +1 -0
  305. package/dist/sandbox/PlaybackControls.js +10 -0
  306. package/dist/sandbox/PlaybackControls.js.map +1 -0
  307. package/dist/sandbox/ScenarioRunner.js +1 -0
  308. package/dist/sandbox/index.js +2 -0
  309. package/dist/style.css +71 -67
  310. package/dist/transcoding/types/index.d.ts +2 -1
  311. package/dist/transcoding/utils/UrlGenerator.d.ts +6 -1
  312. package/dist/transcoding/utils/UrlGenerator.js +12 -3
  313. package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
  314. package/dist/utils/LRUCache.js +1 -375
  315. package/dist/utils/LRUCache.js.map +1 -1
  316. package/dist/utils/frameTime.js +14 -0
  317. package/dist/utils/frameTime.js.map +1 -0
  318. package/package.json +3 -3
  319. package/test/profilingPlugin.ts +223 -0
  320. package/test/recordReplayProxyPlugin.js +22 -27
  321. package/test/thumbnail-performance-test.html +116 -0
  322. package/test/visualRegressionUtils.ts +286 -0
  323. package/types.json +1 -1
  324. package/dist/elements/TimegroupController.d.ts +0 -18
  325. package/dist/msToTimeCode.js +0 -17
  326. package/dist/msToTimeCode.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"EFFilmstrip.js","names":["host: LitElement","filmstrip: FilmstripItem","EFAudioFilmstrip","EFVideoFilmstrip","EFCaptionsFilmstrip","EFCaptionsActiveWordFilmstrip","EFCaptionsSegmentFilmstrip","EFCaptionsBeforeWordFilmstrip","EFCaptionsAfterWordFilmstrip","EFWaveformFilmstrip","EFTextFilmstrip","EFTextSegmentFilmstrip","EFImageFilmstrip","EFTimegroupFilmstrip","EFHTMLFilmstrip","EFHierarchyItem","EFTimegroupHierarchyItem","EFAudioHierarchyItem","EFVideoHierarchyItem","EFCaptionsHierarchyItem","EFCaptionsActiveWordHierarchyItem","EFTextHierarchyItem","EFTextSegmentHierarchyItem","EFWaveformHierarchyItem","EFImageHierarchyItem","EFHTMLHierarchyItem","EFFilmstrip","#bindToTargetTimegroup","#handleKeyPress","#targetController","#contextElement","target","#lastTargetTemporal"],"sources":["../../src/gui/EFFilmstrip.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 {\n customElement,\n eventOptions,\n property,\n state,\n} from \"lit/decorators.js\";\nimport { createRef, ref } from \"lit/directives/ref.js\";\nimport { styleMap } from \"lit/directives/style-map.js\";\n\nimport { EFAudio } from \"../elements/EFAudio.js\";\nimport {\n type Caption,\n EFCaptions,\n EFCaptionsActiveWord,\n} from \"../elements/EFCaptions.js\";\nimport { EFImage } from \"../elements/EFImage.js\";\nimport { EFText } from \"../elements/EFText.js\";\nimport { EFTextSegment } from \"../elements/EFTextSegment.js\";\nimport {\n isEFTemporal,\n type TemporalMixinInterface,\n} from \"../elements/EFTemporal.js\";\nimport { EFTimegroup } from \"../elements/EFTimegroup.js\";\nimport { EFVideo } from \"../elements/EFVideo.js\";\nimport { EFWaveform } from \"../elements/EFWaveform.js\";\nimport { TargetController } from \"../elements/TargetController.js\";\nimport { TimegroupController } from \"../elements/TimegroupController.js\";\nimport { msToTimeCode } from \"../msToTimeCode.js\";\nimport { targetTemporalContext } from \"./ContextMixin.ts\";\nimport type { EFPreview } from \"./EFPreview.js\";\nimport type { EFWorkbench } from \"./EFWorkbench.js\";\nimport { type FocusContext, focusContext } from \"./focusContext.js\";\nimport { focusedElementContext } from \"./focusedElementContext.js\";\nimport { loopContext, playingContext } from \"./playingContext.js\";\nimport { TWMixin } from \"./TWMixin.js\";\n\nclass ElementFilmstripController implements ReactiveController {\n constructor(\n private host: LitElement,\n private filmstrip: FilmstripItem,\n ) {\n this.host.addController(this);\n }\n\n remove() {\n this.host.removeController(this);\n }\n\n hostDisconnected() {\n this.host.removeController(this);\n }\n\n hostUpdated(): void {\n this.filmstrip.requestUpdate();\n }\n}\n\nconst CommonEffectKeys = new Set([\n \"offset\",\n \"easing\",\n \"composite\",\n \"computedOffset\",\n]);\n\nclass FilmstripItem extends TWMixin(LitElement) {\n static styles = [\n css`\n :host {\n display: block;\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 @property({ type: Object, attribute: false })\n element: TemporalMixinInterface & LitElement = new EFTimegroup();\n\n @property({ type: Number })\n pixelsPerMs = 0.04;\n\n // Gutter styles represent the entire source media.\n // If there is no trim, then the gutter and trim portion are the same.\n get gutterStyles() {\n return {\n position: \"relative\",\n left: `${this.pixelsPerMs * (this.element.startTimeWithinParentMs - this.element.sourceStartMs)}px`,\n width: `${this.pixelsPerMs * (this.element.intrinsicDurationMs ?? this.element.durationMs)}px`,\n };\n }\n\n // Trim portion is the section of source that will be placed in the timeline\n // If there is no trim, then the gutter and trim portion are the same.\n get trimPortionStyles() {\n return {\n width: `${this.pixelsPerMs * this.element.durationMs}px`,\n left: `${this.pixelsPerMs * this.element.sourceStartMs}px`,\n };\n }\n\n render() {\n return html`<div style=${styleMap(this.gutterStyles)}>\n <div\n style=\"background-color: var(--filmstrip-bg);\"\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=\"border-outset relative mb-[1px] block h-[1.1rem] text-nowrap border text-sm\"\n style=${styleMap({\n ...this.trimPortionStyles,\n backgroundColor: this.isFocused\n ? \"var(--filmstrip-item-focused)\"\n : \"var(--filmstrip-item-bg)\",\n borderColor: \"var(--filmstrip-border)\",\n })}\n >\n ${this.animations()}\n </div>\n </div>\n ${this.renderChildren()}\n </div>`;\n }\n\n renderChildren(): Array<TemplateResult<1> | typeof nothing> | typeof nothing {\n return renderFilmstripChildren(\n Array.from(this.element.children),\n this.pixelsPerMs,\n this.hideSelectors,\n this.showSelectors,\n );\n }\n\n @property({ type: Array, attribute: false })\n hideSelectors?: string[];\n\n @property({ type: Array, attribute: false })\n showSelectors?: string[];\n\n contents() {\n return html``;\n }\n\n animations() {\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 <!-- <div class=\"text-nowrap\">${Array.from(properties).join(\" \")}</div> -->\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 protected filmstripController?: ElementFilmstripController;\n\n update(changedProperties: Map<string | number | symbol, unknown>) {\n if (\n changedProperties.has(\"element\") &&\n this.element instanceof LitElement\n ) {\n this.filmstripController?.remove();\n this.filmstripController = new ElementFilmstripController(\n this.element,\n this,\n );\n }\n super.update(changedProperties);\n }\n}\n\n@customElement(\"ef-audio-filmstrip\")\nexport class EFAudioFilmstrip extends FilmstripItem {\n contents() {\n return html``;\n }\n}\n\n@customElement(\"ef-video-filmstrip\")\nexport class EFVideoFilmstrip extends FilmstripItem {\n contents() {\n return html` 📼 `;\n }\n}\n\n@customElement(\"ef-captions-filmstrip\")\nexport class EFCaptionsFilmstrip extends FilmstripItem {\n render() {\n const captions = this.element as EFCaptions;\n const captionsData = captions.unifiedCaptionsDataTask.value;\n\n return html`<div style=${styleMap(this.gutterStyles)}>\n <div\n class=\"relative\"\n style=\"background-color: var(--filmstrip-bg);\"\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=\"border-outset relative mb-[1px] block h-[1.1rem] text-nowrap border text-sm overflow-hidden\"\n style=${styleMap({\n ...this.trimPortionStyles,\n backgroundColor: this.isFocused\n ? \"var(--filmstrip-item-focused)\"\n : \"var(--filmstrip-item-bg)\",\n borderColor: \"var(--filmstrip-border)\",\n })}\n >\n 📝 ${this.renderCaptionsData(captionsData)}\n </div>\n </div>\n ${this.renderChildren()}\n </div>`;\n }\n\n renderCaptionsData(captionsData: Caption | null | undefined) {\n if (!captionsData) {\n return html``;\n }\n\n // Get current time for highlighting active elements\n const captions = this.element as EFCaptions;\n const rootTimegroup = captions.rootTimegroup;\n const currentTimeMs = rootTimegroup?.currentTimeMs || 0;\n const captionsLocalTimeMs = currentTimeMs - captions.startTimeMs;\n const captionsLocalTimeSec = captionsLocalTimeMs / 1000;\n\n // Show all segments with text content, let them clip naturally\n const segmentElements = captionsData.segments.map((segment) => {\n const isActive =\n captionsLocalTimeSec >= segment.start &&\n captionsLocalTimeSec < segment.end;\n\n return html`<div\n class=\"absolute border text-xs overflow-hidden flex items-center ${isActive ? \"font-bold z-[5]\" : \"\"}\"\n style=${styleMap({\n left: `${this.pixelsPerMs * segment.start * 1000}px`,\n width: `${this.pixelsPerMs * (segment.end - segment.start) * 1000}px`,\n height: \"100%\",\n top: \"0px\",\n backgroundColor: isActive\n ? \"var(--filmstrip-segment-bg)\"\n : \"var(--filmstrip-item-bg)\",\n borderColor: isActive\n ? \"var(--filmstrip-segment-border)\"\n : \"var(--filmstrip-border)\",\n })}\n title=\"Segment: '${segment.text}' (${segment.start}s - ${segment.end}s)\"\n >\n <span class=\"px-0.5 text-[8px] ${isActive ? \"font-bold\" : \"\"}\">${segment.text}</span>\n </div>`;\n });\n\n return html`${segmentElements}`;\n }\n\n renderChildren(): Array<TemplateResult<1> | typeof nothing> | typeof nothing {\n // Also render normal DOM children (like ef-captions-active-word elements)\n return renderFilmstripChildren(\n Array.from(this.element.children),\n this.pixelsPerMs,\n this.hideSelectors,\n this.showSelectors,\n );\n }\n}\n\n@customElement(\"ef-captions-active-word-filmstrip\")\nexport class EFCaptionsActiveWordFilmstrip extends FilmstripItem {\n get captionsTrackStyles() {\n const parentCaptions = this.element.closest(\"ef-captions\") as EFCaptions;\n return {\n position: \"relative\",\n left: `${this.pixelsPerMs * (parentCaptions?.startTimeWithinParentMs || 0)}px`,\n width: `${this.pixelsPerMs * (parentCaptions?.durationMs || 0)}px`,\n };\n }\n\n render() {\n // Get parent captions element and its data\n const parentCaptions = this.element.closest(\"ef-captions\") as EFCaptions;\n const captionsData = parentCaptions?.unifiedCaptionsDataTask.value;\n\n if (!captionsData) {\n return html`<div style=${styleMap(this.captionsTrackStyles)}>\n <div class=\"border h-[1.1rem] mb-[1px] text-xs\" style=\"background-color: var(--filmstrip-bg); border-color: var(--filmstrip-border);\">\n 🗣️ Active Word\n </div>\n </div>`;\n }\n\n // Get current time for highlighting\n const rootTimegroup = parentCaptions.rootTimegroup;\n const currentTimeMs = rootTimegroup?.currentTimeMs || 0;\n const captionsLocalTimeMs = currentTimeMs - parentCaptions.startTimeMs;\n const captionsLocalTimeSec = captionsLocalTimeMs / 1000;\n\n return html`<div style=${styleMap(this.captionsTrackStyles)}>\n <div class=\"relative border h-[1.1rem] mb-[1px] w-full\" style=\"background-color: var(--filmstrip-bg); border-color: var(--filmstrip-border);\">\n ${captionsData.word_segments.map((word) => {\n const isCurrentlyActive =\n captionsLocalTimeSec >= word.start &&\n captionsLocalTimeSec < word.end;\n\n return html`<div\n class=\"absolute border text-xs overflow-visible flex items-center ${isCurrentlyActive ? \"font-bold z-[5]\" : \"\"}\"\n style=${styleMap({\n left: `${this.pixelsPerMs * word.start * 1000}px`,\n width: `${this.pixelsPerMs * (word.end - word.start) * 1000}px`,\n height: \"100%\",\n top: \"0px\",\n backgroundColor: isCurrentlyActive\n ? \"var(--filmstrip-caption-bg)\"\n : \"var(--filmstrip-item-bg)\",\n borderColor: isCurrentlyActive\n ? \"var(--filmstrip-caption-border)\"\n : \"var(--filmstrip-border)\",\n })}\n title=\"Word: '${word.text}' (${word.start}s - ${word.end}s)\"\n >\n ${isCurrentlyActive ? html`<span class=\"px-0.5 text-[8px] font-bold whitespace-nowrap\" style=\"background-color: var(--filmstrip-caption-bg);\">${word.text.trim()}</span>` : \"\"}\n </div>`;\n })}\n </div>\n </div>`;\n }\n}\n\n@customElement(\"ef-captions-segment-filmstrip\")\nexport class EFCaptionsSegmentFilmstrip extends FilmstripItem {\n get captionsTrackStyles() {\n const parentCaptions = this.element.closest(\"ef-captions\") as EFCaptions;\n return {\n position: \"relative\",\n left: `${this.pixelsPerMs * (parentCaptions?.startTimeWithinParentMs || 0)}px`,\n width: `${this.pixelsPerMs * (parentCaptions?.durationMs || 0)}px`,\n };\n }\n\n render() {\n // Get parent captions element and its data\n const parentCaptions = this.element.closest(\"ef-captions\") as EFCaptions;\n const captionsData = parentCaptions?.unifiedCaptionsDataTask.value;\n\n if (!captionsData) {\n return html`<div style=${styleMap(this.captionsTrackStyles)}>\n <div class=\"border h-[1.1rem] mb-[1px] text-xs\" style=\"background-color: var(--filmstrip-bg); border-color: var(--filmstrip-border);\">\n 📄 Segment\n </div>\n </div>`;\n }\n\n // Get current time for highlighting\n const rootTimegroup = parentCaptions.rootTimegroup;\n const currentTimeMs = rootTimegroup?.currentTimeMs || 0;\n const captionsLocalTimeMs = currentTimeMs - parentCaptions.startTimeMs;\n const captionsLocalTimeSec = captionsLocalTimeMs / 1000;\n\n return html`<div style=${styleMap(this.captionsTrackStyles)}>\n <div class=\"relative border h-[1.1rem] mb-[1px] w-full\" style=\"background-color: var(--filmstrip-bg); border-color: var(--filmstrip-border);\">\n ${captionsData.segments.map((segment) => {\n const isCurrentlyActive =\n captionsLocalTimeSec >= segment.start &&\n captionsLocalTimeSec < segment.end;\n\n return html`<div\n class=\"absolute border text-xs overflow-visible flex items-center ${isCurrentlyActive ? \"font-bold z-[5]\" : \"\"}\"\n style=${styleMap({\n left: `${this.pixelsPerMs * segment.start * 1000}px`,\n width: `${this.pixelsPerMs * (segment.end - segment.start) * 1000}px`,\n height: \"100%\",\n top: \"0px\",\n backgroundColor: isCurrentlyActive\n ? \"var(--filmstrip-segment-bg)\"\n : \"var(--filmstrip-item-bg)\",\n borderColor: isCurrentlyActive\n ? \"var(--filmstrip-segment-border)\"\n : \"var(--filmstrip-border)\",\n })}\n title=\"Segment: '${segment.text}' (${segment.start}s - ${segment.end}s)\"\n >\n ${isCurrentlyActive ? html`<span class=\"px-0.5 text-[8px] font-bold whitespace-nowrap\" style=\"background-color: var(--filmstrip-segment-bg);\">${segment.text}</span>` : \"\"}\n </div>`;\n })}\n </div>\n </div>`;\n }\n}\n\n@customElement(\"ef-captions-before-word-filmstrip\")\nexport class EFCaptionsBeforeWordFilmstrip extends FilmstripItem {\n get captionsTrackStyles() {\n const parentCaptions = this.element.closest(\"ef-captions\") as EFCaptions;\n return {\n position: \"relative\",\n left: `${this.pixelsPerMs * (parentCaptions?.startTimeWithinParentMs || 0)}px`,\n width: `${this.pixelsPerMs * (parentCaptions?.durationMs || 0)}px`,\n };\n }\n\n render() {\n // Get parent captions element and its data\n const parentCaptions = this.element.closest(\"ef-captions\") as EFCaptions;\n const captionsData = parentCaptions?.unifiedCaptionsDataTask.value;\n\n if (!captionsData) {\n return html`<div style=${styleMap(this.captionsTrackStyles)}>\n <div class=\"border h-[1.1rem] mb-[1px] text-xs\" style=\"background-color: var(--filmstrip-bg); border-color: var(--filmstrip-border);\">\n ⬅️ Before\n </div>\n </div>`;\n }\n\n // Get current time for highlighting\n const rootTimegroup = parentCaptions.rootTimegroup;\n const currentTimeMs = rootTimegroup?.currentTimeMs || 0;\n const captionsLocalTimeMs = currentTimeMs - parentCaptions.startTimeMs;\n const captionsLocalTimeSec = captionsLocalTimeMs / 1000;\n\n return html`<div style=${styleMap(this.captionsTrackStyles)}>\n <div class=\"relative border h-[1.1rem] mb-[1px] w-full\" style=\"background-color: var(--filmstrip-bg); border-color: var(--filmstrip-border);\">\n ${captionsData.word_segments.map((word) => {\n const isCurrentlyActive =\n captionsLocalTimeSec >= word.start &&\n captionsLocalTimeSec < word.end;\n\n return html`<div\n class=\"absolute border text-xs overflow-visible flex items-center ${isCurrentlyActive ? \"font-bold z-[5]\" : \"\"}\"\n style=${styleMap({\n left: `${this.pixelsPerMs * word.start * 1000}px`,\n width: `${this.pixelsPerMs * (word.end - word.start) * 1000}px`,\n height: \"100%\",\n top: \"0px\",\n backgroundColor: isCurrentlyActive\n ? \"var(--filmstrip-caption-bg)\"\n : \"var(--filmstrip-waveform-bg)\",\n borderColor: isCurrentlyActive\n ? \"var(--filmstrip-caption-border)\"\n : \"var(--filmstrip-waveform-border)\",\n })}\n title=\"Word: '${word.text}' (${word.start}s - ${word.end}s)\"\n >\n <!-- No text for before tracks - they're redundant -->\n </div>`;\n })}\n </div>\n </div>`;\n }\n}\n\n@customElement(\"ef-captions-after-word-filmstrip\")\nexport class EFCaptionsAfterWordFilmstrip extends FilmstripItem {\n get captionsTrackStyles() {\n const parentCaptions = this.element.closest(\"ef-captions\") as EFCaptions;\n return {\n position: \"relative\",\n left: `${this.pixelsPerMs * (parentCaptions?.startTimeWithinParentMs || 0)}px`,\n width: `${this.pixelsPerMs * (parentCaptions?.durationMs || 0)}px`,\n };\n }\n\n render() {\n // Get parent captions element and its data\n const parentCaptions = this.element.closest(\"ef-captions\") as EFCaptions;\n const captionsData = parentCaptions?.unifiedCaptionsDataTask.value;\n\n if (!captionsData) {\n return html`<div style=${styleMap(this.captionsTrackStyles)}>\n <div class=\"border h-[1.1rem] mb-[1px] text-xs\" style=\"background-color: var(--filmstrip-bg); border-color: var(--filmstrip-border);\">\n ➡️ After\n </div>\n </div>`;\n }\n\n // Get current time for highlighting\n const rootTimegroup = parentCaptions.rootTimegroup;\n const currentTimeMs = rootTimegroup?.currentTimeMs || 0;\n const captionsLocalTimeMs = currentTimeMs - parentCaptions.startTimeMs;\n const captionsLocalTimeSec = captionsLocalTimeMs / 1000;\n\n return html`<div style=${styleMap(this.captionsTrackStyles)}>\n <div class=\"relative border h-[1.1rem] mb-[1px] w-full\" style=\"background-color: var(--filmstrip-bg); border-color: var(--filmstrip-border);\">\n ${captionsData.word_segments.map((word) => {\n const isCurrentlyActive =\n captionsLocalTimeSec >= word.start &&\n captionsLocalTimeSec < word.end;\n\n return html`<div\n class=\"absolute border text-xs overflow-visible flex items-center ${isCurrentlyActive ? \"font-bold z-[5]\" : \"\"}\"\n style=${styleMap({\n left: `${this.pixelsPerMs * word.start * 1000}px`,\n width: `${this.pixelsPerMs * (word.end - word.start) * 1000}px`,\n height: \"100%\",\n top: \"0px\",\n backgroundColor: isCurrentlyActive\n ? \"var(--filmstrip-caption-bg)\"\n : \"var(--filmstrip-waveform-bg)\",\n borderColor: isCurrentlyActive\n ? \"var(--filmstrip-caption-border)\"\n : \"var(--filmstrip-waveform-border)\",\n })}\n title=\"Word: '${word.text}' (${word.start}s - ${word.end}s)\"\n >\n <!-- No text for after tracks - they're redundant -->\n </div>`;\n })}\n </div>\n </div>`;\n }\n}\n\n@customElement(\"ef-waveform-filmstrip\")\nexport class EFWaveformFilmstrip extends FilmstripItem {\n contents() {\n return html` 🌊 `;\n }\n\n renderChildren(): typeof nothing {\n return nothing;\n }\n}\n\n@customElement(\"ef-text-filmstrip\")\nexport class EFTextFilmstrip extends FilmstripItem {\n render() {\n const text = this.element as EFText;\n const segments = Array.from(text.querySelectorAll(\"ef-text-segment\"));\n\n return html`<div style=${styleMap(this.gutterStyles)}>\n <div\n class=\"relative\"\n style=\"background-color: var(--filmstrip-bg);\"\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=\"border-outset relative mb-[1px] block h-[1.1rem] text-nowrap border text-sm overflow-hidden\"\n style=${styleMap({\n ...this.trimPortionStyles,\n backgroundColor: this.isFocused\n ? \"var(--filmstrip-item-focused)\"\n : \"var(--filmstrip-item-bg)\",\n borderColor: \"var(--filmstrip-border)\",\n })}\n >\n 📄 ${this.renderTextSegments(segments)}\n </div>\n </div>\n ${this.renderChildren()}\n </div>`;\n }\n\n renderTextSegments(segments: EFTextSegment[]) {\n if (segments.length === 0) {\n return html``;\n }\n\n // Get current time for highlighting active segments\n const text = this.element as EFText;\n const rootTimegroup = text.rootTimegroup;\n const currentTimeMs = rootTimegroup?.currentTimeMs || 0;\n const textLocalTimeMs = currentTimeMs - text.startTimeMs;\n\n return segments.map((segment) => {\n const isActive =\n textLocalTimeMs >= segment.segmentStartMs &&\n textLocalTimeMs < segment.segmentEndMs;\n\n return html`<div\n class=\"absolute border text-xs overflow-hidden flex items-center ${isActive ? \"font-bold z-[5]\" : \"\"}\"\n style=${styleMap({\n left: `${this.pixelsPerMs * segment.segmentStartMs}px`,\n width: `${this.pixelsPerMs * (segment.segmentEndMs - segment.segmentStartMs)}px`,\n height: \"100%\",\n top: \"0px\",\n backgroundColor: isActive\n ? \"var(--filmstrip-segment-bg)\"\n : \"var(--filmstrip-item-bg)\",\n borderColor: isActive\n ? \"var(--filmstrip-segment-border)\"\n : \"var(--filmstrip-border)\",\n })}\n title=\"Segment: '${segment.segmentText}' (${segment.segmentStartMs}ms - ${segment.segmentEndMs}ms)\"\n >\n <span class=\"px-0.5 text-[8px] ${isActive ? \"font-bold\" : \"\"}\">${segment.segmentText}</span>\n </div>`;\n });\n }\n\n renderChildren(): Array<TemplateResult<1> | typeof nothing> | typeof nothing {\n return renderFilmstripChildren(\n Array.from(this.element.children),\n this.pixelsPerMs,\n this.hideSelectors,\n this.showSelectors,\n );\n }\n}\n\n@customElement(\"ef-text-segment-filmstrip\")\nexport class EFTextSegmentFilmstrip extends FilmstripItem {\n get textTrackStyles() {\n const parentText = this.element.closest(\"ef-text\") as EFText;\n return {\n position: \"relative\",\n left: `${this.pixelsPerMs * (parentText?.startTimeWithinParentMs || 0)}px`,\n width: `${this.pixelsPerMs * (parentText?.durationMs || 0)}px`,\n };\n }\n\n render() {\n const segment = this.element as EFTextSegment;\n const parentText = segment.closest(\"ef-text\") as EFText;\n\n if (!parentText) {\n return html`<div style=${styleMap(this.textTrackStyles)}>\n <div class=\"border h-[1.1rem] mb-[1px] text-xs\" style=\"background-color: var(--filmstrip-bg); border-color: var(--filmstrip-border);\">\n 📄 Text Segment\n </div>\n </div>`;\n }\n\n // Get current time for highlighting\n const rootTimegroup = parentText.rootTimegroup;\n const currentTimeMs = rootTimegroup?.currentTimeMs || 0;\n const textLocalTimeMs = currentTimeMs - parentText.startTimeMs;\n\n const isCurrentlyActive =\n textLocalTimeMs >= segment.segmentStartMs &&\n textLocalTimeMs < segment.segmentEndMs;\n\n return html`<div style=${styleMap(this.textTrackStyles)}>\n <div class=\"relative border h-[1.1rem] mb-[1px] w-full\" style=\"background-color: var(--filmstrip-bg); border-color: var(--filmstrip-border);\">\n <div\n class=\"absolute border text-xs overflow-visible flex items-center ${isCurrentlyActive ? \"font-bold z-[5]\" : \"\"}\"\n style=${styleMap({\n left: `${this.pixelsPerMs * segment.segmentStartMs}px`,\n width: `${this.pixelsPerMs * (segment.segmentEndMs - segment.segmentStartMs)}px`,\n height: \"100%\",\n top: \"0px\",\n backgroundColor: isCurrentlyActive\n ? \"var(--filmstrip-caption-bg)\"\n : \"var(--filmstrip-item-bg)\",\n borderColor: isCurrentlyActive\n ? \"var(--filmstrip-caption-border)\"\n : \"var(--filmstrip-border)\",\n })}\n title=\"Segment: '${segment.segmentText}' (${segment.segmentStartMs}ms - ${segment.segmentEndMs}ms)\"\n >\n ${isCurrentlyActive ? html`<span class=\"px-0.5 text-[8px] font-bold whitespace-nowrap\" style=\"background-color: var(--filmstrip-caption-bg);\">${segment.segmentText}</span>` : \"\"}\n </div>\n </div>\n </div>`;\n }\n}\n\n@customElement(\"ef-image-filmstrip\")\nexport class EFImageFilmstrip extends FilmstripItem {\n contents() {\n return html` 🖼️ `;\n }\n}\n\n@customElement(\"ef-timegroup-filmstrip\")\nexport class EFTimegroupFilmstrip extends FilmstripItem {\n contents() {\n return html`\n <span>TIME GROUP</span>\n ${renderFilmstripChildren(\n Array.from(this.element.children || []),\n this.pixelsPerMs,\n this.hideSelectors,\n this.showSelectors,\n )}\n </div>\n `;\n }\n}\n\n@customElement(\"ef-html-filmstrip\")\nexport class EFHTMLFilmstrip extends FilmstripItem {\n contents() {\n return html`\n <span>${this.element.tagName}</span>\n ${renderFilmstripChildren(\n Array.from(this.element.children || []),\n this.pixelsPerMs,\n this.hideSelectors,\n this.showSelectors,\n )}\n `;\n }\n}\n\n@customElement(\"ef-hierarchy-item\")\nclass EFHierarchyItem<\n ElementType extends HTMLElement = HTMLElement,\n> extends TWMixin(LitElement) {\n @property({ type: Object, attribute: false })\n // @ts-expect-error This could be initialzed with any HTMLElement\n element: ElementType = new EFTimegroup();\n\n @consume({ context: focusContext })\n focusContext?: FocusContext;\n\n @consume({ context: focusedElementContext, subscribe: true })\n focusedElement?: HTMLElement | null;\n\n @property({ type: Array, attribute: false })\n hideSelectors?: string[];\n\n @property({ type: Array, attribute: false })\n showSelectors?: string[];\n\n get icon(): TemplateResult<1> | string {\n return \"📼\";\n }\n\n get isFocused() {\n return this.element && this.focusContext?.focusedElement === this.element;\n }\n\n displayLabel(): TemplateResult<1> | string | typeof nothing {\n return nothing;\n }\n\n render() {\n return html` \n <div>\n <div\n class=\"peer flex h-[1.1rem] items-center overflow-hidden text-nowrap border pl-2 text-xs font-mono\"\n style=${styleMap({\n backgroundColor: this.isFocused\n ? \"var(--filmstrip-timegroup-focused)\"\n : \"var(--filmstrip-timegroup-bg)\",\n borderColor: \"var(--filmstrip-border)\",\n })}\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 ${this.icon} ${this.displayLabel()}\n </div>\n <div\n class=\"p-[1px] pb-0 pl-2 pr-0 peer-hover-container peer-data-container\"\n style=${styleMap({\n backgroundColor: this.isFocused\n ? \"var(--filmstrip-bg)\"\n : \"transparent\",\n })}\n >\n ${this.renderChildren()}\n </div>\n </div>`;\n }\n\n renderChildren(): Array<TemplateResult<1> | typeof nothing> | typeof nothing {\n return renderHierarchyChildren(\n Array.from(this.element.children),\n this.hideSelectors,\n this.showSelectors,\n );\n }\n}\n\n@customElement(\"ef-timegroup-hierarchy-item\")\nclass EFTimegroupHierarchyItem extends EFHierarchyItem<EFTimegroup> {\n get icon() {\n return \"🕒\";\n }\n\n displayLabel(): string | TemplateResult<1> | typeof nothing {\n return this.element.mode ?? \"(no mode)\";\n }\n}\n\n@customElement(\"ef-audio-hierarchy-item\")\nclass EFAudioHierarchyItem extends EFHierarchyItem<EFAudio> {\n get icon() {\n return \"🔊\";\n }\n\n displayLabel() {\n return this.element.src ?? \"(no src)\";\n }\n}\n\n@customElement(\"ef-video-hierarchy-item\")\nclass EFVideoHierarchyItem extends EFHierarchyItem<EFVideo> {\n get icon() {\n return \"📼\";\n }\n\n displayLabel() {\n return this.element.src ?? \"(no src)\";\n }\n}\n\n@customElement(\"ef-captions-hierarchy-item\")\nclass EFCaptionsHierarchyItem extends EFHierarchyItem {\n get icon() {\n return \"📝 Captions\";\n }\n}\n\n@customElement(\"ef-captions-active-word-hierarchy-item\")\nclass EFCaptionsActiveWordHierarchyItem extends EFHierarchyItem {\n get icon() {\n return \"🗣️ Active Word\";\n }\n}\n\n@customElement(\"ef-text-hierarchy-item\")\nclass EFTextHierarchyItem extends EFHierarchyItem {\n get icon() {\n return \"📄 Text\";\n }\n}\n\n@customElement(\"ef-text-segment-hierarchy-item\")\nclass EFTextSegmentHierarchyItem extends EFHierarchyItem {\n get icon() {\n return \"📄 Segment\";\n }\n}\n\n@customElement(\"ef-waveform-hierarchy-item\")\nclass EFWaveformHierarchyItem extends EFHierarchyItem {\n get icon() {\n return \"🌊\";\n }\n\n renderChildren(): typeof nothing {\n return nothing;\n }\n}\n\n@customElement(\"ef-image-hierarchy-item\")\nclass EFImageHierarchyItem extends EFHierarchyItem<EFImage> {\n get icon() {\n return \"🖼️\";\n }\n\n displayLabel() {\n return this.element.src ?? \"(no src)\";\n }\n}\n\n@customElement(\"ef-html-hierarchy-item\")\nclass EFHTMLHierarchyItem extends EFHierarchyItem {\n get icon() {\n return html`<code>${`<${this.element.tagName.toLowerCase()}>`}</code>`;\n }\n}\n\nconst shouldRenderElement = (\n element: Element,\n hideSelectors?: string[],\n showSelectors?: string[],\n): boolean => {\n if (element instanceof HTMLElement && element.dataset?.efHidden) {\n return false;\n }\n\n // If show selectors are provided (allowlist mode), only render if matches\n if (showSelectors && showSelectors.length > 0) {\n return showSelectors.some((selector) => {\n try {\n return element.matches(selector);\n } catch {\n return false;\n }\n });\n }\n\n // If hide selectors are provided, don't render if matches\n if (hideSelectors && hideSelectors.length > 0) {\n return !hideSelectors.some((selector) => {\n try {\n return element.matches(selector);\n } catch {\n return false;\n }\n });\n }\n\n // No filters, render everything\n return true;\n};\n\nconst renderHierarchyChildren = (\n children: Element[],\n hideSelectors?: string[],\n showSelectors?: string[],\n skipRootFiltering = false,\n): Array<TemplateResult<1> | typeof nothing> => {\n return children.map((child) => {\n if (\n !skipRootFiltering &&\n !shouldRenderElement(child, hideSelectors, showSelectors)\n ) {\n return nothing;\n }\n\n if (child instanceof EFTimegroup) {\n return html`<ef-timegroup-hierarchy-item\n .element=${child}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-timegroup-hierarchy-item>`;\n }\n if (child instanceof EFImage) {\n return html`<ef-image-hierarchy-item\n .element=${child}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-image-hierarchy-item>`;\n }\n if (child instanceof EFAudio) {\n return html`<ef-audio-hierarchy-item\n .element=${child}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-audio-hierarchy-item>`;\n }\n if (child instanceof EFVideo) {\n return html`<ef-video-hierarchy-item\n .element=${child}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-video-hierarchy-item>`;\n }\n if (child instanceof EFCaptions) {\n return html`<ef-captions-hierarchy-item\n .element=${child}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-captions-hierarchy-item>`;\n }\n if (child instanceof EFCaptionsActiveWord) {\n return html`<ef-captions-active-word-hierarchy-item\n .element=${child}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-captions-active-word-hierarchy-item>`;\n }\n if (child instanceof EFText) {\n return html`<ef-text-hierarchy-item\n .element=${child}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-text-hierarchy-item>`;\n }\n if (child instanceof EFTextSegment) {\n return html`<ef-text-segment-hierarchy-item\n .element=${child}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-text-segment-hierarchy-item>`;\n }\n if (child instanceof EFWaveform) {\n return html`<ef-waveform-hierarchy-item\n .element=${child}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-waveform-hierarchy-item>`;\n }\n return html`<ef-html-hierarchy-item\n .element=${child}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-html-hierarchy-item>`;\n });\n};\n\nconst renderFilmstripChildren = (\n children: Element[],\n pixelsPerMs: number,\n hideSelectors?: string[],\n showSelectors?: string[],\n skipRootFiltering = false,\n): Array<TemplateResult<1> | typeof nothing> => {\n return children.map((child) => {\n if (\n !skipRootFiltering &&\n !shouldRenderElement(child, hideSelectors, showSelectors)\n ) {\n return nothing;\n }\n\n if (child instanceof EFTimegroup) {\n return html`<ef-timegroup-filmstrip\n .element=${child}\n .pixelsPerMs=${pixelsPerMs}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n >\n </ef-timegroup-filmstrip>`;\n }\n if (child instanceof EFImage) {\n return html`<ef-image-filmstrip\n .element=${child}\n .pixelsPerMs=${pixelsPerMs}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-image-filmstrip>`;\n }\n if (child instanceof EFAudio) {\n return html`<ef-audio-filmstrip\n .element=${child}\n .pixelsPerMs=${pixelsPerMs}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-audio-filmstrip>`;\n }\n if (child instanceof EFVideo) {\n return html`<ef-video-filmstrip\n .element=${child}\n .pixelsPerMs=${pixelsPerMs}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-video-filmstrip>`;\n }\n if (child instanceof EFCaptions) {\n return html`<ef-captions-filmstrip\n .element=${child}\n .pixelsPerMs=${pixelsPerMs}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-captions-filmstrip>`;\n }\n if (child instanceof EFCaptionsActiveWord) {\n return html`<ef-captions-active-word-filmstrip\n .element=${child}\n .pixelsPerMs=${pixelsPerMs}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-captions-active-word-filmstrip>`;\n }\n if (child instanceof EFText) {\n return html`<ef-text-filmstrip\n .element=${child}\n .pixelsPerMs=${pixelsPerMs}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-text-filmstrip>`;\n }\n if (child instanceof EFTextSegment) {\n return html`<ef-text-segment-filmstrip\n .element=${child}\n .pixelsPerMs=${pixelsPerMs}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-text-segment-filmstrip>`;\n }\n if (child.tagName === \"EF-CAPTIONS-SEGMENT\") {\n return html`<ef-captions-segment-filmstrip\n .element=${child}\n .pixelsPerMs=${pixelsPerMs}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-captions-segment-filmstrip>`;\n }\n if (child.tagName === \"EF-CAPTIONS-BEFORE-ACTIVE-WORD\") {\n return html`<ef-captions-before-word-filmstrip\n .element=${child}\n .pixelsPerMs=${pixelsPerMs}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-captions-before-word-filmstrip>`;\n }\n if (child.tagName === \"EF-CAPTIONS-AFTER-ACTIVE-WORD\") {\n return html`<ef-captions-after-word-filmstrip\n .element=${child}\n .pixelsPerMs=${pixelsPerMs}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-captions-after-word-filmstrip>`;\n }\n if (child instanceof EFWaveform) {\n return html`<ef-waveform-filmstrip\n .element=${child}\n .pixelsPerMs=${pixelsPerMs}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-waveform-filmstrip>`;\n }\n return html`<ef-html-filmstrip\n .element=${child}\n .pixelsPerMs=${pixelsPerMs}\n .hideSelectors=${hideSelectors}\n .showSelectors=${showSelectors}\n ></ef-html-filmstrip>`;\n });\n};\n\n@customElement(\"ef-filmstrip\")\nexport class EFFilmstrip extends TWMixin(LitElement) {\n static styles = [\n css`\n :host {\n display: block;\n overflow: hidden;\n width: 100%;\n height: 100%;\n \n /* Light mode colors */\n --filmstrip-bg: rgb(203 213 225); /* slate-300 */\n --filmstrip-border: rgb(100 116 139); /* slate-500 */\n --filmstrip-item-bg: rgb(191 219 254); /* blue-200 */\n --filmstrip-item-focused: rgb(148 163 184); /* slate-400 */\n --filmstrip-animation-bg: rgb(59 130 246); /* blue-500 */\n --filmstrip-keyframe-bg: rgb(239 68 68); /* red-500 */\n --filmstrip-caption-bg: rgb(254 240 138); /* yellow-200 */\n --filmstrip-caption-border: rgb(234 179 8); /* yellow-500 */\n --filmstrip-segment-bg: rgb(187 247 208); /* green-200 */\n --filmstrip-segment-border: rgb(34 197 94); /* green-500 */\n --filmstrip-waveform-bg: rgb(250 245 255); /* purple-50 */\n --filmstrip-waveform-border: rgb(233 213 255); /* purple-200 */\n --filmstrip-timegroup-bg: rgb(226 232 240); /* slate-200 */\n --filmstrip-timegroup-hover: rgb(148 163 184); /* slate-400 */\n --filmstrip-timegroup-focused: rgb(148 163 184); /* slate-400 */\n --filmstrip-gutter-bg: rgb(241 245 249); /* slate-100 */\n --filmstrip-timeline-bg: rgb(226 232 240); /* slate-200 */\n --filmstrip-playhead: rgb(185 28 28); /* red-700 */\n }\n \n :host(.dark), :host-context(.dark) {\n /* Dark mode colors */\n --filmstrip-bg: rgb(71 85 105); /* slate-600 */\n --filmstrip-border: rgb(148 163 184); /* slate-400 */\n --filmstrip-item-bg: rgb(30 64 175); /* blue-800 */\n --filmstrip-item-focused: rgb(100 116 139); /* slate-500 */\n --filmstrip-animation-bg: rgb(96 165 250); /* blue-400 */\n --filmstrip-keyframe-bg: rgb(248 113 113); /* red-400 */\n --filmstrip-caption-bg: rgb(133 77 14); /* yellow-800 */\n --filmstrip-caption-border: rgb(250 204 21); /* yellow-400 */\n --filmstrip-segment-bg: rgb(22 101 52); /* green-800 */\n --filmstrip-segment-border: rgb(74 222 128); /* green-400 */\n --filmstrip-waveform-bg: rgb(88 28 135); /* purple-900 */\n --filmstrip-waveform-border: rgb(126 34 206); /* purple-700 */\n --filmstrip-timegroup-bg: rgb(51 65 85); /* slate-700 */\n --filmstrip-timegroup-hover: rgb(100 116 139); /* slate-500 */\n --filmstrip-timegroup-focused: rgb(100 116 139); /* slate-500 */\n --filmstrip-gutter-bg: rgb(30 41 59); /* slate-800 */\n --filmstrip-timeline-bg: rgb(51 65 85); /* slate-700 */\n --filmstrip-playhead: rgb(239 68 68); /* red-500 */\n }\n `,\n ];\n @property({ type: Number })\n pixelsPerMs = 0.04;\n\n @property({ type: String })\n hide = \"\";\n\n @property({ type: String })\n show = \"\";\n\n get hideSelectors(): string[] | undefined {\n if (!this.hide) return undefined;\n return this.hide\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n\n get showSelectors(): string[] | undefined {\n if (!this.show) return undefined;\n return this.show\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n\n @state()\n scrubbing = false;\n\n private capturedPointerId: number | null = null;\n\n @state()\n timelineScrolltop = 0;\n\n @consume({ context: playingContext, subscribe: true })\n @state()\n playing?: boolean;\n\n @consume({ context: loopContext, subscribe: true })\n @state()\n loop?: boolean;\n\n timegroupController?: TimegroupController;\n\n @state()\n currentTimeMs = 0;\n\n @property({ type: Boolean, reflect: true, attribute: \"auto-scale\" })\n autoScale = false;\n\n private resizeObserver = new ResizeObserver(() => {\n if (this.autoScale) {\n this.updatePixelsPerMs();\n }\n });\n\n connectedCallback(): void {\n super.connectedCallback();\n this.#bindToTargetTimegroup();\n window.addEventListener(\"keypress\", this.#handleKeyPress);\n\n this.resizeObserver.observe(this);\n\n if (this.target) {\n this.#targetController = new TargetController(this);\n }\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n window.removeEventListener(\"keypress\", this.#handleKeyPress);\n this.resizeObserver.disconnect();\n }\n\n updatePixelsPerMs() {\n const target = this.targetTemporal;\n const gutter = this.gutterRef.value;\n if (target && gutter && gutter.clientWidth > 0) {\n this.pixelsPerMs = gutter.clientWidth / (target.durationMs || 1);\n }\n }\n\n #bindToTargetTimegroup() {\n if (this.timegroupController) {\n this.timegroupController.remove();\n }\n const target = this.targetTemporal;\n if (target) {\n this.timegroupController = new TimegroupController(\n target as EFTimegroup,\n this,\n );\n // Set the current time to the last saved time to avoid a cycle\n // where the filmstrip clobbers the time loaded from localStorage\n this.currentTimeMs = target.currentTimeMs;\n }\n }\n\n #handleKeyPress = (event: KeyboardEvent) => {\n // On spacebar, toggle playback\n if (event.key === \" \") {\n const [target] = event.composedPath();\n // CSS selector to match all interactive elements\n const interactiveSelector =\n \"input, textarea, button, select, a, [contenteditable]\";\n\n // Check if the event target or its ancestor matches an interactive element\n const closestInteractive = (target as HTMLElement | null)?.closest(\n interactiveSelector,\n );\n if (closestInteractive) {\n return;\n }\n event.preventDefault();\n if (this.#contextElement) {\n this.#contextElement.playing = !this.#contextElement.playing;\n }\n }\n };\n\n @eventOptions({ passive: false })\n syncGutterScroll() {\n if (this.gutter && this.hierarchyRef.value) {\n this.hierarchyRef.value.scrollTop = this.gutter.scrollTop;\n this.timelineScrolltop = this.gutter.scrollTop;\n }\n }\n\n @eventOptions({ passive: false })\n syncHierarchyScroll() {\n if (this.gutter && this.hierarchyRef.value) {\n this.gutter.scrollTop = this.hierarchyRef.value.scrollTop;\n this.timelineScrolltop = this.hierarchyRef.value.scrollTop;\n }\n }\n\n @eventOptions({ capture: false, passive: false })\n scrub(e: PointerEvent) {\n if (this.playing) {\n return;\n }\n if (!this.scrubbing) {\n return;\n }\n if (\n this.capturedPointerId !== null &&\n e.pointerId !== this.capturedPointerId\n ) {\n return;\n }\n e.preventDefault();\n e.stopPropagation();\n this.applyScrub(e);\n }\n\n @eventOptions({ capture: false, passive: false })\n startScrub(e: PointerEvent) {\n e.preventDefault();\n e.stopPropagation();\n this.scrubbing = true;\n this.capturedPointerId = e.pointerId;\n\n const target = e.currentTarget as HTMLElement;\n if (target) {\n try {\n target.setPointerCapture(e.pointerId);\n } catch (err) {\n // setPointerCapture may fail in some cases, continue anyway\n console.warn(\"Failed to set pointer capture:\", err);\n }\n }\n\n // Running scrub in the current microtask doesn't\n // result in an actual update. Not sure why.\n queueMicrotask(() => {\n this.applyScrub(e);\n });\n\n const handlePointerUp = (upEvent: PointerEvent) => {\n if (upEvent.pointerId === this.capturedPointerId) {\n upEvent.preventDefault();\n upEvent.stopPropagation();\n if (target) {\n try {\n target.releasePointerCapture(upEvent.pointerId);\n } catch (_err) {\n // releasePointerCapture may fail if capture was already lost\n }\n }\n this.capturedPointerId = null;\n this.scrubbing = false;\n removeEventListener(\"pointerup\", handlePointerUp);\n }\n };\n\n const handlePointerCancel = (cancelEvent: PointerEvent) => {\n if (cancelEvent.pointerId === this.capturedPointerId) {\n if (target) {\n try {\n target.releasePointerCapture(cancelEvent.pointerId);\n } catch (_err) {\n // releasePointerCapture may fail if capture was already lost\n }\n }\n this.capturedPointerId = null;\n this.scrubbing = false;\n removeEventListener(\"pointercancel\", handlePointerCancel);\n }\n };\n\n addEventListener(\"pointerup\", handlePointerUp, {\n once: true,\n passive: false,\n });\n addEventListener(\"pointercancel\", handlePointerCancel, {\n once: true,\n passive: false,\n });\n }\n\n @eventOptions({ passive: false, capture: false })\n handleContextMenu(e: Event) {\n if (this.scrubbing) {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n\n applyScrub(e: PointerEvent) {\n const gutter = this.shadowRoot?.querySelector(\"#gutter\");\n if (!gutter) {\n return;\n }\n const rect = gutter.getBoundingClientRect();\n if (this.targetTemporal) {\n const layerX = e.pageX - rect.left + gutter.scrollLeft;\n const scrubTimeMs = layerX / this.pixelsPerMs;\n this.targetTemporal.currentTimeMs = scrubTimeMs;\n }\n }\n\n @eventOptions({ passive: false })\n scrollScrub(e: WheelEvent) {\n if (this.targetTemporal && this.gutter && !this.playing) {\n if (e.deltaX !== 0) {\n e.preventDefault(); // Prevent default side scroll behavior only\n }\n // Avoid over-scrolling to the left\n if (\n this.gutterRef.value &&\n this.gutterRef.value.scrollLeft === 0 &&\n e.deltaX < 0\n ) {\n this.gutter.scrollBy(0, e.deltaY);\n return;\n }\n\n // Avoid over-scrolling to the right\n if (\n this.gutter.scrollWidth - this.gutter.scrollLeft ===\n this.gutter.clientWidth &&\n e.deltaX > 0\n ) {\n this.gutter.scrollBy(0, e.deltaY);\n return;\n }\n\n if (this) {\n this.gutter.scrollBy(e.deltaX, e.deltaY);\n this.targetTemporal.currentTimeMs += e.deltaX / this.pixelsPerMs;\n }\n }\n }\n\n gutterRef = createRef<HTMLDivElement>();\n hierarchyRef = createRef<HTMLDivElement>();\n playheadRef = createRef<HTMLDivElement>();\n\n get gutter() {\n return this.gutterRef.value;\n }\n\n render() {\n const target = this.targetTemporal;\n\n return html` <div\n class=\"grid h-full\"\n style=${styleMap({\n gridTemplateColumns: \"200px 1fr\",\n gridTemplateRows: \"1.5rem 1fr\",\n backgroundColor: \"var(--filmstrip-gutter-bg)\",\n })}\n >\n <div\n class=\"z-20 col-span-2 shadow\"\n style=\"background-color: var(--filmstrip-gutter-bg); border-bottom-color: var(--filmstrip-border);\"\n >\n ${\n !this.autoScale\n ? html`<input\n type=\"range\"\n .value=${this.pixelsPerMs}\n min=\"0.01\"\n max=\"0.1\"\n step=\"0.001\"\n @input=${(e: Event) => {\n const target = e.target as HTMLInputElement;\n this.pixelsPerMs = Number.parseFloat(target.value);\n }}\n />`\n : nothing\n }\n <code>${msToTimeCode(this.currentTimeMs, true)} </code> /\n <code>${msToTimeCode(target?.durationMs ?? 0, true)}</code>\n <ef-toggle-play class=\"inline-block mx-2\">\n <div slot=\"pause\"> \n <button>⏸️</button>\n </div>\n <div slot=\"play\">\n <button>▶️</button>\n </div>\n </ef-toggle-play>\n <ef-toggle-loop><button>${this.loop ? \"🔁\" : html`<span class=\"opacity-50 line-through\">🔁</span>`}</button></ef-toggle-loop>\n </div>\n <div\n class=\"z-10 pl-1 pr-1 pt-[8px] shadow overflow-auto\"\n ${ref(this.hierarchyRef)}\n @scroll=${this.syncHierarchyScroll}\n >\n ${renderHierarchyChildren(\n target ? ([target] as unknown as Element[]) : [],\n this.hideSelectors,\n this.showSelectors,\n true,\n )}\n </div>\n <div\n class=\"flex h-full w-full cursor-crosshair overflow-auto pt-[8px] touch-pan-x\"\n style=\"background-color: var(--filmstrip-timeline-bg);\"\n id=\"gutter\"\n ${ref(this.gutterRef)}\n @scroll=${this.syncGutterScroll}\n @wheel=${this.scrollScrub}\n >\n <div\n class=\"relative h-full w-full touch-none\"\n style=\"width: ${this.pixelsPerMs * (target?.durationMs ?? 0)}px; touch-action: none; user-select: none;\"\n @pointermove=${this.scrub}\n @pointerdown=${this.startScrub}\n @contextmenu=${this.handleContextMenu}\n >\n <div\n class=\"border-red pointer-events-none absolute z-[20] h-full w-[2px] border-r-2\"\n style=${styleMap({\n left: `${this.pixelsPerMs * this.currentTimeMs}px`,\n top: `${this.timelineScrolltop}px`,\n borderColor: \"var(--filmstrip-playhead)\",\n })}\n ${ref(this.playheadRef)}\n ></div>\n\n ${renderFilmstripChildren(\n target ? ([target] as unknown as Element[]) : [],\n this.pixelsPerMs,\n this.hideSelectors,\n this.showSelectors,\n true,\n )}\n </div>\n </div>\n </div>`;\n }\n\n updated(changes: PropertyValueMap<any> | Map<PropertyKey, unknown>) {\n if (!this.targetTemporal) {\n return;\n }\n if (changes.has(\"currentTimeMs\")) {\n if (this.targetTemporal.currentTimeMs !== this.currentTimeMs) {\n this.targetTemporal.currentTimeMs = this.currentTimeMs;\n }\n }\n }\n\n get #contextElement(): EFWorkbench | EFPreview | null {\n return this.closest(\"ef-workbench, ef-preview\") as EFWorkbench | EFPreview;\n }\n\n @property({ type: String })\n target = \"\";\n\n @state()\n targetElement: Element | null = null;\n\n #targetController?: TargetController;\n #lastTargetTemporal?: TemporalMixinInterface | null;\n\n @consume({ context: targetTemporalContext, subscribe: true })\n @state()\n private _contextProvidedTemporal?: TemporalMixinInterface | null;\n\n get targetTemporal(): TemporalMixinInterface | null {\n const fromTarget =\n this.targetElement && isEFTemporal(this.targetElement)\n ? (this.targetElement as TemporalMixinInterface & HTMLElement)\n : null;\n const fromContext = this._contextProvidedTemporal;\n\n if (fromTarget && fromContext && fromTarget !== fromContext) {\n console.warn(\n \"EFFilmstrip: Both target attribute and parent context found. Using target attribute.\",\n { target: this.target, fromTarget, fromContext },\n );\n }\n\n return fromTarget ?? fromContext ?? null;\n }\n\n protected willUpdate(\n changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>,\n ) {\n if (changedProperties.has(\"target\")) {\n if (this.target && !this.#targetController) {\n this.#targetController = new TargetController(this);\n }\n }\n\n const currentTargetTemporal = this.targetTemporal;\n if (this.#lastTargetTemporal !== currentTargetTemporal) {\n this.#bindToTargetTimegroup();\n this.#lastTargetTemporal = currentTargetTemporal;\n }\n\n if (this.autoScale) {\n this.updatePixelsPerMs();\n }\n super.willUpdate(changedProperties);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-filmstrip\": EFFilmstrip;\n \"ef-timegroup-hierarchy-item\": EFTimegroupHierarchyItem;\n \"ef-audio-hierarchy-item\": EFAudioHierarchyItem;\n \"ef-video-hierarchy-item\": EFVideoHierarchyItem;\n \"ef-captions-hierarchy-item\": EFCaptionsHierarchyItem;\n \"ef-captions-active-word-hierarchy-item\": EFCaptionsActiveWordHierarchyItem;\n \"ef-waveform-hierarchy-item\": EFWaveformHierarchyItem;\n \"ef-image-hierarchy-item\": EFImageHierarchyItem;\n \"ef-html-hierarchy-item\": EFHTMLHierarchyItem;\n \"ef-timegroup-filmstrip\": EFTimegroupFilmstrip;\n \"ef-audio-filmstrip\": EFAudioFilmstrip;\n \"ef-video-filmstrip\": EFVideoFilmstrip;\n \"ef-captions-filmstrip\": EFCaptionsFilmstrip;\n \"ef-captions-active-word-filmstrip\": EFCaptionsActiveWordFilmstrip;\n \"ef-captions-segment-filmstrip\": EFCaptionsSegmentFilmstrip;\n \"ef-captions-before-word-filmstrip\": EFCaptionsBeforeWordFilmstrip;\n \"ef-captions-after-word-filmstrip\": EFCaptionsAfterWordFilmstrip;\n \"ef-text-filmstrip\": EFTextFilmstrip;\n \"ef-text-segment-filmstrip\": EFTextSegmentFilmstrip;\n \"ef-text-hierarchy-item\": EFTextHierarchyItem;\n \"ef-text-segment-hierarchy-item\": EFTextSegmentHierarchyItem;\n \"ef-waveform-filmstrip\": EFWaveformFilmstrip;\n \"ef-image-filmstrip\": EFImageFilmstrip;\n \"ef-html-filmstrip\": EFHTMLFilmstrip;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,IAAM,6BAAN,MAA+D;CAC7D,YACE,AAAQA,MACR,AAAQC,WACR;EAFQ;EACA;AAER,OAAK,KAAK,cAAc,KAAK;;CAG/B,SAAS;AACP,OAAK,KAAK,iBAAiB,KAAK;;CAGlC,mBAAmB;AACjB,OAAK,KAAK,iBAAiB,KAAK;;CAGlC,cAAoB;AAClB,OAAK,UAAU,eAAe;;;AAIlC,MAAM,mBAAmB,IAAI,IAAI;CAC/B;CACA;CACA;CACA;CACD,CAAC;AAEF,IAAM,gBAAN,cAA4B,QAAQ,WAAW,CAAC;;;iBAoBC,IAAI,aAAa;qBAGlD;;;gBAtBE,CACd,GAAG;;;;MAKJ;;CAQD,IAAI,YAAY;AACd,SAAO,KAAK,WAAW,KAAK,cAAc,mBAAmB,KAAK;;CAWpE,IAAI,eAAe;AACjB,SAAO;GACL,UAAU;GACV,MAAM,GAAG,KAAK,eAAe,KAAK,QAAQ,0BAA0B,KAAK,QAAQ,eAAe;GAChG,OAAO,GAAG,KAAK,eAAe,KAAK,QAAQ,uBAAuB,KAAK,QAAQ,YAAY;GAC5F;;CAKH,IAAI,oBAAoB;AACtB,SAAO;GACL,OAAO,GAAG,KAAK,cAAc,KAAK,QAAQ,WAAW;GACrD,MAAM,GAAG,KAAK,cAAc,KAAK,QAAQ,cAAc;GACxD;;CAGH,SAAS;AACP,SAAO,IAAI,cAAc,SAAS,KAAK,aAAa,CAAC;;;wBAGjC,KAAK,UAAU;4BACX;AAClB,OAAI,KAAK,aACP,MAAK,aAAa,iBAAiB,KAAK;IAE1C;4BACkB;AAClB,OAAI,KAAK,aACP,MAAK,aAAa,iBAAiB;IAErC;;;0BAGgB,KAAK,UAAU;;kBAEvB,SAAS;GACf,GAAG,KAAK;GACR,iBAAiB,KAAK,YAClB,kCACA;GACJ,aAAa;GACd,CAAC,CAAC;;YAED,KAAK,YAAY,CAAC;;;QAGtB,KAAK,gBAAgB,CAAC;;;CAI5B,iBAA6E;AAC3E,SAAO,wBACL,MAAM,KAAK,KAAK,QAAQ,SAAS,EACjC,KAAK,aACL,KAAK,eACL,KAAK,cACN;;CASH,WAAW;AACT,SAAO,IAAI;;CAGb,aAAa;AAEX,SADmB,KAAK,QAAQ,eAAe,CAC7B,KAAK,cAAc;GACnC,MAAM,SAAS,UAAU;AACzB,OAAI,EAAE,kBAAkB,gBACtB,QAAO;GAET,MAAM,QAAQ,OAAO,WAAW,CAAC,SAAS;GAC1C,MAAM,WAAW,OAAO,WAAW,CAAC;AACpC,OAAI,aAAa,KACf,QAAO;GAGT,MAAM,gBADY,OAAO,cAAc,CACP;AAChC,OAAI,CAAC,cACH,QAAO;GAET,MAAM,aAAa,IAAI,IAAI,OAAO,KAAK,cAAc,CAAC;AACtD,QAAK,MAAM,OAAO,iBAChB,YAAW,OAAO,IAAI;AAGxB,UAAO,IAAI;;;gBAGD,SAAS;IACf,MAAM,GAAG,KAAK,cAAc,MAAM;IAClC,OAAO,GAAG,KAAK,cAAc,OAAO,SAAS,CAAC;IAC9C,iBAAiB;IAClB,CAAC,CAAC;;wCAE6B,MAAM,KAAK,WAAW,CAAC,KAAK,IAAI,CAAC;UAC/D,OAAO,cAAc,CAAC,KAAK,aAAa;AACxC,WAAO,IAAI;;oBAED,SAAS;KACf,MAAM,GACJ,KAAK,cAAc,SAAS,iBAAiB,OAAO,SAAS,CAC9D;KACD,iBAAiB;KAClB,CAAC,CAAC;;KAEL,CAAC;;IAEL;;CAKJ,OAAO,mBAA2D;AAChE,MACE,kBAAkB,IAAI,UAAU,IAChC,KAAK,mBAAmB,YACxB;AACA,QAAK,qBAAqB,QAAQ;AAClC,QAAK,sBAAsB,IAAI,2BAC7B,KAAK,SACL,KACD;;AAEH,QAAM,OAAO,kBAAkB;;;YApJhC,QAAQ;CAAE,SAAS;CAAc,WAAW;CAAM,CAAC;YAGnD,QAAQ;CAAE,SAAS;CAAuB,WAAW;CAAM,CAAC;YAO5D,SAAS;CAAE,MAAM;CAAQ,WAAW;CAAO,CAAC;YAG5C,SAAS,EAAE,MAAM,QAAQ,CAAC;YAiE1B,SAAS;CAAE,MAAM;CAAO,WAAW;CAAO,CAAC;YAG3C,SAAS;CAAE,MAAM;CAAO,WAAW;CAAO,CAAC;AAwEvC,6BAAMC,2BAAyB,cAAc;CAClD,WAAW;AACT,SAAO,IAAI;;;+BAHd,cAAc,qBAAqB;AAQ7B,6BAAMC,2BAAyB,cAAc;CAClD,WAAW;AACT,SAAO,IAAI;;;+BAHd,cAAc,qBAAqB;AAQ7B,gCAAMC,8BAA4B,cAAc;CACrD,SAAS;EAEP,MAAM,eADW,KAAK,QACQ,wBAAwB;AAEtD,SAAO,IAAI,cAAc,SAAS,KAAK,aAAa,CAAC;;;;wBAIjC,KAAK,UAAU;4BACX;AAClB,OAAI,KAAK,aACP,MAAK,aAAa,iBAAiB,KAAK;IAE1C;4BACkB;AAClB,OAAI,KAAK,aACP,MAAK,aAAa,iBAAiB;IAErC;;;0BAGgB,KAAK,UAAU;;kBAEvB,SAAS;GACf,GAAG,KAAK;GACR,iBAAiB,KAAK,YAClB,kCACA;GACJ,aAAa;GACd,CAAC,CAAC;;eAEE,KAAK,mBAAmB,aAAa,CAAC;;;QAG7C,KAAK,gBAAgB,CAAC;;;CAI5B,mBAAmB,cAA0C;AAC3D,MAAI,CAAC,aACH,QAAO,IAAI;EAIb,MAAM,WAAW,KAAK;EAItB,MAAM,yBAHgB,SAAS,eACM,iBAAiB,KACV,SAAS,eACF;AA4BnD,SAAO,IAAI,GAzBa,aAAa,SAAS,KAAK,YAAY;GAC7D,MAAM,WACJ,wBAAwB,QAAQ,SAChC,uBAAuB,QAAQ;AAEjC,UAAO,IAAI;2EAC0D,WAAW,oBAAoB,GAAG;gBAC7F,SAAS;IACf,MAAM,GAAG,KAAK,cAAc,QAAQ,QAAQ,IAAK;IACjD,OAAO,GAAG,KAAK,eAAe,QAAQ,MAAM,QAAQ,SAAS,IAAK;IAClE,QAAQ;IACR,KAAK;IACL,iBAAiB,WACb,gCACA;IACJ,aAAa,WACT,oCACA;IACL,CAAC,CAAC;2BACgB,QAAQ,KAAK,KAAK,QAAQ,MAAM,MAAM,QAAQ,IAAI;;yCAEpC,WAAW,cAAc,GAAG,IAAI,QAAQ,KAAK;;IAEhF;;CAKJ,iBAA6E;AAE3E,SAAO,wBACL,MAAM,KAAK,KAAK,QAAQ,SAAS,EACjC,KAAK,aACL,KAAK,eACL,KAAK,cACN;;;kCAxFJ,cAAc,wBAAwB;AA6FhC,0CAAMC,wCAAsC,cAAc;CAC/D,IAAI,sBAAsB;EACxB,MAAM,iBAAiB,KAAK,QAAQ,QAAQ,cAAc;AAC1D,SAAO;GACL,UAAU;GACV,MAAM,GAAG,KAAK,eAAe,gBAAgB,2BAA2B,GAAG;GAC3E,OAAO,GAAG,KAAK,eAAe,gBAAgB,cAAc,GAAG;GAChE;;CAGH,SAAS;EAEP,MAAM,iBAAiB,KAAK,QAAQ,QAAQ,cAAc;EAC1D,MAAM,eAAe,gBAAgB,wBAAwB;AAE7D,MAAI,CAAC,aACH,QAAO,IAAI,cAAc,SAAS,KAAK,oBAAoB,CAAC;;;;;EAW9D,MAAM,yBAHgB,eAAe,eACA,iBAAiB,KACV,eAAe,eACR;AAEnD,SAAO,IAAI,cAAc,SAAS,KAAK,oBAAoB,CAAC;;UAEtD,aAAa,cAAc,KAAK,SAAS;GACzC,MAAM,oBACJ,wBAAwB,KAAK,SAC7B,uBAAuB,KAAK;AAE9B,UAAO,IAAI;gFAC2D,oBAAoB,oBAAoB,GAAG;oBACvG,SAAS;IACf,MAAM,GAAG,KAAK,cAAc,KAAK,QAAQ,IAAK;IAC9C,OAAO,GAAG,KAAK,eAAe,KAAK,MAAM,KAAK,SAAS,IAAK;IAC5D,QAAQ;IACR,KAAK;IACL,iBAAiB,oBACb,gCACA;IACJ,aAAa,oBACT,oCACA;IACL,CAAC,CAAC;4BACa,KAAK,KAAK,KAAK,KAAK,MAAM,MAAM,KAAK,IAAI;;cAEvD,oBAAoB,IAAI,sHAAsH,KAAK,KAAK,MAAM,CAAC,WAAW,GAAG;;IAEjL,CAAC;;;;;4CAvDV,cAAc,oCAAoC;AA8D5C,uCAAMC,qCAAmC,cAAc;CAC5D,IAAI,sBAAsB;EACxB,MAAM,iBAAiB,KAAK,QAAQ,QAAQ,cAAc;AAC1D,SAAO;GACL,UAAU;GACV,MAAM,GAAG,KAAK,eAAe,gBAAgB,2BAA2B,GAAG;GAC3E,OAAO,GAAG,KAAK,eAAe,gBAAgB,cAAc,GAAG;GAChE;;CAGH,SAAS;EAEP,MAAM,iBAAiB,KAAK,QAAQ,QAAQ,cAAc;EAC1D,MAAM,eAAe,gBAAgB,wBAAwB;AAE7D,MAAI,CAAC,aACH,QAAO,IAAI,cAAc,SAAS,KAAK,oBAAoB,CAAC;;;;;EAW9D,MAAM,yBAHgB,eAAe,eACA,iBAAiB,KACV,eAAe,eACR;AAEnD,SAAO,IAAI,cAAc,SAAS,KAAK,oBAAoB,CAAC;;UAEtD,aAAa,SAAS,KAAK,YAAY;GACvC,MAAM,oBACJ,wBAAwB,QAAQ,SAChC,uBAAuB,QAAQ;AAEjC,UAAO,IAAI;gFAC2D,oBAAoB,oBAAoB,GAAG;oBACvG,SAAS;IACf,MAAM,GAAG,KAAK,cAAc,QAAQ,QAAQ,IAAK;IACjD,OAAO,GAAG,KAAK,eAAe,QAAQ,MAAM,QAAQ,SAAS,IAAK;IAClE,QAAQ;IACR,KAAK;IACL,iBAAiB,oBACb,gCACA;IACJ,aAAa,oBACT,oCACA;IACL,CAAC,CAAC;+BACgB,QAAQ,KAAK,KAAK,QAAQ,MAAM,MAAM,QAAQ,IAAI;;cAEnE,oBAAoB,IAAI,sHAAsH,QAAQ,KAAK,WAAW,GAAG;;IAE7K,CAAC;;;;;yCAvDV,cAAc,gCAAgC;AA8DxC,0CAAMC,wCAAsC,cAAc;CAC/D,IAAI,sBAAsB;EACxB,MAAM,iBAAiB,KAAK,QAAQ,QAAQ,cAAc;AAC1D,SAAO;GACL,UAAU;GACV,MAAM,GAAG,KAAK,eAAe,gBAAgB,2BAA2B,GAAG;GAC3E,OAAO,GAAG,KAAK,eAAe,gBAAgB,cAAc,GAAG;GAChE;;CAGH,SAAS;EAEP,MAAM,iBAAiB,KAAK,QAAQ,QAAQ,cAAc;EAC1D,MAAM,eAAe,gBAAgB,wBAAwB;AAE7D,MAAI,CAAC,aACH,QAAO,IAAI,cAAc,SAAS,KAAK,oBAAoB,CAAC;;;;;EAW9D,MAAM,yBAHgB,eAAe,eACA,iBAAiB,KACV,eAAe,eACR;AAEnD,SAAO,IAAI,cAAc,SAAS,KAAK,oBAAoB,CAAC;;UAEtD,aAAa,cAAc,KAAK,SAAS;GACzC,MAAM,oBACJ,wBAAwB,KAAK,SAC7B,uBAAuB,KAAK;AAE9B,UAAO,IAAI;gFAC2D,oBAAoB,oBAAoB,GAAG;oBACvG,SAAS;IACf,MAAM,GAAG,KAAK,cAAc,KAAK,QAAQ,IAAK;IAC9C,OAAO,GAAG,KAAK,eAAe,KAAK,MAAM,KAAK,SAAS,IAAK;IAC5D,QAAQ;IACR,KAAK;IACL,iBAAiB,oBACb,gCACA;IACJ,aAAa,oBACT,oCACA;IACL,CAAC,CAAC;4BACa,KAAK,KAAK,KAAK,KAAK,MAAM,MAAM,KAAK,IAAI;;;;IAI3D,CAAC;;;;;4CAvDV,cAAc,oCAAoC;AA8D5C,yCAAMC,uCAAqC,cAAc;CAC9D,IAAI,sBAAsB;EACxB,MAAM,iBAAiB,KAAK,QAAQ,QAAQ,cAAc;AAC1D,SAAO;GACL,UAAU;GACV,MAAM,GAAG,KAAK,eAAe,gBAAgB,2BAA2B,GAAG;GAC3E,OAAO,GAAG,KAAK,eAAe,gBAAgB,cAAc,GAAG;GAChE;;CAGH,SAAS;EAEP,MAAM,iBAAiB,KAAK,QAAQ,QAAQ,cAAc;EAC1D,MAAM,eAAe,gBAAgB,wBAAwB;AAE7D,MAAI,CAAC,aACH,QAAO,IAAI,cAAc,SAAS,KAAK,oBAAoB,CAAC;;;;;EAW9D,MAAM,yBAHgB,eAAe,eACA,iBAAiB,KACV,eAAe,eACR;AAEnD,SAAO,IAAI,cAAc,SAAS,KAAK,oBAAoB,CAAC;;UAEtD,aAAa,cAAc,KAAK,SAAS;GACzC,MAAM,oBACJ,wBAAwB,KAAK,SAC7B,uBAAuB,KAAK;AAE9B,UAAO,IAAI;gFAC2D,oBAAoB,oBAAoB,GAAG;oBACvG,SAAS;IACf,MAAM,GAAG,KAAK,cAAc,KAAK,QAAQ,IAAK;IAC9C,OAAO,GAAG,KAAK,eAAe,KAAK,MAAM,KAAK,SAAS,IAAK;IAC5D,QAAQ;IACR,KAAK;IACL,iBAAiB,oBACb,gCACA;IACJ,aAAa,oBACT,oCACA;IACL,CAAC,CAAC;4BACa,KAAK,KAAK,KAAK,KAAK,MAAM,MAAM,KAAK,IAAI;;;;IAI3D,CAAC;;;;;2CAvDV,cAAc,mCAAmC;AA8D3C,gCAAMC,8BAA4B,cAAc;CACrD,WAAW;AACT,SAAO,IAAI;;CAGb,iBAAiC;AAC/B,SAAO;;;kCAPV,cAAc,wBAAwB;AAYhC,4BAAMC,0BAAwB,cAAc;CACjD,SAAS;EACP,MAAM,OAAO,KAAK;EAClB,MAAM,WAAW,MAAM,KAAK,KAAK,iBAAiB,kBAAkB,CAAC;AAErE,SAAO,IAAI,cAAc,SAAS,KAAK,aAAa,CAAC;;;;wBAIjC,KAAK,UAAU;4BACX;AAClB,OAAI,KAAK,aACP,MAAK,aAAa,iBAAiB,KAAK;IAE1C;4BACkB;AAClB,OAAI,KAAK,aACP,MAAK,aAAa,iBAAiB;IAErC;;;0BAGgB,KAAK,UAAU;;kBAEvB,SAAS;GACf,GAAG,KAAK;GACR,iBAAiB,KAAK,YAClB,kCACA;GACJ,aAAa;GACd,CAAC,CAAC;;eAEE,KAAK,mBAAmB,SAAS,CAAC;;;QAGzC,KAAK,gBAAgB,CAAC;;;CAI5B,mBAAmB,UAA2B;AAC5C,MAAI,SAAS,WAAW,EACtB,QAAO,IAAI;EAIb,MAAM,OAAO,KAAK;EAGlB,MAAM,mBAFgB,KAAK,eACU,iBAAiB,KACd,KAAK;AAE7C,SAAO,SAAS,KAAK,YAAY;GAC/B,MAAM,WACJ,mBAAmB,QAAQ,kBAC3B,kBAAkB,QAAQ;AAE5B,UAAO,IAAI;2EAC0D,WAAW,oBAAoB,GAAG;gBAC7F,SAAS;IACf,MAAM,GAAG,KAAK,cAAc,QAAQ,eAAe;IACnD,OAAO,GAAG,KAAK,eAAe,QAAQ,eAAe,QAAQ,gBAAgB;IAC7E,QAAQ;IACR,KAAK;IACL,iBAAiB,WACb,gCACA;IACJ,aAAa,WACT,oCACA;IACL,CAAC,CAAC;2BACgB,QAAQ,YAAY,KAAK,QAAQ,eAAe,OAAO,QAAQ,aAAa;;yCAE9D,WAAW,cAAc,GAAG,IAAI,QAAQ,YAAY;;IAEvF;;CAGJ,iBAA6E;AAC3E,SAAO,wBACL,MAAM,KAAK,KAAK,QAAQ,SAAS,EACjC,KAAK,aACL,KAAK,eACL,KAAK,cACN;;;8BAnFJ,cAAc,oBAAoB;AAwF5B,mCAAMC,iCAA+B,cAAc;CACxD,IAAI,kBAAkB;EACpB,MAAM,aAAa,KAAK,QAAQ,QAAQ,UAAU;AAClD,SAAO;GACL,UAAU;GACV,MAAM,GAAG,KAAK,eAAe,YAAY,2BAA2B,GAAG;GACvE,OAAO,GAAG,KAAK,eAAe,YAAY,cAAc,GAAG;GAC5D;;CAGH,SAAS;EACP,MAAM,UAAU,KAAK;EACrB,MAAM,aAAa,QAAQ,QAAQ,UAAU;AAE7C,MAAI,CAAC,WACH,QAAO,IAAI,cAAc,SAAS,KAAK,gBAAgB,CAAC;;;;;EAU1D,MAAM,mBAFgB,WAAW,eACI,iBAAiB,KACd,WAAW;EAEnD,MAAM,oBACJ,mBAAmB,QAAQ,kBAC3B,kBAAkB,QAAQ;AAE5B,SAAO,IAAI,cAAc,SAAS,KAAK,gBAAgB,CAAC;;;8EAGkB,oBAAoB,oBAAoB,GAAG;kBACvG,SAAS;GACf,MAAM,GAAG,KAAK,cAAc,QAAQ,eAAe;GACnD,OAAO,GAAG,KAAK,eAAe,QAAQ,eAAe,QAAQ,gBAAgB;GAC7E,QAAQ;GACR,KAAK;GACL,iBAAiB,oBACb,gCACA;GACJ,aAAa,oBACT,oCACA;GACL,CAAC,CAAC;6BACgB,QAAQ,YAAY,KAAK,QAAQ,eAAe,OAAO,QAAQ,aAAa;;YAE7F,oBAAoB,IAAI,sHAAsH,QAAQ,YAAY,WAAW,GAAG;;;;;;qCAlD3L,cAAc,4BAA4B;AA0DpC,6BAAMC,2BAAyB,cAAc;CAClD,WAAW;AACT,SAAO,IAAI;;;+BAHd,cAAc,qBAAqB;AAQ7B,iCAAMC,+BAA6B,cAAc;CACtD,WAAW;AACT,SAAO,IAAI;;QAEP,wBACA,MAAM,KAAK,KAAK,QAAQ,YAAY,EAAE,CAAC,EACvC,KAAK,aACL,KAAK,eACL,KAAK,cACN,CAAC;;;;;mCAVP,cAAc,yBAAyB;AAiBjC,4BAAMC,0BAAwB,cAAc;CACjD,WAAW;AACT,SAAO,IAAI;cACD,KAAK,QAAQ,QAAQ;QAC3B,wBACA,MAAM,KAAK,KAAK,QAAQ,YAAY,EAAE,CAAC,EACvC,KAAK,aACL,KAAK,eACL,KAAK,cACN,CAAC;;;;8BAVP,cAAc,oBAAoB;AAenC,4BACMC,0BAEI,QAAQ,WAAW,CAAC;;;iBAGL,IAAI,aAAa;;CAcxC,IAAI,OAAmC;AACrC,SAAO;;CAGT,IAAI,YAAY;AACd,SAAO,KAAK,WAAW,KAAK,cAAc,mBAAmB,KAAK;;CAGpE,eAA4D;AAC1D,SAAO;;CAGT,SAAS;AACP,SAAO,IAAI;;;;kBAIG,SAAS;GACf,iBAAiB,KAAK,YAClB,uCACA;GACJ,aAAa;GACd,CAAC,CAAC;0BACa,KAAK,UAAU;8BACX;AAClB,OAAI,KAAK,aACP,MAAK,aAAa,iBAAiB,KAAK;IAE1C;8BACkB;AAClB,OAAI,KAAK,aACP,MAAK,aAAa,iBAAiB;IAErC;;YAEA,KAAK,KAAK,GAAG,KAAK,cAAc,CAAC;;;;kBAI3B,SAAS,EACf,iBAAiB,KAAK,YAClB,wBACA,eACL,CAAC,CAAC;;YAED,KAAK,gBAAgB,CAAC;;;;CAKhC,iBAA6E;AAC3E,SAAO,wBACL,MAAM,KAAK,KAAK,QAAQ,SAAS,EACjC,KAAK,eACL,KAAK,cACN;;;YAvEF,SAAS;CAAE,MAAM;CAAQ,WAAW;CAAO,CAAC;YAI5C,QAAQ,EAAE,SAAS,cAAc,CAAC;YAGlC,QAAQ;CAAE,SAAS;CAAuB,WAAW;CAAM,CAAC;YAG5D,SAAS;CAAE,MAAM;CAAO,WAAW;CAAO,CAAC;YAG3C,SAAS;CAAE,MAAM;CAAO,WAAW;CAAO,CAAC;8BAjB7C,cAAc,oBAAoB;AA+EnC,qCACMC,mCAAiC,gBAA6B;CAClE,IAAI,OAAO;AACT,SAAO;;CAGT,eAA4D;AAC1D,SAAO,KAAK,QAAQ,QAAQ;;;uCAP/B,cAAc,8BAA8B;AAW7C,iCACMC,+BAA6B,gBAAyB;CAC1D,IAAI,OAAO;AACT,SAAO;;CAGT,eAAe;AACb,SAAO,KAAK,QAAQ,OAAO;;;mCAP9B,cAAc,0BAA0B;AAWzC,iCACMC,+BAA6B,gBAAyB;CAC1D,IAAI,OAAO;AACT,SAAO;;CAGT,eAAe;AACb,SAAO,KAAK,QAAQ,OAAO;;;mCAP9B,cAAc,0BAA0B;AAWzC,oCACMC,kCAAgC,gBAAgB;CACpD,IAAI,OAAO;AACT,SAAO;;;sCAHV,cAAc,6BAA6B;AAO5C,8CACMC,4CAA0C,gBAAgB;CAC9D,IAAI,OAAO;AACT,SAAO;;;gDAHV,cAAc,yCAAyC;AAOxD,gCACMC,8BAA4B,gBAAgB;CAChD,IAAI,OAAO;AACT,SAAO;;;kCAHV,cAAc,yBAAyB;AAOxC,uCACMC,qCAAmC,gBAAgB;CACvD,IAAI,OAAO;AACT,SAAO;;;yCAHV,cAAc,iCAAiC;AAOhD,oCACMC,kCAAgC,gBAAgB;CACpD,IAAI,OAAO;AACT,SAAO;;CAGT,iBAAiC;AAC/B,SAAO;;;sCAPV,cAAc,6BAA6B;AAW5C,iCACMC,+BAA6B,gBAAyB;CAC1D,IAAI,OAAO;AACT,SAAO;;CAGT,eAAe;AACb,SAAO,KAAK,QAAQ,OAAO;;;mCAP9B,cAAc,0BAA0B;AAWzC,gCACMC,8BAA4B,gBAAgB;CAChD,IAAI,OAAO;AACT,SAAO,IAAI,SAAS,IAAI,KAAK,QAAQ,QAAQ,aAAa,CAAC,GAAG;;;kCAHjE,cAAc,yBAAyB;AAOxC,MAAM,uBACJ,SACA,eACA,kBACY;AACZ,KAAI,mBAAmB,eAAe,QAAQ,SAAS,SACrD,QAAO;AAIT,KAAI,iBAAiB,cAAc,SAAS,EAC1C,QAAO,cAAc,MAAM,aAAa;AACtC,MAAI;AACF,UAAO,QAAQ,QAAQ,SAAS;UAC1B;AACN,UAAO;;GAET;AAIJ,KAAI,iBAAiB,cAAc,SAAS,EAC1C,QAAO,CAAC,cAAc,MAAM,aAAa;AACvC,MAAI;AACF,UAAO,QAAQ,QAAQ,SAAS;UAC1B;AACN,UAAO;;GAET;AAIJ,QAAO;;AAGT,MAAM,2BACJ,UACA,eACA,eACA,oBAAoB,UAC0B;AAC9C,QAAO,SAAS,KAAK,UAAU;AAC7B,MACE,CAAC,qBACD,CAAC,oBAAoB,OAAO,eAAe,cAAc,CAEzD,QAAO;AAGT,MAAI,iBAAiB,YACnB,QAAO,IAAI;mBACE,MAAM;yBACA,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,QACnB,QAAO,IAAI;mBACE,MAAM;yBACA,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,QACnB,QAAO,IAAI;mBACE,MAAM;yBACA,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,QACnB,QAAO,IAAI;mBACE,MAAM;yBACA,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,WACnB,QAAO,IAAI;mBACE,MAAM;yBACA,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,qBACnB,QAAO,IAAI;mBACE,MAAM;yBACA,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,OACnB,QAAO,IAAI;mBACE,MAAM;yBACA,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,cACnB,QAAO,IAAI;mBACE,MAAM;yBACA,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,WACnB,QAAO,IAAI;mBACE,MAAM;yBACA,cAAc;yBACd,cAAc;;AAGnC,SAAO,IAAI;iBACE,MAAM;uBACA,cAAc;uBACd,cAAc;;GAEjC;;AAGJ,MAAM,2BACJ,UACA,aACA,eACA,eACA,oBAAoB,UAC0B;AAC9C,QAAO,SAAS,KAAK,UAAU;AAC7B,MACE,CAAC,qBACD,CAAC,oBAAoB,OAAO,eAAe,cAAc,CAEzD,QAAO;AAGT,MAAI,iBAAiB,YACnB,QAAO,IAAI;mBACE,MAAM;uBACF,YAAY;yBACV,cAAc;yBACd,cAAc;;;AAInC,MAAI,iBAAiB,QACnB,QAAO,IAAI;mBACE,MAAM;uBACF,YAAY;yBACV,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,QACnB,QAAO,IAAI;mBACE,MAAM;uBACF,YAAY;yBACV,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,QACnB,QAAO,IAAI;mBACE,MAAM;uBACF,YAAY;yBACV,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,WACnB,QAAO,IAAI;mBACE,MAAM;uBACF,YAAY;yBACV,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,qBACnB,QAAO,IAAI;mBACE,MAAM;uBACF,YAAY;yBACV,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,OACnB,QAAO,IAAI;mBACE,MAAM;uBACF,YAAY;yBACV,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,cACnB,QAAO,IAAI;mBACE,MAAM;uBACF,YAAY;yBACV,cAAc;yBACd,cAAc;;AAGnC,MAAI,MAAM,YAAY,sBACpB,QAAO,IAAI;mBACE,MAAM;uBACF,YAAY;yBACV,cAAc;yBACd,cAAc;;AAGnC,MAAI,MAAM,YAAY,iCACpB,QAAO,IAAI;mBACE,MAAM;uBACF,YAAY;yBACV,cAAc;yBACd,cAAc;;AAGnC,MAAI,MAAM,YAAY,gCACpB,QAAO,IAAI;mBACE,MAAM;uBACF,YAAY;yBACV,cAAc;yBACd,cAAc;;AAGnC,MAAI,iBAAiB,WACnB,QAAO,IAAI;mBACE,MAAM;uBACF,YAAY;yBACV,cAAc;yBACd,cAAc;;AAGnC,SAAO,IAAI;iBACE,MAAM;qBACF,YAAY;uBACV,cAAc;uBACd,cAAc;;GAEjC;;AAIG,wBAAMC,sBAAoB,QAAQ,WAAW,CAAC;;;qBAsDrC;cAGP;cAGA;mBAmBK;2BAE+B;2BAGvB;uBAaJ;mBAGJ;wBAEa,IAAI,qBAAqB;AAChD,OAAI,KAAK,UACP,MAAK,mBAAmB;IAE1B;mBA4NU,WAA2B;sBACxB,WAA2B;qBAC5B,WAA2B;gBAiHhC;uBAGuB;;;gBA3bhB,CACd,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAkDJ;;CAUD,IAAI,gBAAsC;AACxC,MAAI,CAAC,KAAK,KAAM,QAAO;AACvB,SAAO,KAAK,KACT,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,QAAQ,MAAM,EAAE,SAAS,EAAE;;CAGhC,IAAI,gBAAsC;AACxC,MAAI,CAAC,KAAK,KAAM,QAAO;AACvB,SAAO,KAAK,KACT,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,QAAQ,MAAM,EAAE,SAAS,EAAE;;CAiChC,oBAA0B;AACxB,QAAM,mBAAmB;AACzB,QAAKC,uBAAwB;AAC7B,SAAO,iBAAiB,YAAY,MAAKC,eAAgB;AAEzD,OAAK,eAAe,QAAQ,KAAK;AAEjC,MAAI,KAAK,OACP,OAAKC,mBAAoB,IAAI,iBAAiB,KAAK;;CAIvD,uBAA6B;AAC3B,QAAM,sBAAsB;AAC5B,SAAO,oBAAoB,YAAY,MAAKD,eAAgB;AAC5D,OAAK,eAAe,YAAY;;CAGlC,oBAAoB;EAClB,MAAM,SAAS,KAAK;EACpB,MAAM,SAAS,KAAK,UAAU;AAC9B,MAAI,UAAU,UAAU,OAAO,cAAc,EAC3C,MAAK,cAAc,OAAO,eAAe,OAAO,cAAc;;CAIlE,yBAAyB;AACvB,MAAI,KAAK,oBACP,MAAK,oBAAoB,QAAQ;EAEnC,MAAM,SAAS,KAAK;AACpB,MAAI,QAAQ;AACV,QAAK,sBAAsB,IAAI,oBAC7B,QACA,KACD;AAGD,QAAK,gBAAgB,OAAO;;;CAIhC,mBAAmB,UAAyB;AAE1C,MAAI,MAAM,QAAQ,KAAK;GACrB,MAAM,CAAC,UAAU,MAAM,cAAc;AASrC,OAH4B,QAA+B,QAHzD,wDAKD,CAEC;AAEF,SAAM,gBAAgB;AACtB,OAAI,MAAKE,eACP,OAAKA,eAAgB,UAAU,CAAC,MAAKA,eAAgB;;;CAK3D,AACA,mBAAmB;AACjB,MAAI,KAAK,UAAU,KAAK,aAAa,OAAO;AAC1C,QAAK,aAAa,MAAM,YAAY,KAAK,OAAO;AAChD,QAAK,oBAAoB,KAAK,OAAO;;;CAIzC,AACA,sBAAsB;AACpB,MAAI,KAAK,UAAU,KAAK,aAAa,OAAO;AAC1C,QAAK,OAAO,YAAY,KAAK,aAAa,MAAM;AAChD,QAAK,oBAAoB,KAAK,aAAa,MAAM;;;CAIrD,AACA,MAAM,GAAiB;AACrB,MAAI,KAAK,QACP;AAEF,MAAI,CAAC,KAAK,UACR;AAEF,MACE,KAAK,sBAAsB,QAC3B,EAAE,cAAc,KAAK,kBAErB;AAEF,IAAE,gBAAgB;AAClB,IAAE,iBAAiB;AACnB,OAAK,WAAW,EAAE;;CAGpB,AACA,WAAW,GAAiB;AAC1B,IAAE,gBAAgB;AAClB,IAAE,iBAAiB;AACnB,OAAK,YAAY;AACjB,OAAK,oBAAoB,EAAE;EAE3B,MAAM,SAAS,EAAE;AACjB,MAAI,OACF,KAAI;AACF,UAAO,kBAAkB,EAAE,UAAU;WAC9B,KAAK;AAEZ,WAAQ,KAAK,kCAAkC,IAAI;;AAMvD,uBAAqB;AACnB,QAAK,WAAW,EAAE;IAClB;EAEF,MAAM,mBAAmB,YAA0B;AACjD,OAAI,QAAQ,cAAc,KAAK,mBAAmB;AAChD,YAAQ,gBAAgB;AACxB,YAAQ,iBAAiB;AACzB,QAAI,OACF,KAAI;AACF,YAAO,sBAAsB,QAAQ,UAAU;aACxC,MAAM;AAIjB,SAAK,oBAAoB;AACzB,SAAK,YAAY;AACjB,wBAAoB,aAAa,gBAAgB;;;EAIrD,MAAM,uBAAuB,gBAA8B;AACzD,OAAI,YAAY,cAAc,KAAK,mBAAmB;AACpD,QAAI,OACF,KAAI;AACF,YAAO,sBAAsB,YAAY,UAAU;aAC5C,MAAM;AAIjB,SAAK,oBAAoB;AACzB,SAAK,YAAY;AACjB,wBAAoB,iBAAiB,oBAAoB;;;AAI7D,mBAAiB,aAAa,iBAAiB;GAC7C,MAAM;GACN,SAAS;GACV,CAAC;AACF,mBAAiB,iBAAiB,qBAAqB;GACrD,MAAM;GACN,SAAS;GACV,CAAC;;CAGJ,AACA,kBAAkB,GAAU;AAC1B,MAAI,KAAK,WAAW;AAClB,KAAE,gBAAgB;AAClB,KAAE,iBAAiB;;;CAIvB,WAAW,GAAiB;EAC1B,MAAM,SAAS,KAAK,YAAY,cAAc,UAAU;AACxD,MAAI,CAAC,OACH;EAEF,MAAM,OAAO,OAAO,uBAAuB;AAC3C,MAAI,KAAK,gBAAgB;GAEvB,MAAM,eADS,EAAE,QAAQ,KAAK,OAAO,OAAO,cACf,KAAK;AAClC,QAAK,eAAe,gBAAgB;;;CAIxC,AACA,YAAY,GAAe;AACzB,MAAI,KAAK,kBAAkB,KAAK,UAAU,CAAC,KAAK,SAAS;AACvD,OAAI,EAAE,WAAW,EACf,GAAE,gBAAgB;AAGpB,OACE,KAAK,UAAU,SACf,KAAK,UAAU,MAAM,eAAe,KACpC,EAAE,SAAS,GACX;AACA,SAAK,OAAO,SAAS,GAAG,EAAE,OAAO;AACjC;;AAIF,OACE,KAAK,OAAO,cAAc,KAAK,OAAO,eACpC,KAAK,OAAO,eACd,EAAE,SAAS,GACX;AACA,SAAK,OAAO,SAAS,GAAG,EAAE,OAAO;AACjC;;AAGF,OAAI,MAAM;AACR,SAAK,OAAO,SAAS,EAAE,QAAQ,EAAE,OAAO;AACxC,SAAK,eAAe,iBAAiB,EAAE,SAAS,KAAK;;;;CAS3D,IAAI,SAAS;AACX,SAAO,KAAK,UAAU;;CAGxB,SAAS;EACP,MAAM,SAAS,KAAK;AAEpB,SAAO,IAAI;;cAED,SAAS;GACf,qBAAqB;GACrB,kBAAkB;GAClB,iBAAiB;GAClB,CAAC,CAAC;;;;;;UAOC,CAAC,KAAK,YACF,IAAI;;uBAEK,KAAK,YAAY;;;;wBAIhB,MAAa;GACrB,MAAMC,WAAS,EAAE;AACjB,QAAK,cAAc,OAAO,WAAWA,SAAO,MAAM;IAClD;kBAEF,QACL;gBACO,aAAa,KAAK,eAAe,KAAK,CAAC;gBACvC,aAAa,QAAQ,cAAc,GAAG,KAAK,CAAC;;;;;;;;;kCAS1B,KAAK,OAAO,OAAO,IAAI,kDAAkD;;;;UAIjG,IAAI,KAAK,aAAa,CAAC;kBACf,KAAK,oBAAoB;;UAEjC,wBACA,SAAU,CAAC,OAAO,GAA4B,EAAE,EAChD,KAAK,eACL,KAAK,eACL,KACD,CAAC;;;;;;UAMA,IAAI,KAAK,UAAU,CAAC;kBACZ,KAAK,iBAAiB;iBACvB,KAAK,YAAY;;;;0BAIR,KAAK,eAAe,QAAQ,cAAc,GAAG;yBAC9C,KAAK,MAAM;yBACX,KAAK,WAAW;yBAChB,KAAK,kBAAkB;;;;oBAI5B,SAAS;GACf,MAAM,GAAG,KAAK,cAAc,KAAK,cAAc;GAC/C,KAAK,GAAG,KAAK,kBAAkB;GAC/B,aAAa;GACd,CAAC,CAAC;cACD,IAAI,KAAK,YAAY,CAAC;;;YAGxB,wBACA,SAAU,CAAC,OAAO,GAA4B,EAAE,EAChD,KAAK,aACL,KAAK,eACL,KAAK,eACL,KACD,CAAC;;;;;CAMV,QAAQ,SAA4D;AAClE,MAAI,CAAC,KAAK,eACR;AAEF,MAAI,QAAQ,IAAI,gBAAgB,EAC9B;OAAI,KAAK,eAAe,kBAAkB,KAAK,cAC7C,MAAK,eAAe,gBAAgB,KAAK;;;CAK/C,KAAID,iBAAkD;AACpD,SAAO,KAAK,QAAQ,2BAA2B;;CASjD;CACA;CAMA,IAAI,iBAAgD;EAClD,MAAM,aACJ,KAAK,iBAAiB,aAAa,KAAK,cAAc,GACjD,KAAK,gBACN;EACN,MAAM,cAAc,KAAK;AAEzB,MAAI,cAAc,eAAe,eAAe,YAC9C,SAAQ,KACN,wFACA;GAAE,QAAQ,KAAK;GAAQ;GAAY;GAAa,CACjD;AAGH,SAAO,cAAc,eAAe;;CAGtC,AAAU,WACR,mBACA;AACA,MAAI,kBAAkB,IAAI,SAAS,EACjC;OAAI,KAAK,UAAU,CAAC,MAAKD,iBACvB,OAAKA,mBAAoB,IAAI,iBAAiB,KAAK;;EAIvD,MAAM,wBAAwB,KAAK;AACnC,MAAI,MAAKG,uBAAwB,uBAAuB;AACtD,SAAKL,uBAAwB;AAC7B,SAAKK,qBAAsB;;AAG7B,MAAI,KAAK,UACP,MAAK,mBAAmB;AAE1B,QAAM,WAAW,kBAAkB;;;YAnbpC,SAAS,EAAE,MAAM,QAAQ,CAAC;YAG1B,SAAS,EAAE,MAAM,QAAQ,CAAC;YAG1B,SAAS,EAAE,MAAM,QAAQ,CAAC;YAmB1B,OAAO;YAKP,OAAO;YAGP,QAAQ;CAAE,SAAS;CAAgB,WAAW;CAAM,CAAC,EACrD,OAAO;YAGP,QAAQ;CAAE,SAAS;CAAa,WAAW;CAAM,CAAC,EAClD,OAAO;YAKP,OAAO;YAGP,SAAS;CAAE,MAAM;CAAS,SAAS;CAAM,WAAW;CAAc,CAAC;YAyEnE,aAAa,EAAE,SAAS,OAAO,CAAC;YAQhC,aAAa,EAAE,SAAS,OAAO,CAAC;YAQhC,aAAa;CAAE,SAAS;CAAO,SAAS;CAAO,CAAC;YAmBhD,aAAa;CAAE,SAAS;CAAO,SAAS;CAAO,CAAC;YAiEhD,aAAa;CAAE,SAAS;CAAO,SAAS;CAAO,CAAC;YAqBhD,aAAa,EAAE,SAAS,OAAO,CAAC;YAmJhC,SAAS,EAAE,MAAM,QAAQ,CAAC;YAG1B,OAAO;YAMP,QAAQ;CAAE,SAAS;CAAuB,WAAW;CAAM,CAAC,EAC5D,OAAO;0BAncT,cAAc,eAAe"}
1
+ {"version":3,"file":"EFFilmstrip.js","names":["EFFilmstrip","#targetController","#lastTargetTemporal"],"sources":["../../src/gui/EFFilmstrip.ts"],"sourcesContent":["import { consume } from \"@lit/context\";\nimport { html, LitElement, nothing } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { createRef, ref } from \"lit/directives/ref.js\";\n\nimport {\n isEFTemporal,\n type TemporalMixinInterface,\n} from \"../elements/EFTemporal.js\";\nimport { TargetController } from \"../elements/TargetController.js\";\nimport { targetTemporalContext } from \"./ContextMixin.ts\";\nimport { TWMixin } from \"./TWMixin.js\";\nimport \"./timeline/EFTimeline.js\";\n\n@customElement(\"ef-filmstrip\")\nexport class EFFilmstrip extends TWMixin(LitElement) {\n // CSS custom properties are now defined in track components\n // Keep this class minimal as a wrapper\n\n @property({ type: String })\n target = \"\";\n\n @property({ type: Number, attribute: \"pixels-per-ms\" })\n pixelsPerMs = 0.04;\n\n @property({ type: Boolean, attribute: \"hide-playhead\" })\n hidePlayhead = false;\n\n @property({ type: Boolean, attribute: \"disable-internal-scroll\" })\n disableInternalScroll = false;\n\n @property({ type: String })\n hide = \"\";\n\n @property({ type: String })\n show = \"\";\n\n @state()\n targetElement: Element | null = null;\n\n #targetController?: TargetController;\n #lastTargetTemporal?: TemporalMixinInterface | null;\n\n @consume({ context: targetTemporalContext, subscribe: true })\n @state()\n private _contextProvidedTemporal?: TemporalMixinInterface | null;\n\n get targetTemporal(): TemporalMixinInterface | null {\n const fromTarget =\n this.targetElement && isEFTemporal(this.targetElement)\n ? (this.targetElement as TemporalMixinInterface & HTMLElement)\n : null;\n const fromContext = this._contextProvidedTemporal;\n\n if (fromTarget && fromContext && fromTarget !== fromContext) {\n console.warn(\n \"EFFilmstrip: Both target attribute and parent context found. Using target attribute.\",\n { target: this.target, fromTarget, fromContext },\n );\n }\n\n return fromTarget ?? fromContext ?? null;\n }\n\n get hideSelectors(): string[] | undefined {\n if (!this.hide) return undefined;\n return this.hide\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n\n get showSelectors(): string[] | undefined {\n if (!this.show) return undefined;\n return this.show\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n\n timelineRef = createRef<HTMLElement>();\n\n connectedCallback(): void {\n super.connectedCallback();\n if (this.target) {\n this.#targetController = new TargetController(this);\n }\n }\n\n protected willUpdate(\n changedProperties: Map<string | number | symbol, unknown>,\n ) {\n if (changedProperties.has(\"target\")) {\n if (this.target && !this.#targetController) {\n this.#targetController = new TargetController(this);\n }\n }\n\n const currentTargetTemporal = this.targetTemporal;\n if (this.#lastTargetTemporal !== currentTargetTemporal) {\n this.#lastTargetTemporal = currentTargetTemporal;\n }\n\n super.willUpdate(changedProperties);\n }\n\n render() {\n const targetId = this.targetTemporal\n ? ((this.targetTemporal as unknown as HTMLElement).id || this.target)\n : this.target;\n\n return html`\n <ef-timeline\n ${ref(this.timelineRef)}\n target=${targetId}\n control-target=${targetId}\n pixels-per-ms=${this.pixelsPerMs}\n ?show-playhead=${!this.hidePlayhead}\n ?show-controls=${false}\n ?show-ruler=${false}\n ?show-hierarchy=${true}\n ?show-playback-controls=${false}\n ?show-zoom-controls=${false}\n ?show-time-display=${false}\n hide=${this.hide}\n show=${this.show}\n ></ef-timeline>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-filmstrip\": EFFilmstrip;\n }\n}\n"],"mappings":";;;;;;;;;;;;AAeO,wBAAMA,sBAAoB,QAAQ,WAAW,CAAC;;;gBAK1C;qBAGK;sBAGC;+BAGS;cAGjB;cAGA;uBAGyB;qBA0ClB,WAAwB;;CAxCtC;CACA;CAMA,IAAI,iBAAgD;EAClD,MAAM,aACJ,KAAK,iBAAiB,aAAa,KAAK,cAAc,GACjD,KAAK,gBACN;EACN,MAAM,cAAc,KAAK;AAEzB,MAAI,cAAc,eAAe,eAAe,YAC9C,SAAQ,KACN,wFACA;GAAE,QAAQ,KAAK;GAAQ;GAAY;GAAa,CACjD;AAGH,SAAO,cAAc,eAAe;;CAGtC,IAAI,gBAAsC;AACxC,MAAI,CAAC,KAAK,KAAM,QAAO;AACvB,SAAO,KAAK,KACT,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,QAAQ,MAAM,EAAE,SAAS,EAAE;;CAGhC,IAAI,gBAAsC;AACxC,MAAI,CAAC,KAAK,KAAM,QAAO;AACvB,SAAO,KAAK,KACT,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,QAAQ,MAAM,EAAE,SAAS,EAAE;;CAKhC,oBAA0B;AACxB,QAAM,mBAAmB;AACzB,MAAI,KAAK,OACP,OAAKC,mBAAoB,IAAI,iBAAiB,KAAK;;CAIvD,AAAU,WACR,mBACA;AACA,MAAI,kBAAkB,IAAI,SAAS,EACjC;OAAI,KAAK,UAAU,CAAC,MAAKA,iBACvB,OAAKA,mBAAoB,IAAI,iBAAiB,KAAK;;EAIvD,MAAM,wBAAwB,KAAK;AACnC,MAAI,MAAKC,uBAAwB,sBAC/B,OAAKA,qBAAsB;AAG7B,QAAM,WAAW,kBAAkB;;CAGrC,SAAS;EACP,MAAM,WAAW,KAAK,iBAChB,KAAK,eAA0C,MAAM,KAAK,SAC5D,KAAK;AAET,SAAO,IAAI;;UAEL,IAAI,KAAK,YAAY,CAAC;iBACf,SAAS;yBACD,SAAS;wBACV,KAAK,YAAY;yBAChB,CAAC,KAAK,aAAa;yBACnB,MAAM;sBACT,MAAM;0BACF,KAAK;kCACG,MAAM;8BACV,MAAM;6BACP,MAAM;eACpB,KAAK,KAAK;eACV,KAAK,KAAK;;;;;YA1GtB,SAAS,EAAE,MAAM,QAAQ,CAAC;YAG1B,SAAS;CAAE,MAAM;CAAQ,WAAW;CAAiB,CAAC;YAGtD,SAAS;CAAE,MAAM;CAAS,WAAW;CAAiB,CAAC;YAGvD,SAAS;CAAE,MAAM;CAAS,WAAW;CAA2B,CAAC;YAGjE,SAAS,EAAE,MAAM,QAAQ,CAAC;YAG1B,SAAS,EAAE,MAAM,QAAQ,CAAC;YAG1B,OAAO;YAMP,QAAQ;CAAE,SAAS;CAAuB,WAAW;CAAM,CAAC,EAC5D,OAAO;0BA9BT,cAAc,eAAe"}
@@ -1,10 +1,10 @@
1
1
  import { LitElement } from "lit";
2
- import * as lit_html_directives_ref3 from "lit-html/directives/ref";
2
+ import * as lit_html_directives_ref1 from "lit-html/directives/ref";
3
3
 
4
4
  //#region src/gui/EFFitScale.d.ts
5
5
  declare class EFFitScale extends LitElement {
6
- containerRef: lit_html_directives_ref3.Ref<HTMLDivElement>;
7
- contentRef: lit_html_directives_ref3.Ref<HTMLSlotElement>;
6
+ containerRef: lit_html_directives_ref1.Ref<HTMLDivElement>;
7
+ contentRef: lit_html_directives_ref1.Ref<HTMLSlotElement>;
8
8
  createRenderRoot(): this;
9
9
  uniqueId: string;
10
10
  private scale;
@@ -14,18 +14,16 @@ let EFFitScale = class EFFitScale$1 extends LitElement {
14
14
  this.scaleLastSetOn = null;
15
15
  this.setScale = () => {
16
16
  if (this.isConnected) {
17
- const { scale } = this.scaleInfo;
18
- if (this.contentChild) {
19
- const containerRect = this.getBoundingClientRect();
20
- const contentRect = this.contentChild.getBoundingClientRect();
21
- const unscaledWidth = contentRect.width / this.scale;
22
- const unscaledHeight = contentRect.height / this.scale;
23
- const scaledWidth = unscaledWidth * scale;
24
- const scaledHeight = unscaledHeight * scale;
25
- const translateX = (containerRect.width - scaledWidth) / 2;
26
- const translateY = (containerRect.height - scaledHeight) / 2;
17
+ const { scale, containerWidth, containerHeight, contentWidth, contentHeight } = this.scaleInfo;
18
+ if (this.contentChild && contentWidth > 0 && contentHeight > 0) {
19
+ const scaledWidth = contentWidth * scale;
20
+ const scaledHeight = contentHeight * scale;
21
+ const translateX = (containerWidth - scaledWidth) / 2;
22
+ const translateY = (containerHeight - scaledHeight) / 2;
27
23
  if (this.scaleLastSetOn !== this.contentChild) this.removeScale();
28
24
  Object.assign(this.contentChild.style, {
25
+ width: `${contentWidth}px`,
26
+ height: `${contentHeight}px`,
29
27
  transform: `translate(${translateX.toFixed(4)}px, ${translateY.toFixed(4)}px) scale(${scale.toFixed(4)})`,
30
28
  transformOrigin: "top left"
31
29
  });
@@ -38,6 +36,8 @@ let EFFitScale = class EFFitScale$1 extends LitElement {
38
36
  this.removeScale = () => {
39
37
  if (this.scaleLastSetOn) {
40
38
  Object.assign(this.scaleLastSetOn.style, {
39
+ width: "",
40
+ height: "",
41
41
  transform: "",
42
42
  transformOrigin: ""
43
43
  });
@@ -113,8 +113,35 @@ let EFFitScale = class EFFitScale$1 extends LitElement {
113
113
  };
114
114
  const containerWidth = this.clientWidth;
115
115
  const containerHeight = this.clientHeight;
116
- const contentWidth = this.contentChild.clientWidth;
117
- const contentHeight = this.contentChild.clientHeight;
116
+ let contentWidth = 0;
117
+ let contentHeight = 0;
118
+ if (typeof this.contentChild.getNaturalDimensions === "function") {
119
+ const naturalDimensions = this.contentChild.getNaturalDimensions();
120
+ if (naturalDimensions && naturalDimensions.width > 0 && naturalDimensions.height > 0) {
121
+ contentWidth = naturalDimensions.width;
122
+ contentHeight = naturalDimensions.height;
123
+ if (this.contentChild.tagName === "EF-VIDEO") {
124
+ const canvas = this.contentChild.canvasElement;
125
+ if (canvas) {
126
+ canvas.style.setProperty("width", `${contentWidth}px`, "important");
127
+ canvas.style.setProperty("height", `${contentHeight}px`, "important");
128
+ }
129
+ }
130
+ } else {
131
+ contentWidth = this.contentChild.clientWidth;
132
+ contentHeight = this.contentChild.clientHeight;
133
+ }
134
+ } else {
135
+ contentWidth = this.contentChild.clientWidth;
136
+ contentHeight = this.contentChild.clientHeight;
137
+ }
138
+ if (contentWidth === 0 || contentHeight === 0) return {
139
+ scale: 1,
140
+ containerWidth,
141
+ containerHeight,
142
+ contentWidth: 0,
143
+ contentHeight: 0
144
+ };
118
145
  return {
119
146
  scale: containerWidth / containerHeight > contentWidth / contentHeight ? containerHeight / contentHeight : containerWidth / contentWidth,
120
147
  containerWidth,
@@ -1 +1 @@
1
- {"version":3,"file":"EFFitScale.js","names":["EFFitScale","results: HTMLElement[]","children","allContentElements: HTMLElement[]"],"sources":["../../src/gui/EFFitScale.ts"],"sourcesContent":["import { LitElement } from \"lit\";\nimport { customElement, state } from \"lit/decorators.js\";\nimport { createRef } from \"lit/directives/ref.js\";\n\n@customElement(\"ef-fit-scale\")\nexport class EFFitScale extends LitElement {\n containerRef = createRef<HTMLDivElement>();\n contentRef = createRef<HTMLSlotElement>();\n\n createRenderRoot() {\n Object.assign(this.style, {\n display: \"grid\",\n width: \"100%\",\n height: \"100%\",\n gridTemplateColumns: \"100%\",\n gridTemplateRows: \"100%\",\n overflow: \"hidden\",\n boxSizing: \"border-box\",\n contain: \"strict\",\n position: \"relative\",\n });\n this.id = `${this.uniqueId}`;\n return this;\n }\n\n uniqueId = Math.random().toString(36).substring(2, 15);\n\n @state()\n private scale = 1;\n\n private animationFrameId?: number;\n\n get contentChild() {\n if (!this.children.length) return null;\n\n const isNonContentElement = (element: Element): boolean => {\n const tagName = element.tagName.toLowerCase();\n const nonContentTags = [\n \"style\",\n \"script\",\n \"meta\",\n \"link\",\n \"title\",\n \"noscript\",\n ];\n if (nonContentTags.includes(tagName)) return true;\n\n try {\n const display = window.getComputedStyle(element).display;\n return display === \"none\" || display === \"contents\";\n } catch {\n return false;\n }\n };\n\n const findAllContentElements = (element: Element): HTMLElement[] => {\n const results: HTMLElement[] = [];\n\n if (element instanceof HTMLSlotElement) {\n const assigned = element.assignedElements()[0];\n if (assigned) {\n results.push(...findAllContentElements(assigned));\n }\n return results;\n }\n\n if (!isNonContentElement(element)) {\n results.push(element as HTMLElement);\n }\n\n const children = Array.from(element.children);\n for (let i = 0; i < children.length; i++) {\n const child = children[i];\n if (child) {\n results.push(...findAllContentElements(child));\n }\n }\n\n return results;\n };\n\n const children = Array.from(this.children);\n const allContentElements: HTMLElement[] = [];\n for (let i = 0; i < children.length; i++) {\n const child = children[i];\n if (child) {\n allContentElements.push(...findAllContentElements(child));\n }\n }\n\n if (allContentElements.length === 0) return null;\n\n return allContentElements[0] ?? null;\n }\n\n get scaleInfo() {\n if (!this.contentChild) {\n return {\n scale: 1,\n containerWidth: 0,\n containerHeight: 0,\n contentWidth: 0,\n contentHeight: 0,\n };\n }\n\n const containerWidth = this.clientWidth;\n const containerHeight = this.clientHeight;\n const contentWidth = this.contentChild.clientWidth;\n const contentHeight = this.contentChild.clientHeight;\n\n const containerRatio = containerWidth / containerHeight;\n const contentRatio = contentWidth / contentHeight;\n\n const scale =\n containerRatio > contentRatio\n ? containerHeight / contentHeight\n : containerWidth / contentWidth;\n\n return {\n scale,\n containerWidth,\n containerHeight,\n contentWidth,\n contentHeight,\n };\n }\n\n scaleLastSetOn: HTMLElement | null = null;\n\n setScale = () => {\n if (this.isConnected) {\n const { scale } = this.scaleInfo;\n if (this.contentChild) {\n const containerRect = this.getBoundingClientRect();\n const contentRect = this.contentChild.getBoundingClientRect();\n\n const unscaledWidth = contentRect.width / this.scale;\n const unscaledHeight = contentRect.height / this.scale;\n const scaledWidth = unscaledWidth * scale;\n const scaledHeight = unscaledHeight * scale;\n const translateX = (containerRect.width - scaledWidth) / 2;\n const translateY = (containerRect.height - scaledHeight) / 2;\n\n // In the rare event that the content child is changed, we need to remove the scale\n // because we don't want to have a scale on the old content child that is somewhere else in the DOM\n if (this.scaleLastSetOn !== this.contentChild) {\n this.removeScale();\n }\n // Use toFixed to avoid floating point precision issues\n // this will update every frame with sub-pixel changes if we don't pin it down\n Object.assign(this.contentChild.style, {\n transform: `translate(${translateX.toFixed(4)}px, ${translateY.toFixed(4)}px) scale(${scale.toFixed(4)})`,\n transformOrigin: \"top left\",\n });\n this.scale = scale;\n this.scaleLastSetOn = this.contentChild;\n }\n this.animationFrameId = requestAnimationFrame(this.setScale);\n }\n };\n\n removeScale = () => {\n if (this.scaleLastSetOn) {\n Object.assign(this.scaleLastSetOn.style, {\n transform: \"\",\n transformOrigin: \"\",\n });\n this.scaleLastSetOn = null;\n }\n };\n\n connectedCallback(): void {\n super.connectedCallback();\n this.animationFrameId = requestAnimationFrame(this.setScale);\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeScale();\n if (this.animationFrameId) {\n cancelAnimationFrame(this.animationFrameId);\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-fit-scale\": EFFitScale;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"EFFitScale.js","names":["EFFitScale","results: HTMLElement[]","children","allContentElements: HTMLElement[]"],"sources":["../../src/gui/EFFitScale.ts"],"sourcesContent":["import { LitElement } from \"lit\";\nimport { customElement, state } from \"lit/decorators.js\";\nimport { createRef } from \"lit/directives/ref.js\";\n\n@customElement(\"ef-fit-scale\")\nexport class EFFitScale extends LitElement {\n containerRef = createRef<HTMLDivElement>();\n contentRef = createRef<HTMLSlotElement>();\n\n createRenderRoot() {\n Object.assign(this.style, {\n display: \"grid\",\n width: \"100%\",\n height: \"100%\",\n gridTemplateColumns: \"100%\",\n gridTemplateRows: \"100%\",\n overflow: \"hidden\",\n boxSizing: \"border-box\",\n contain: \"strict\",\n position: \"relative\",\n });\n this.id = `${this.uniqueId}`;\n return this;\n }\n\n uniqueId = Math.random().toString(36).substring(2, 15);\n\n @state()\n private scale = 1;\n\n private animationFrameId?: number;\n\n get contentChild() {\n if (!this.children.length) return null;\n\n const isNonContentElement = (element: Element): boolean => {\n const tagName = element.tagName.toLowerCase();\n const nonContentTags = [\n \"style\",\n \"script\",\n \"meta\",\n \"link\",\n \"title\",\n \"noscript\",\n ];\n if (nonContentTags.includes(tagName)) return true;\n\n try {\n const display = window.getComputedStyle(element).display;\n return display === \"none\" || display === \"contents\";\n } catch {\n return false;\n }\n };\n\n const findAllContentElements = (element: Element): HTMLElement[] => {\n const results: HTMLElement[] = [];\n\n if (element instanceof HTMLSlotElement) {\n const assigned = element.assignedElements()[0];\n if (assigned) {\n results.push(...findAllContentElements(assigned));\n }\n return results;\n }\n\n if (!isNonContentElement(element)) {\n results.push(element as HTMLElement);\n }\n\n const children = Array.from(element.children);\n for (let i = 0; i < children.length; i++) {\n const child = children[i];\n if (child) {\n results.push(...findAllContentElements(child));\n }\n }\n\n return results;\n };\n\n const children = Array.from(this.children);\n const allContentElements: HTMLElement[] = [];\n for (let i = 0; i < children.length; i++) {\n const child = children[i];\n if (child) {\n allContentElements.push(...findAllContentElements(child));\n }\n }\n\n if (allContentElements.length === 0) return null;\n\n return allContentElements[0] ?? null;\n }\n\n get scaleInfo() {\n if (!this.contentChild) {\n return {\n scale: 1,\n containerWidth: 0,\n containerHeight: 0,\n contentWidth: 0,\n contentHeight: 0,\n };\n }\n\n const containerWidth = this.clientWidth;\n const containerHeight = this.clientHeight;\n\n // Try to get natural dimensions from media elements (ef-video, ef-image)\n let contentWidth = 0;\n let contentHeight = 0;\n\n if (typeof (this.contentChild as any).getNaturalDimensions === \"function\") {\n const naturalDimensions = (\n this.contentChild as any\n ).getNaturalDimensions() as { width: number; height: number } | null;\n if (\n naturalDimensions &&\n naturalDimensions.width > 0 &&\n naturalDimensions.height > 0\n ) {\n contentWidth = naturalDimensions.width;\n contentHeight = naturalDimensions.height;\n\n // ESSENTIAL: For ef-video, set canvas to explicit pixel dimensions to break 100% circular dependency\n // Canvas default CSS is width:100%, height:100% which would make ef-video collapse to 0x0\n // when ef-video is set to width:auto, height:auto by ElementRenderer\n if (this.contentChild.tagName === \"EF-VIDEO\") {\n const canvas = (this.contentChild as any).canvasElement;\n if (canvas) {\n canvas.style.setProperty(\"width\", `${contentWidth}px`, \"important\");\n canvas.style.setProperty(\n \"height\",\n `${contentHeight}px`,\n \"important\",\n );\n }\n }\n } else {\n // Natural dimensions not available yet, fall back to client dimensions\n contentWidth = this.contentChild.clientWidth;\n contentHeight = this.contentChild.clientHeight;\n }\n } else {\n // For other elements, use clientWidth/Height\n contentWidth = this.contentChild.clientWidth;\n contentHeight = this.contentChild.clientHeight;\n }\n\n if (contentWidth === 0 || contentHeight === 0) {\n return {\n scale: 1,\n containerWidth,\n containerHeight,\n contentWidth: 0,\n contentHeight: 0,\n };\n }\n\n const containerRatio = containerWidth / containerHeight;\n const contentRatio = contentWidth / contentHeight;\n\n const scale =\n containerRatio > contentRatio\n ? containerHeight / contentHeight\n : containerWidth / contentWidth;\n\n return {\n scale,\n containerWidth,\n containerHeight,\n contentWidth,\n contentHeight,\n };\n }\n\n scaleLastSetOn: HTMLElement | null = null;\n\n setScale = () => {\n if (this.isConnected) {\n const scaleInfo = this.scaleInfo;\n const {\n scale,\n containerWidth,\n containerHeight,\n contentWidth,\n contentHeight,\n } = scaleInfo;\n\n if (this.contentChild && contentWidth > 0 && contentHeight > 0) {\n // Calculate scaled dimensions using natural size from scaleInfo\n const scaledWidth = contentWidth * scale;\n const scaledHeight = contentHeight * scale;\n const translateX = (containerWidth - scaledWidth) / 2;\n const translateY = (containerHeight - scaledHeight) / 2;\n\n // In the rare event that the content child is changed, we need to remove the scale\n // because we don't want to have a scale on the old content child that is somewhere else in the DOM\n if (this.scaleLastSetOn !== this.contentChild) {\n this.removeScale();\n }\n // Use toFixed to avoid floating point precision issues\n // this will update every frame with sub-pixel changes if we don't pin it down\n Object.assign(this.contentChild.style, {\n width: `${contentWidth}px`,\n height: `${contentHeight}px`,\n transform: `translate(${translateX.toFixed(4)}px, ${translateY.toFixed(4)}px) scale(${scale.toFixed(4)})`,\n transformOrigin: \"top left\",\n });\n this.scale = scale;\n this.scaleLastSetOn = this.contentChild;\n }\n this.animationFrameId = requestAnimationFrame(this.setScale);\n }\n };\n\n removeScale = () => {\n if (this.scaleLastSetOn) {\n Object.assign(this.scaleLastSetOn.style, {\n width: \"\",\n height: \"\",\n transform: \"\",\n transformOrigin: \"\",\n });\n this.scaleLastSetOn = null;\n }\n };\n\n connectedCallback(): void {\n super.connectedCallback();\n this.animationFrameId = requestAnimationFrame(this.setScale);\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeScale();\n if (this.animationFrameId) {\n cancelAnimationFrame(this.animationFrameId);\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-fit-scale\": EFFitScale;\n }\n}\n"],"mappings":"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"}
@@ -1,16 +1,16 @@
1
- import * as lit24 from "lit";
1
+ import * as lit27 from "lit";
2
2
  import { LitElement } from "lit";
3
- import * as lit_html21 from "lit-html";
3
+ import * as lit_html26 from "lit-html";
4
4
  import * as lit_html_directives_ref_js3 from "lit-html/directives/ref.js";
5
5
 
6
6
  //#region src/gui/EFFocusOverlay.d.ts
7
7
  declare class EFFocusOverlay extends LitElement {
8
- static styles: lit24.CSSResult;
8
+ static styles: lit27.CSSResult;
9
9
  focusedElement?: HTMLElement | null;
10
10
  overlay: lit_html_directives_ref_js3.Ref<HTMLDivElement>;
11
11
  private animationFrame?;
12
12
  drawOverlay: () => void;
13
- render(): lit_html21.TemplateResult<1>;
13
+ render(): lit_html26.TemplateResult<1>;
14
14
  connectedCallback(): void;
15
15
  disconnectedCallback(): void;
16
16
  protected updated(): void;
@@ -0,0 +1,48 @@
1
+ import * as lit36 from "lit";
2
+ import { LitElement } from "lit";
3
+ import * as lit_html35 from "lit-html";
4
+
5
+ //#region src/gui/EFOverlayItem.d.ts
6
+ /**
7
+ * Position changed event detail.
8
+ */
9
+ interface OverlayItemPosition {
10
+ x: number;
11
+ y: number;
12
+ width: number;
13
+ height: number;
14
+ rotation: number;
15
+ }
16
+ /**
17
+ * Individual overlay item that tracks a target element.
18
+ * Must be a direct child of ef-overlay-layer.
19
+ *
20
+ * NEW ARCHITECTURE: This component is now PASSIVE - it does not run its own RAF loop.
21
+ * Instead, it registers with its parent EFOverlayLayer, which manages position updates
22
+ * for all children in a synchronized RAF loop. This eliminates race conditions and
23
+ * ensures transforms are applied before positions are read.
24
+ */
25
+ declare class EFOverlayItem extends LitElement {
26
+ static styles: lit36.CSSResult[];
27
+ elementId?: string;
28
+ target?: HTMLElement | string;
29
+ private currentPosition;
30
+ private resolveTarget;
31
+ /**
32
+ * Update position based on target element.
33
+ * Called by parent EFOverlayLayer in its synchronized RAF loop.
34
+ * PUBLIC API - called by parent layer.
35
+ */
36
+ updatePosition(): void;
37
+ connectedCallback(): void;
38
+ disconnectedCallback(): void;
39
+ render(): lit_html35.TemplateResult<1>;
40
+ }
41
+ declare global {
42
+ interface HTMLElementTagNameMap {
43
+ "ef-overlay-item": EFOverlayItem;
44
+ }
45
+ }
46
+ //#endregion
47
+ export { EFOverlayItem, OverlayItemPosition };
48
+ //# sourceMappingURL=EFOverlayItem.d.ts.map
@@ -0,0 +1,97 @@
1
+ import { __decorate } from "../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js";
2
+ import { parseRotationFromTransform } from "./transformCalculations.js";
3
+ import { LitElement, css, html } from "lit";
4
+ import { customElement, property } from "lit/decorators.js";
5
+
6
+ //#region src/gui/EFOverlayItem.ts
7
+ let EFOverlayItem = class EFOverlayItem$1 extends LitElement {
8
+ constructor(..._args) {
9
+ super(..._args);
10
+ this.currentPosition = null;
11
+ }
12
+ static {
13
+ this.styles = [css`
14
+ :host {
15
+ display: block;
16
+ position: absolute;
17
+ pointer-events: auto;
18
+ transform-origin: center;
19
+ }
20
+ `];
21
+ }
22
+ resolveTarget() {
23
+ if (this.elementId) {
24
+ let element = document.querySelector(`[data-element-id="${this.elementId}"]`);
25
+ if (!element) element = document.querySelector(`[data-timegroup-id="${this.elementId}"]`);
26
+ return element || null;
27
+ }
28
+ if (!this.target) return null;
29
+ if (typeof this.target === "string") return document.querySelector(this.target) || document.getElementById(this.target);
30
+ if (!this.target.isConnected) return null;
31
+ return this.target;
32
+ }
33
+ /**
34
+ * Update position based on target element.
35
+ * Called by parent EFOverlayLayer in its synchronized RAF loop.
36
+ * PUBLIC API - called by parent layer.
37
+ */
38
+ updatePosition() {
39
+ const targetElement = this.resolveTarget();
40
+ if (!targetElement) {
41
+ if (this.style.display !== "none") this.style.display = "none";
42
+ return;
43
+ }
44
+ if (this.style.display === "none") this.style.display = "";
45
+ const overlayLayer = this.parentElement;
46
+ if (!overlayLayer) return;
47
+ const overlayLayerRect = overlayLayer.getBoundingClientRect();
48
+ const targetRect = targetElement.getBoundingClientRect();
49
+ if (targetRect.width === 0 && targetRect.height === 0) return;
50
+ const rotation = parseRotationFromTransform(window.getComputedStyle(targetElement).transform);
51
+ const overlayPosition = {
52
+ x: targetRect.left - overlayLayerRect.left,
53
+ y: targetRect.top - overlayLayerRect.top,
54
+ width: targetRect.width,
55
+ height: targetRect.height,
56
+ rotation
57
+ };
58
+ if (overlayPosition.width <= 0 || overlayPosition.height <= 0) return;
59
+ if (!this.currentPosition || Math.abs(this.currentPosition.x - overlayPosition.x) > .01 || Math.abs(this.currentPosition.y - overlayPosition.y) > .01 || Math.abs(this.currentPosition.width - overlayPosition.width) > .01 || Math.abs(this.currentPosition.height - overlayPosition.height) > .01 || Math.abs(this.currentPosition.rotation - overlayPosition.rotation) > .01) {
60
+ this.currentPosition = overlayPosition;
61
+ this.style.left = `${overlayPosition.x}px`;
62
+ this.style.top = `${overlayPosition.y}px`;
63
+ this.style.width = `${overlayPosition.width}px`;
64
+ this.style.height = `${overlayPosition.height}px`;
65
+ if (overlayPosition.rotation !== 0) this.style.transform = `rotate(${overlayPosition.rotation}deg)`;
66
+ else this.style.transform = "";
67
+ this.dispatchEvent(new CustomEvent("position-changed", {
68
+ detail: { ...overlayPosition },
69
+ bubbles: true,
70
+ composed: true
71
+ }));
72
+ }
73
+ }
74
+ connectedCallback() {
75
+ super.connectedCallback();
76
+ const parent = this.parentElement;
77
+ if (parent && typeof parent.registerOverlayItem === "function") parent.registerOverlayItem(this);
78
+ }
79
+ disconnectedCallback() {
80
+ super.disconnectedCallback();
81
+ const parent = this.parentElement;
82
+ if (parent && typeof parent.unregisterOverlayItem === "function") parent.unregisterOverlayItem(this);
83
+ }
84
+ render() {
85
+ return html`<slot></slot>`;
86
+ }
87
+ };
88
+ __decorate([property({
89
+ type: String,
90
+ attribute: "element-id"
91
+ })], EFOverlayItem.prototype, "elementId", void 0);
92
+ __decorate([property({ attribute: false })], EFOverlayItem.prototype, "target", void 0);
93
+ EFOverlayItem = __decorate([customElement("ef-overlay-item")], EFOverlayItem);
94
+
95
+ //#endregion
96
+ export { EFOverlayItem };
97
+ //# sourceMappingURL=EFOverlayItem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EFOverlayItem.js","names":["EFOverlayItem"],"sources":["../../src/gui/EFOverlayItem.ts"],"sourcesContent":["import { css, html, LitElement } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport type { EFOverlayLayer } from \"./EFOverlayLayer.js\";\nimport { parseRotationFromTransform } from \"./transformCalculations.js\";\n\n/**\n * Position changed event detail.\n */\nexport interface OverlayItemPosition {\n x: number;\n y: number;\n width: number;\n height: number;\n rotation: number;\n}\n\n/**\n * Individual overlay item that tracks a target element.\n * Must be a direct child of ef-overlay-layer.\n *\n * NEW ARCHITECTURE: This component is now PASSIVE - it does not run its own RAF loop.\n * Instead, it registers with its parent EFOverlayLayer, which manages position updates\n * for all children in a synchronized RAF loop. This eliminates race conditions and\n * ensures transforms are applied before positions are read.\n */\n@customElement(\"ef-overlay-item\")\nexport class EFOverlayItem extends LitElement {\n static styles = [\n css`\n :host {\n display: block;\n position: absolute;\n pointer-events: auto;\n transform-origin: center;\n }\n `,\n ];\n\n @property({ type: String, attribute: \"element-id\" })\n elementId?: string;\n\n @property({ attribute: false })\n target?: HTMLElement | string;\n\n private currentPosition: OverlayItemPosition | null = null;\n\n private resolveTarget(): HTMLElement | null {\n if (this.elementId) {\n let element = document.querySelector(\n `[data-element-id=\"${this.elementId}\"]`,\n ) as HTMLElement;\n\n if (!element) {\n element = document.querySelector(\n `[data-timegroup-id=\"${this.elementId}\"]`,\n ) as HTMLElement;\n }\n\n return element || null;\n }\n\n if (!this.target) return null;\n\n if (typeof this.target === \"string\") {\n return (\n (document.querySelector(this.target) as HTMLElement) ||\n document.getElementById(this.target)\n );\n }\n\n // For direct HTMLElement references, verify the element is still in the DOM\n if (!this.target.isConnected) {\n return null;\n }\n\n return this.target;\n }\n\n /**\n * Update position based on target element.\n * Called by parent EFOverlayLayer in its synchronized RAF loop.\n * PUBLIC API - called by parent layer.\n */\n updatePosition() {\n const targetElement = this.resolveTarget();\n if (!targetElement) {\n // Target not found - hide the overlay item\n if (this.style.display !== \"none\") {\n this.style.display = \"none\";\n }\n return;\n }\n\n // Target found - ensure we're visible\n if (this.style.display === \"none\") {\n this.style.display = \"\";\n }\n\n // Use parent element as overlay layer (like old system)\n const overlayLayer = this.parentElement;\n if (!overlayLayer) {\n return;\n }\n\n const overlayLayerRect = overlayLayer.getBoundingClientRect();\n const targetRect = targetElement.getBoundingClientRect();\n\n if (targetRect.width === 0 && targetRect.height === 0) return;\n\n const computedStyle = window.getComputedStyle(targetElement);\n const rotation = parseRotationFromTransform(computedStyle.transform);\n\n const overlayPosition = {\n x: targetRect.left - overlayLayerRect.left,\n y: targetRect.top - overlayLayerRect.top,\n width: targetRect.width,\n height: targetRect.height,\n rotation,\n };\n\n if (overlayPosition.width <= 0 || overlayPosition.height <= 0) return;\n\n const positionChanged =\n !this.currentPosition ||\n Math.abs(this.currentPosition.x - overlayPosition.x) > 0.01 ||\n Math.abs(this.currentPosition.y - overlayPosition.y) > 0.01 ||\n Math.abs(this.currentPosition.width - overlayPosition.width) > 0.01 ||\n Math.abs(this.currentPosition.height - overlayPosition.height) > 0.01 ||\n Math.abs(this.currentPosition.rotation - overlayPosition.rotation) > 0.01;\n\n if (positionChanged) {\n this.currentPosition = overlayPosition;\n\n this.style.left = `${overlayPosition.x}px`;\n this.style.top = `${overlayPosition.y}px`;\n this.style.width = `${overlayPosition.width}px`;\n this.style.height = `${overlayPosition.height}px`;\n\n if (overlayPosition.rotation !== 0) {\n this.style.transform = `rotate(${overlayPosition.rotation}deg)`;\n } else {\n this.style.transform = \"\";\n }\n\n this.dispatchEvent(\n new CustomEvent<OverlayItemPosition>(\"position-changed\", {\n detail: { ...overlayPosition },\n bubbles: true,\n composed: true,\n }),\n );\n }\n }\n\n connectedCallback() {\n super.connectedCallback();\n // Register with parent overlay layer for coordinated updates\n const parent = this.parentElement as EFOverlayLayer | null;\n if (parent && typeof (parent as any).registerOverlayItem === \"function\") {\n (parent as any).registerOverlayItem(this);\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n // Unregister from parent overlay layer\n const parent = this.parentElement as EFOverlayLayer | null;\n if (parent && typeof (parent as any).unregisterOverlayItem === \"function\") {\n (parent as any).unregisterOverlayItem(this);\n }\n }\n\n render() {\n return html`<slot></slot>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-overlay-item\": EFOverlayItem;\n }\n}\n"],"mappings":";;;;;;AA0BO,0BAAMA,wBAAsB,WAAW;;;yBAkBU;;;gBAjBtC,CACd,GAAG;;;;;;;MAQJ;;CAUD,AAAQ,gBAAoC;AAC1C,MAAI,KAAK,WAAW;GAClB,IAAI,UAAU,SAAS,cACrB,qBAAqB,KAAK,UAAU,IACrC;AAED,OAAI,CAAC,QACH,WAAU,SAAS,cACjB,uBAAuB,KAAK,UAAU,IACvC;AAGH,UAAO,WAAW;;AAGpB,MAAI,CAAC,KAAK,OAAQ,QAAO;AAEzB,MAAI,OAAO,KAAK,WAAW,SACzB,QACG,SAAS,cAAc,KAAK,OAAO,IACpC,SAAS,eAAe,KAAK,OAAO;AAKxC,MAAI,CAAC,KAAK,OAAO,YACf,QAAO;AAGT,SAAO,KAAK;;;;;;;CAQd,iBAAiB;EACf,MAAM,gBAAgB,KAAK,eAAe;AAC1C,MAAI,CAAC,eAAe;AAElB,OAAI,KAAK,MAAM,YAAY,OACzB,MAAK,MAAM,UAAU;AAEvB;;AAIF,MAAI,KAAK,MAAM,YAAY,OACzB,MAAK,MAAM,UAAU;EAIvB,MAAM,eAAe,KAAK;AAC1B,MAAI,CAAC,aACH;EAGF,MAAM,mBAAmB,aAAa,uBAAuB;EAC7D,MAAM,aAAa,cAAc,uBAAuB;AAExD,MAAI,WAAW,UAAU,KAAK,WAAW,WAAW,EAAG;EAGvD,MAAM,WAAW,2BADK,OAAO,iBAAiB,cAAc,CACF,UAAU;EAEpE,MAAM,kBAAkB;GACtB,GAAG,WAAW,OAAO,iBAAiB;GACtC,GAAG,WAAW,MAAM,iBAAiB;GACrC,OAAO,WAAW;GAClB,QAAQ,WAAW;GACnB;GACD;AAED,MAAI,gBAAgB,SAAS,KAAK,gBAAgB,UAAU,EAAG;AAU/D,MAPE,CAAC,KAAK,mBACN,KAAK,IAAI,KAAK,gBAAgB,IAAI,gBAAgB,EAAE,GAAG,OACvD,KAAK,IAAI,KAAK,gBAAgB,IAAI,gBAAgB,EAAE,GAAG,OACvD,KAAK,IAAI,KAAK,gBAAgB,QAAQ,gBAAgB,MAAM,GAAG,OAC/D,KAAK,IAAI,KAAK,gBAAgB,SAAS,gBAAgB,OAAO,GAAG,OACjE,KAAK,IAAI,KAAK,gBAAgB,WAAW,gBAAgB,SAAS,GAAG,KAElD;AACnB,QAAK,kBAAkB;AAEvB,QAAK,MAAM,OAAO,GAAG,gBAAgB,EAAE;AACvC,QAAK,MAAM,MAAM,GAAG,gBAAgB,EAAE;AACtC,QAAK,MAAM,QAAQ,GAAG,gBAAgB,MAAM;AAC5C,QAAK,MAAM,SAAS,GAAG,gBAAgB,OAAO;AAE9C,OAAI,gBAAgB,aAAa,EAC/B,MAAK,MAAM,YAAY,UAAU,gBAAgB,SAAS;OAE1D,MAAK,MAAM,YAAY;AAGzB,QAAK,cACH,IAAI,YAAiC,oBAAoB;IACvD,QAAQ,EAAE,GAAG,iBAAiB;IAC9B,SAAS;IACT,UAAU;IACX,CAAC,CACH;;;CAIL,oBAAoB;AAClB,QAAM,mBAAmB;EAEzB,MAAM,SAAS,KAAK;AACpB,MAAI,UAAU,OAAQ,OAAe,wBAAwB,WAC3D,CAAC,OAAe,oBAAoB,KAAK;;CAI7C,uBAAuB;AACrB,QAAM,sBAAsB;EAE5B,MAAM,SAAS,KAAK;AACpB,MAAI,UAAU,OAAQ,OAAe,0BAA0B,WAC7D,CAAC,OAAe,sBAAsB,KAAK;;CAI/C,SAAS;AACP,SAAO,IAAI;;;YAvIZ,SAAS;CAAE,MAAM;CAAQ,WAAW;CAAc,CAAC;YAGnD,SAAS,EAAE,WAAW,OAAO,CAAC;4BAhBhC,cAAc,kBAAkB"}
@@ -0,0 +1,70 @@
1
+ import { PanZoomTransform } from "../elements/EFPanZoom.js";
2
+ import { EFOverlayItem } from "./EFOverlayItem.js";
3
+ import * as lit35 from "lit";
4
+ import { LitElement } from "lit";
5
+ import * as lit_html34 from "lit-html";
6
+
7
+ //#region src/gui/EFOverlayLayer.d.ts
8
+
9
+ /**
10
+ * Overlay layer container component.
11
+ *
12
+ * SIMPLE ARCHITECTURE:
13
+ * - Runs a SINGLE RAF loop that updates everything every frame
14
+ * - No change detection, no optimization, no delays
15
+ * - Just: read transform → apply it → update all items → next frame
16
+ *
17
+ * Core responsibilities:
18
+ * - Consumes PanZoom transform from context (if PanZoom is ancestor)
19
+ * - Accepts transform via props as fallback (for sibling PanZoom)
20
+ * - Applies translate-only transform (no scale) directly to host element
21
+ * - Manages registered child EFOverlayItem components
22
+ * - Updates all overlays every animation frame
23
+ *
24
+ * The transform is applied directly to the host element so that:
25
+ * 1. getBoundingClientRect() returns the transformed position
26
+ * 2. EFOverlayItem can use this rect for coordinate calculations
27
+ */
28
+ declare class EFOverlayLayer extends LitElement {
29
+ static styles: lit35.CSSResult[];
30
+ panZoomTransformFromContext?: PanZoomTransform;
31
+ /**
32
+ * Pan/zoom transform as fallback for when context or sibling PanZoom is not available.
33
+ * Primarily used for testing and standalone usage.
34
+ */
35
+ panZoomTransform?: PanZoomTransform;
36
+ private registeredItems;
37
+ private rafId;
38
+ /**
39
+ * Register an overlay item for coordinated updates.
40
+ * Called by EFOverlayItem in connectedCallback.
41
+ */
42
+ registerOverlayItem(item: EFOverlayItem): void;
43
+ /**
44
+ * Unregister an overlay item.
45
+ * Called by EFOverlayItem in disconnectedCallback.
46
+ */
47
+ unregisterOverlayItem(item: EFOverlayItem): void;
48
+ /**
49
+ * Single source of truth for reading the current transform.
50
+ * Priority: context > sibling DOM read > prop > default
51
+ */
52
+ private readTransform;
53
+ /**
54
+ * Simple RAF loop: Update everything on every frame.
55
+ */
56
+ private startLoop;
57
+ private stopLoop;
58
+ connectedCallback(): void;
59
+ disconnectedCallback(): void;
60
+ updated(): void;
61
+ render(): lit_html34.TemplateResult<1>;
62
+ }
63
+ declare global {
64
+ interface HTMLElementTagNameMap {
65
+ "ef-overlay-layer": EFOverlayLayer;
66
+ }
67
+ }
68
+ //#endregion
69
+ export { EFOverlayLayer };
70
+ //# sourceMappingURL=EFOverlayLayer.d.ts.map