@editframe/elements 0.37.3-beta → 0.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) hide show
  1. package/dist/EF_FRAMEGEN.js +17 -14
  2. package/dist/EF_FRAMEGEN.js.map +1 -1
  3. package/dist/EF_RENDERING.js.map +1 -1
  4. package/dist/canvas/EFCanvas.d.ts +9 -2
  5. package/dist/canvas/EFCanvas.js +14 -4
  6. package/dist/canvas/EFCanvas.js.map +1 -1
  7. package/dist/canvas/EFCanvasItem.d.ts +2 -2
  8. package/dist/canvas/overlays/SelectionOverlay.d.ts +10 -2
  9. package/dist/canvas/overlays/SelectionOverlay.js +5 -12
  10. package/dist/canvas/overlays/SelectionOverlay.js.map +1 -1
  11. package/dist/canvas/overlays/overlayState.js.map +1 -1
  12. package/dist/canvas/selection/SelectionController.js.map +1 -1
  13. package/dist/elements/EFAudio.d.ts +1 -11
  14. package/dist/elements/EFAudio.js +2 -10
  15. package/dist/elements/EFAudio.js.map +1 -1
  16. package/dist/elements/EFCaptions.d.ts +5 -9
  17. package/dist/elements/EFCaptions.js +34 -11
  18. package/dist/elements/EFCaptions.js.map +1 -1
  19. package/dist/elements/EFImage.d.ts +10 -8
  20. package/dist/elements/EFImage.js +117 -32
  21. package/dist/elements/EFImage.js.map +1 -1
  22. package/dist/elements/EFMedia/AssetMediaEngine.js +2 -2
  23. package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
  24. package/dist/elements/EFMedia/BaseMediaEngine.js +15 -92
  25. package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
  26. package/dist/elements/EFMedia/BufferedSeekingInput.js +10 -11
  27. package/dist/elements/EFMedia/BufferedSeekingInput.js.map +1 -1
  28. package/dist/elements/EFMedia/{AssetIdMediaEngine.js → FileMediaEngine.js} +44 -24
  29. package/dist/elements/EFMedia/FileMediaEngine.js.map +1 -0
  30. package/dist/elements/EFMedia/JitMediaEngine.js +14 -13
  31. package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
  32. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -3
  33. package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
  34. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +12 -7
  35. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
  36. package/dist/elements/EFMedia/shared/timeoutUtils.js +44 -0
  37. package/dist/elements/EFMedia/shared/timeoutUtils.js.map +1 -0
  38. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +1 -1
  39. package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
  40. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +4 -4
  41. package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
  42. package/dist/elements/EFMedia.d.ts +14 -8
  43. package/dist/elements/EFMedia.js +52 -19
  44. package/dist/elements/EFMedia.js.map +1 -1
  45. package/dist/elements/EFPanZoom.d.ts +2 -2
  46. package/dist/elements/EFPanZoom.js +1 -1
  47. package/dist/elements/EFPanZoom.js.map +1 -1
  48. package/dist/elements/EFSourceMixin.js +16 -8
  49. package/dist/elements/EFSourceMixin.js.map +1 -1
  50. package/dist/elements/EFSurface.d.ts +5 -8
  51. package/dist/elements/EFSurface.js +4 -43
  52. package/dist/elements/EFSurface.js.map +1 -1
  53. package/dist/elements/EFTemporal.d.ts +33 -8
  54. package/dist/elements/EFTemporal.js +92 -40
  55. package/dist/elements/EFTemporal.js.map +1 -1
  56. package/dist/elements/EFText.d.ts +3 -0
  57. package/dist/elements/EFText.js +54 -21
  58. package/dist/elements/EFText.js.map +1 -1
  59. package/dist/elements/EFTextSegment.js +8 -4
  60. package/dist/elements/EFTextSegment.js.map +1 -1
  61. package/dist/elements/EFTimegroup.d.ts +26 -43
  62. package/dist/elements/EFTimegroup.js +295 -314
  63. package/dist/elements/EFTimegroup.js.map +1 -1
  64. package/dist/elements/EFVideo.d.ts +44 -42
  65. package/dist/elements/EFVideo.js +259 -172
  66. package/dist/elements/EFVideo.js.map +1 -1
  67. package/dist/elements/EFWaveform.d.ts +3 -8
  68. package/dist/elements/EFWaveform.js +18 -13
  69. package/dist/elements/EFWaveform.js.map +1 -1
  70. package/dist/elements/ElementPositionInfo.js.map +1 -1
  71. package/dist/elements/FetchMixin.js.map +1 -1
  72. package/dist/elements/TargetController.d.ts +0 -3
  73. package/dist/elements/TargetController.js +12 -35
  74. package/dist/elements/TargetController.js.map +1 -1
  75. package/dist/elements/TimegroupController.js.map +1 -1
  76. package/dist/elements/cloneFactoryRegistry.d.ts +14 -0
  77. package/dist/elements/cloneFactoryRegistry.js +15 -0
  78. package/dist/elements/cloneFactoryRegistry.js.map +1 -0
  79. package/dist/elements/renderTemporalAudio.js +8 -6
  80. package/dist/elements/renderTemporalAudio.js.map +1 -1
  81. package/dist/elements/setupTemporalHierarchy.js +62 -0
  82. package/dist/elements/setupTemporalHierarchy.js.map +1 -0
  83. package/dist/elements/updateAnimations.js +62 -87
  84. package/dist/elements/updateAnimations.js.map +1 -1
  85. package/dist/getRenderInfo.d.ts +3 -2
  86. package/dist/getRenderInfo.js +20 -4
  87. package/dist/getRenderInfo.js.map +1 -1
  88. package/dist/gui/ContextMixin.js +68 -12
  89. package/dist/gui/ContextMixin.js.map +1 -1
  90. package/dist/gui/Controllable.js +1 -1
  91. package/dist/gui/Controllable.js.map +1 -1
  92. package/dist/gui/EFActiveRootTemporal.d.ts +2 -2
  93. package/dist/gui/EFActiveRootTemporal.js.map +1 -1
  94. package/dist/gui/EFControls.d.ts +2 -2
  95. package/dist/gui/EFControls.js +2 -2
  96. package/dist/gui/EFControls.js.map +1 -1
  97. package/dist/gui/EFDial.d.ts +2 -2
  98. package/dist/gui/EFDial.js +12 -9
  99. package/dist/gui/EFDial.js.map +1 -1
  100. package/dist/gui/EFFilmstrip.d.ts +2 -0
  101. package/dist/gui/EFFilmstrip.js +18 -10
  102. package/dist/gui/EFFilmstrip.js.map +1 -1
  103. package/dist/gui/EFFitScale.d.ts +28 -4
  104. package/dist/gui/EFFitScale.js +88 -26
  105. package/dist/gui/EFFitScale.js.map +1 -1
  106. package/dist/gui/EFFocusOverlay.d.ts +2 -2
  107. package/dist/gui/EFFocusOverlay.js +3 -3
  108. package/dist/gui/EFFocusOverlay.js.map +1 -1
  109. package/dist/gui/EFOverlayItem.d.ts +2 -2
  110. package/dist/gui/EFOverlayLayer.d.ts +2 -2
  111. package/dist/gui/EFPause.d.ts +2 -2
  112. package/dist/gui/EFPause.js +1 -1
  113. package/dist/gui/EFPlay.d.ts +2 -2
  114. package/dist/gui/EFPlay.js +1 -1
  115. package/dist/gui/EFPreview.js +1 -1
  116. package/dist/gui/EFResizableBox.d.ts +2 -2
  117. package/dist/gui/EFResizableBox.js +5 -5
  118. package/dist/gui/EFResizableBox.js.map +1 -1
  119. package/dist/gui/EFScrubber.d.ts +2 -2
  120. package/dist/gui/EFScrubber.js +8 -13
  121. package/dist/gui/EFScrubber.js.map +1 -1
  122. package/dist/gui/EFTimeDisplay.d.ts +6 -2
  123. package/dist/gui/EFTimeDisplay.js +25 -7
  124. package/dist/gui/EFTimeDisplay.js.map +1 -1
  125. package/dist/gui/EFTimelineRuler.d.ts +2 -2
  126. package/dist/gui/EFTimelineRuler.js +3 -3
  127. package/dist/gui/EFTimelineRuler.js.map +1 -1
  128. package/dist/gui/EFToggleLoop.d.ts +2 -2
  129. package/dist/gui/EFToggleLoop.js +1 -1
  130. package/dist/gui/EFTogglePlay.d.ts +2 -2
  131. package/dist/gui/EFTogglePlay.js +1 -1
  132. package/dist/gui/EFTransformHandles.d.ts +2 -2
  133. package/dist/gui/EFTransformHandles.js +6 -6
  134. package/dist/gui/EFTransformHandles.js.map +1 -1
  135. package/dist/gui/EFWorkbench.d.ts +40 -36
  136. package/dist/gui/EFWorkbench.js +436 -822
  137. package/dist/gui/EFWorkbench.js.map +1 -1
  138. package/dist/gui/FitScaleHelpers.js.map +1 -1
  139. package/dist/gui/PlaybackController.d.ts +3 -8
  140. package/dist/gui/PlaybackController.js +59 -56
  141. package/dist/gui/PlaybackController.js.map +1 -1
  142. package/dist/gui/TWMixin.js +1 -1
  143. package/dist/gui/TWMixin.js.map +1 -1
  144. package/dist/gui/TargetOrContextMixin.js +43 -6
  145. package/dist/gui/TargetOrContextMixin.js.map +1 -1
  146. package/dist/gui/ef-theme.css +136 -0
  147. package/dist/gui/hierarchy/EFHierarchy.d.ts +2 -2
  148. package/dist/gui/hierarchy/EFHierarchy.js +14 -24
  149. package/dist/gui/hierarchy/EFHierarchy.js.map +1 -1
  150. package/dist/gui/hierarchy/EFHierarchyItem.d.ts +3 -3
  151. package/dist/gui/hierarchy/EFHierarchyItem.js +22 -10
  152. package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -1
  153. package/dist/gui/icons.js.map +1 -1
  154. package/dist/gui/previewSettingsContext.d.ts +18 -0
  155. package/dist/gui/previewSettingsContext.js.map +1 -1
  156. package/dist/gui/theme.js +34 -0
  157. package/dist/gui/theme.js.map +1 -0
  158. package/dist/gui/timeline/EFTimeline.d.ts +2 -2
  159. package/dist/gui/timeline/EFTimeline.js +70 -52
  160. package/dist/gui/timeline/EFTimeline.js.map +1 -1
  161. package/dist/gui/timeline/EFTimelineRow.d.ts +5 -3
  162. package/dist/gui/timeline/EFTimelineRow.js +55 -32
  163. package/dist/gui/timeline/EFTimelineRow.js.map +1 -1
  164. package/dist/gui/timeline/TrimHandles.d.ts +23 -9
  165. package/dist/gui/timeline/TrimHandles.js +224 -51
  166. package/dist/gui/timeline/TrimHandles.js.map +1 -1
  167. package/dist/gui/timeline/flattenHierarchy.js.map +1 -1
  168. package/dist/gui/timeline/timelineEditingContext.d.ts +34 -0
  169. package/dist/gui/timeline/timelineEditingContext.js +24 -0
  170. package/dist/gui/timeline/timelineEditingContext.js.map +1 -0
  171. package/dist/gui/timeline/timelineStateContext.js.map +1 -1
  172. package/dist/gui/timeline/tracks/AudioTrack.js +1 -1
  173. package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -1
  174. package/dist/gui/timeline/tracks/CaptionsTrack.d.ts +2 -3
  175. package/dist/gui/timeline/tracks/CaptionsTrack.js +17 -75
  176. package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -1
  177. package/dist/gui/timeline/tracks/EFThumbnailStrip.d.ts +52 -0
  178. package/dist/gui/timeline/tracks/EFThumbnailStrip.js +596 -0
  179. package/dist/gui/timeline/tracks/EFThumbnailStrip.js.map +1 -0
  180. package/dist/gui/timeline/tracks/HTMLTrack.js.map +1 -1
  181. package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -1
  182. package/dist/gui/timeline/tracks/TextTrack.d.ts +3 -2
  183. package/dist/gui/timeline/tracks/TextTrack.js +17 -43
  184. package/dist/gui/timeline/tracks/TextTrack.js.map +1 -1
  185. package/dist/gui/timeline/tracks/TimegroupTrack.d.ts +3 -4
  186. package/dist/gui/timeline/tracks/TimegroupTrack.js +33 -23
  187. package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -1
  188. package/dist/gui/timeline/tracks/TrackItem.d.ts +7 -9
  189. package/dist/gui/timeline/tracks/TrackItem.js +18 -17
  190. package/dist/gui/timeline/tracks/TrackItem.js.map +1 -1
  191. package/dist/gui/timeline/tracks/VideoTrack.d.ts +3 -3
  192. package/dist/gui/timeline/tracks/VideoTrack.js +11 -14
  193. package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -1
  194. package/dist/gui/timeline/tracks/WaveformTrack.js.map +1 -1
  195. package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -1
  196. package/dist/gui/timeline/tracks/waveformUtils.js +1 -1
  197. package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -1
  198. package/dist/gui/tree/EFTree.d.ts +2 -2
  199. package/dist/gui/tree/EFTree.js +8 -14
  200. package/dist/gui/tree/EFTree.js.map +1 -1
  201. package/dist/gui/tree/EFTreeItem.d.ts +2 -2
  202. package/dist/gui/tree/EFTreeItem.js +3 -3
  203. package/dist/gui/tree/EFTreeItem.js.map +1 -1
  204. package/dist/gui/tree/treeContext.js.map +1 -1
  205. package/dist/index.d.ts +10 -8
  206. package/dist/index.js +6 -5
  207. package/dist/index.js.map +1 -1
  208. package/dist/node.d.ts +2 -2
  209. package/dist/node.js +2 -2
  210. package/dist/preview/AdaptiveResolutionTracker.js +3 -3
  211. package/dist/preview/AdaptiveResolutionTracker.js.map +1 -1
  212. package/dist/preview/FrameController.d.ts +2 -17
  213. package/dist/preview/FrameController.js +40 -63
  214. package/dist/preview/FrameController.js.map +1 -1
  215. package/dist/preview/QualityUpgradeScheduler.d.ts +76 -0
  216. package/dist/preview/QualityUpgradeScheduler.js +158 -0
  217. package/dist/preview/QualityUpgradeScheduler.js.map +1 -0
  218. package/dist/preview/RenderContext.d.ts +119 -1
  219. package/dist/preview/RenderContext.js +21 -3
  220. package/dist/preview/RenderContext.js.map +1 -1
  221. package/dist/preview/RenderProfiler.js.map +1 -1
  222. package/dist/preview/RenderStats.js +85 -0
  223. package/dist/preview/RenderStats.js.map +1 -0
  224. package/dist/preview/encoding/canvasEncoder.js +2 -52
  225. package/dist/preview/encoding/canvasEncoder.js.map +1 -1
  226. package/dist/preview/encoding/mainThreadEncoder.js.map +1 -1
  227. package/dist/preview/encoding/workerEncoder.js.map +1 -1
  228. package/dist/preview/logger.js.map +1 -1
  229. package/dist/preview/previewSettings.d.ts +34 -0
  230. package/dist/preview/previewSettings.js +29 -17
  231. package/dist/preview/previewSettings.js.map +1 -1
  232. package/dist/preview/previewTypes.js +4 -4
  233. package/dist/preview/previewTypes.js.map +1 -1
  234. package/dist/preview/renderElementToCanvas.d.ts +44 -0
  235. package/dist/preview/renderElementToCanvas.js +72 -0
  236. package/dist/preview/renderElementToCanvas.js.map +1 -0
  237. package/dist/preview/renderTimegroupToCanvas.d.ts +134 -32
  238. package/dist/preview/renderTimegroupToCanvas.js +321 -146
  239. package/dist/preview/renderTimegroupToCanvas.js.map +1 -1
  240. package/dist/preview/renderTimegroupToCanvas.types.d.ts +51 -0
  241. package/dist/preview/renderTimegroupToVideo.d.ts +20 -35
  242. package/dist/preview/renderTimegroupToVideo.js +94 -106
  243. package/dist/preview/renderTimegroupToVideo.js.map +1 -1
  244. package/dist/preview/renderTimegroupToVideo.types.d.ts +42 -0
  245. package/dist/preview/renderVideoToVideo.js +286 -0
  246. package/dist/preview/renderVideoToVideo.js.map +1 -0
  247. package/dist/preview/renderers.d.ts +56 -0
  248. package/dist/preview/renderers.js +13 -1
  249. package/dist/preview/renderers.js.map +1 -1
  250. package/dist/preview/rendering/ScaleConfig.js +74 -0
  251. package/dist/preview/rendering/ScaleConfig.js.map +1 -0
  252. package/dist/preview/rendering/inlineImages.d.ts +13 -0
  253. package/dist/preview/rendering/inlineImages.js +7 -44
  254. package/dist/preview/rendering/inlineImages.js.map +1 -1
  255. package/dist/preview/rendering/loadImage.d.ts +8 -0
  256. package/dist/preview/rendering/loadImage.js +22 -0
  257. package/dist/preview/rendering/loadImage.js.map +1 -0
  258. package/dist/preview/rendering/renderToImageNative.js +3 -3
  259. package/dist/preview/rendering/renderToImageNative.js.map +1 -1
  260. package/dist/preview/rendering/serializeTimelineDirect.js +224 -68
  261. package/dist/preview/rendering/serializeTimelineDirect.js.map +1 -1
  262. package/dist/preview/statsTrackingStrategy.js +1 -101
  263. package/dist/preview/statsTrackingStrategy.js.map +1 -1
  264. package/dist/preview/workers/WorkerPool.js +0 -1
  265. package/dist/preview/workers/WorkerPool.js.map +1 -1
  266. package/dist/preview/workers/encoderWorkerInline.js +21 -54
  267. package/dist/preview/workers/encoderWorkerInline.js.map +1 -1
  268. package/dist/render/EFRenderAPI.d.ts +2 -1
  269. package/dist/render/EFRenderAPI.js +12 -36
  270. package/dist/render/EFRenderAPI.js.map +1 -1
  271. package/dist/render/getRenderData.js +4 -4
  272. package/dist/render/getRenderData.js.map +1 -1
  273. package/dist/style.css +114 -163
  274. package/dist/transcoding/cache/RequestDeduplicator.js +1 -0
  275. package/dist/transcoding/cache/RequestDeduplicator.js.map +1 -1
  276. package/dist/transcoding/types/index.d.ts +1 -1
  277. package/dist/transcoding/utils/UrlGenerator.js +10 -3
  278. package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
  279. package/dist/utils/LRUCache.js +1 -0
  280. package/dist/utils/LRUCache.js.map +1 -1
  281. package/dist/utils/frameTime.js +23 -1
  282. package/dist/utils/frameTime.js.map +1 -1
  283. package/package.json +45 -8
  284. package/scripts/build-css.js +8 -1
  285. package/test/setup.ts +0 -1
  286. package/test/useAssetMSW.ts +50 -0
  287. package/test/visualRegressionUtils.ts +23 -9
  288. package/tsdown.config.ts +6 -1
  289. package/dist/_virtual/rolldown_runtime.js +0 -27
  290. package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +0 -1
  291. package/dist/elements/EFThumbnailStrip.d.ts +0 -167
  292. package/dist/elements/EFThumbnailStrip.js +0 -731
  293. package/dist/elements/EFThumbnailStrip.js.map +0 -1
  294. package/dist/elements/SessionThumbnailCache.js +0 -154
  295. package/dist/elements/SessionThumbnailCache.js.map +0 -1
  296. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +0 -688
  297. package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +0 -1
  298. package/dist/node_modules/react/cjs/react.development.js +0 -1521
  299. package/dist/node_modules/react/cjs/react.development.js.map +0 -1
  300. package/dist/node_modules/react/index.js +0 -13
  301. package/dist/node_modules/react/index.js.map +0 -1
  302. package/dist/node_modules/react/jsx-runtime.js +0 -13
  303. package/dist/node_modules/react/jsx-runtime.js.map +0 -1
  304. package/dist/preview/encoding/types.d.ts +0 -1
  305. package/dist/preview/renderTimegroupPreview.js +0 -686
  306. package/dist/preview/renderTimegroupPreview.js.map +0 -1
  307. package/dist/preview/rendering/renderToImage.d.ts +0 -2
  308. package/dist/preview/rendering/renderToImage.js +0 -95
  309. package/dist/preview/rendering/renderToImage.js.map +0 -1
  310. package/dist/preview/rendering/renderToImageForeignObject.js +0 -163
  311. package/dist/preview/rendering/renderToImageForeignObject.js.map +0 -1
  312. package/dist/preview/rendering/renderToImageNative.d.ts +0 -1
  313. package/dist/preview/rendering/svgSerializer.js +0 -43
  314. package/dist/preview/rendering/svgSerializer.js.map +0 -1
  315. package/dist/preview/rendering/types.d.ts +0 -2
  316. package/dist/preview/thumbnailCacheSettings.js +0 -52
  317. package/dist/preview/thumbnailCacheSettings.js.map +0 -1
  318. package/dist/sandbox/PlaybackControls.d.ts +0 -1
  319. package/dist/sandbox/PlaybackControls.js +0 -10
  320. package/dist/sandbox/PlaybackControls.js.map +0 -1
  321. package/dist/sandbox/ScenarioRunner.d.ts +0 -1
  322. package/dist/sandbox/ScenarioRunner.js +0 -1
  323. package/dist/sandbox/defineSandbox.d.ts +0 -1
  324. package/dist/sandbox/index.d.ts +0 -3
  325. package/dist/sandbox/index.js +0 -2
  326. package/test/EFVideo.framegen.browsertest.ts +0 -80
  327. package/test/thumbnail-performance-test.html +0 -116
@@ -1 +1 @@
1
- {"version":3,"file":"FetchMixin.js","names":["fetchPromise: Promise<Response>"],"sources":["../../src/elements/FetchMixin.ts"],"sourcesContent":["import type { LitElement } from \"lit\";\n\nexport declare class FetchMixinInterface {\n fetch: typeof fetch;\n}\n\ntype Constructor<T = {}> = new (...args: any[]) => T;\nexport function FetchMixin<T extends Constructor<LitElement>>(superClass: T) {\n class FetchElement extends superClass {\n fetch = (url: string, init?: RequestInit): Promise<Response> => {\n try {\n let fetchPromise: Promise<Response>;\n\n // Look for context providers up the DOM tree\n const workbench = this.closest(\"ef-workbench\") as any;\n if (workbench?.fetch) {\n fetchPromise = workbench.fetch(url, init);\n } else {\n const preview = this.closest(\"ef-preview\") as any;\n if (preview?.fetch) {\n fetchPromise = preview.fetch(url, init);\n } else {\n const configuration = this.closest(\"ef-configuration\") as any;\n if (configuration?.fetch) {\n fetchPromise = configuration.fetch(url, init);\n } else {\n // Fallback to window.fetch\n fetchPromise = window.fetch(url, init);\n }\n }\n }\n\n // Wrap the promise to catch rejections and log the URL\n // Return the promise chain so errors are logged but still propagate\n return fetchPromise.catch((error) => {\n // Don't log AbortError - these are intentional request cancellations\n const isAbortError = \n error instanceof Error && (\n error.name === \"AbortError\" ||\n error.message.includes(\"signal is aborted\") ||\n error.message.includes(\"The user aborted a request\")\n );\n \n // Don't log errors if element is disconnected from DOM\n // This happens during scenario transitions when elements are removed mid-fetch\n // The browser throws TypeError: \"Failed to fetch\" when the page navigates\n const isDisconnected = !this.isConnected;\n \n // Also suppress \"Failed to fetch\" TypeError when disconnected\n // These occur when the browser aborts a request due to page navigation,\n // but doesn't throw a proper AbortError\n const isNavigationAbort = isDisconnected && \n error instanceof TypeError && \n error.message === \"Failed to fetch\";\n \n // For AbortErrors, navigation aborts, and disconnected elements,\n // re-throw the original error without enhancement to preserve error type\n if (isAbortError || isNavigationAbort || isDisconnected) {\n throw error;\n }\n \n // Log unexpected errors\n console.error(\n \"FetchMixin fetch error\",\n url,\n error,\n window.location.href,\n );\n \n // Create a new error with the URL in the message, preserving the original error type\n const ErrorConstructor =\n error instanceof Error ? error.constructor : Error;\n const enhancedError = new (ErrorConstructor as typeof Error)(\n `Failed to fetch: ${url}. Original error: ${error instanceof Error ? error.message : String(error)}`,\n );\n // Preserve the original error's properties\n if (error instanceof Error) {\n // Some error types (like DOMException) have read-only name property\n // Use try-catch to handle cases where name cannot be set\n try {\n enhancedError.name = error.name;\n } catch {\n // If name is read-only, use Object.defineProperty as fallback\n try {\n Object.defineProperty(enhancedError, \"name\", {\n value: error.name,\n writable: true,\n enumerable: false,\n configurable: true,\n });\n } catch {\n // If that also fails, just skip setting name\n }\n }\n enhancedError.stack = error.stack;\n // Copy any additional properties from the original error\n Object.assign(enhancedError, error);\n }\n throw enhancedError;\n });\n } catch (error) {\n console.error(\"FetchMixin error (synchronous)\", url, error);\n throw error;\n }\n };\n }\n\n return FetchElement as Constructor<FetchMixinInterface> & T;\n}\n"],"mappings":";AAOA,SAAgB,WAA8C,YAAe;CAC3E,MAAM,qBAAqB,WAAW;;;iBAC3B,KAAa,SAA0C;AAC9D,QAAI;KACF,IAAIA;KAGJ,MAAM,YAAY,KAAK,QAAQ,eAAe;AAC9C,SAAI,WAAW,MACb,gBAAe,UAAU,MAAM,KAAK,KAAK;UACpC;MACL,MAAM,UAAU,KAAK,QAAQ,aAAa;AAC1C,UAAI,SAAS,MACX,gBAAe,QAAQ,MAAM,KAAK,KAAK;WAClC;OACL,MAAM,gBAAgB,KAAK,QAAQ,mBAAmB;AACtD,WAAI,eAAe,MACjB,gBAAe,cAAc,MAAM,KAAK,KAAK;WAG7C,gBAAe,OAAO,MAAM,KAAK,KAAK;;;AAO5C,YAAO,aAAa,OAAO,UAAU;MAEnC,MAAM,eACJ,iBAAiB,UACf,MAAM,SAAS,gBACf,MAAM,QAAQ,SAAS,oBAAoB,IAC3C,MAAM,QAAQ,SAAS,6BAA6B;MAMxD,MAAM,iBAAiB,CAAC,KAAK;MAK7B,MAAM,oBAAoB,kBACxB,iBAAiB,aACjB,MAAM,YAAY;AAIpB,UAAI,gBAAgB,qBAAqB,eACvC,OAAM;AAIR,cAAQ,MACN,0BACA,KACA,OACA,OAAO,SAAS,KACjB;MAKD,MAAM,gBAAgB,KADpB,iBAAiB,QAAQ,MAAM,cAAc,OAE7C,oBAAoB,IAAI,oBAAoB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GACnG;AAED,UAAI,iBAAiB,OAAO;AAG1B,WAAI;AACF,sBAAc,OAAO,MAAM;eACrB;AAEN,YAAI;AACF,gBAAO,eAAe,eAAe,QAAQ;UAC3C,OAAO,MAAM;UACb,UAAU;UACV,YAAY;UACZ,cAAc;UACf,CAAC;gBACI;;AAIV,qBAAc,QAAQ,MAAM;AAE5B,cAAO,OAAO,eAAe,MAAM;;AAErC,YAAM;OACN;aACK,OAAO;AACd,aAAQ,MAAM,kCAAkC,KAAK,MAAM;AAC3D,WAAM;;;;;AAKZ,QAAO"}
1
+ {"version":3,"file":"FetchMixin.js","names":["fetchPromise: Promise<Response>"],"sources":["../../src/elements/FetchMixin.ts"],"sourcesContent":["import type { LitElement } from \"lit\";\n\nexport declare class FetchMixinInterface {\n fetch: typeof fetch;\n}\n\ntype Constructor<T = {}> = new (...args: any[]) => T;\nexport function FetchMixin<T extends Constructor<LitElement>>(superClass: T) {\n class FetchElement extends superClass {\n fetch = (url: string, init?: RequestInit): Promise<Response> => {\n try {\n let fetchPromise: Promise<Response>;\n\n // Look for context providers up the DOM tree\n const workbench = this.closest(\"ef-workbench\") as any;\n if (workbench?.fetch) {\n fetchPromise = workbench.fetch(url, init);\n } else {\n const preview = this.closest(\"ef-preview\") as any;\n if (preview?.fetch) {\n fetchPromise = preview.fetch(url, init);\n } else {\n const configuration = this.closest(\"ef-configuration\") as any;\n if (configuration?.fetch) {\n fetchPromise = configuration.fetch(url, init);\n } else {\n // Fallback to window.fetch\n fetchPromise = window.fetch(url, init);\n }\n }\n }\n\n // Wrap the promise to catch rejections and log the URL\n // Return the promise chain so errors are logged but still propagate\n return fetchPromise.catch((error) => {\n // Don't log AbortError - these are intentional request cancellations\n const isAbortError =\n error instanceof Error &&\n (error.name === \"AbortError\" ||\n error.message.includes(\"signal is aborted\") ||\n error.message.includes(\"The user aborted a request\"));\n\n // Don't log errors if element is disconnected from DOM\n // This happens during scenario transitions when elements are removed mid-fetch\n // The browser throws TypeError: \"Failed to fetch\" when the page navigates\n const isDisconnected = !this.isConnected;\n\n // Also suppress \"Failed to fetch\" TypeError when disconnected\n // These occur when the browser aborts a request due to page navigation,\n // but doesn't throw a proper AbortError\n const isNavigationAbort =\n isDisconnected &&\n error instanceof TypeError &&\n error.message === \"Failed to fetch\";\n\n // For AbortErrors, navigation aborts, and disconnected elements,\n // re-throw the original error without enhancement to preserve error type\n if (isAbortError || isNavigationAbort || isDisconnected) {\n throw error;\n }\n\n // Log unexpected errors\n console.error(\n \"FetchMixin fetch error\",\n url,\n error,\n window.location.href,\n );\n\n // Create a new error with the URL in the message, preserving the original error type\n const ErrorConstructor =\n error instanceof Error ? error.constructor : Error;\n const enhancedError = new (ErrorConstructor as typeof Error)(\n `Failed to fetch: ${url}. Original error: ${error instanceof Error ? error.message : String(error)}`,\n );\n // Preserve the original error's properties\n if (error instanceof Error) {\n // Some error types (like DOMException) have read-only name property\n // Use try-catch to handle cases where name cannot be set\n try {\n enhancedError.name = error.name;\n } catch {\n // If name is read-only, use Object.defineProperty as fallback\n try {\n Object.defineProperty(enhancedError, \"name\", {\n value: error.name,\n writable: true,\n enumerable: false,\n configurable: true,\n });\n } catch {\n // If that also fails, just skip setting name\n }\n }\n enhancedError.stack = error.stack;\n // Copy any additional properties from the original error\n Object.assign(enhancedError, error);\n }\n throw enhancedError;\n });\n } catch (error) {\n console.error(\"FetchMixin error (synchronous)\", url, error);\n throw error;\n }\n };\n }\n\n return FetchElement as Constructor<FetchMixinInterface> & T;\n}\n"],"mappings":";AAOA,SAAgB,WAA8C,YAAe;CAC3E,MAAM,qBAAqB,WAAW;;;iBAC3B,KAAa,SAA0C;AAC9D,QAAI;KACF,IAAIA;KAGJ,MAAM,YAAY,KAAK,QAAQ,eAAe;AAC9C,SAAI,WAAW,MACb,gBAAe,UAAU,MAAM,KAAK,KAAK;UACpC;MACL,MAAM,UAAU,KAAK,QAAQ,aAAa;AAC1C,UAAI,SAAS,MACX,gBAAe,QAAQ,MAAM,KAAK,KAAK;WAClC;OACL,MAAM,gBAAgB,KAAK,QAAQ,mBAAmB;AACtD,WAAI,eAAe,MACjB,gBAAe,cAAc,MAAM,KAAK,KAAK;WAG7C,gBAAe,OAAO,MAAM,KAAK,KAAK;;;AAO5C,YAAO,aAAa,OAAO,UAAU;MAEnC,MAAM,eACJ,iBAAiB,UAChB,MAAM,SAAS,gBACd,MAAM,QAAQ,SAAS,oBAAoB,IAC3C,MAAM,QAAQ,SAAS,6BAA6B;MAKxD,MAAM,iBAAiB,CAAC,KAAK;MAK7B,MAAM,oBACJ,kBACA,iBAAiB,aACjB,MAAM,YAAY;AAIpB,UAAI,gBAAgB,qBAAqB,eACvC,OAAM;AAIR,cAAQ,MACN,0BACA,KACA,OACA,OAAO,SAAS,KACjB;MAKD,MAAM,gBAAgB,KADpB,iBAAiB,QAAQ,MAAM,cAAc,OAE7C,oBAAoB,IAAI,oBAAoB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GACnG;AAED,UAAI,iBAAiB,OAAO;AAG1B,WAAI;AACF,sBAAc,OAAO,MAAM;eACrB;AAEN,YAAI;AACF,gBAAO,eAAe,eAAe,QAAQ;UAC3C,OAAO,MAAM;UACb,UAAU;UACV,YAAY;UACZ,cAAc;UACf,CAAC;gBACI;;AAIV,qBAAc,QAAQ,MAAM;AAE5B,cAAO,OAAO,eAAe,MAAM;;AAErC,YAAM;OACN;aACK,OAAO;AACd,aAAQ,MAAM,kCAAkC,KAAK,MAAM;AAC3D,WAAM;;;;;AAKZ,QAAO"}
@@ -4,7 +4,6 @@ import { LitElement, ReactiveController } from "lit";
4
4
 
5
5
  declare class TargetController implements ReactiveController {
6
6
  private host;
7
- private targetController;
8
7
  private currentTargetString;
9
8
  constructor(host: LitElement & {
10
9
  targetElement: Element | null;
@@ -12,8 +11,6 @@ declare class TargetController implements ReactiveController {
12
11
  });
13
12
  private registryCallback;
14
13
  private updateTarget;
15
- private connectToTarget;
16
- private disconnectFromTarget;
17
14
  private get registry();
18
15
  hostDisconnected(): void;
19
16
  hostConnected(): void;
@@ -1,5 +1,3 @@
1
- import { LitElement } from "lit";
2
-
3
1
  //#region src/elements/TargetController.ts
4
2
  const EF_TARGETABLE = Symbol("EF_TARGETABLE");
5
3
  var TargetRegistry = class {
@@ -73,23 +71,8 @@ const EFTargetable = (superClass) => {
73
71
  Object.defineProperty(TargetableElement.prototype, EF_TARGETABLE, { value: true });
74
72
  return TargetableElement;
75
73
  };
76
- var TargetUpdateController = class {
77
- constructor(host) {
78
- this.host = host;
79
- }
80
- hostConnected() {
81
- this.host.requestUpdate();
82
- }
83
- hostDisconnected() {
84
- this.host.requestUpdate();
85
- }
86
- hostUpdate() {
87
- this.host.requestUpdate();
88
- }
89
- };
90
74
  var TargetController = class {
91
75
  constructor(host) {
92
- this.targetController = null;
93
76
  this.currentTargetString = null;
94
77
  this.registryCallback = (target) => {
95
78
  this.host.targetElement = target ?? null;
@@ -100,34 +83,28 @@ var TargetController = class {
100
83
  if (this.currentTargetString) this.registry.subscribe(this.currentTargetString, this.registryCallback);
101
84
  }
102
85
  updateTarget() {
103
- if (!this.host.target) return;
86
+ if (!this.host.target) {
87
+ if (this.host.targetElement !== null) {
88
+ this.host.targetElement = null;
89
+ this.host.requestUpdate("targetElement");
90
+ }
91
+ return;
92
+ }
104
93
  let newTarget = this.registry.get(this.host.target);
94
+ if (!newTarget) {
95
+ const container = this.host.closest("ef-timegroup, ef-configuration") || this.host.getRootNode();
96
+ if (container && "querySelector" in container) newTarget = container.querySelector(`#${CSS.escape(this.host.target)}`);
97
+ }
105
98
  if (!newTarget) newTarget = document.getElementById(this.host.target);
106
99
  if (this.host.targetElement !== newTarget) {
107
- this.disconnectFromTarget();
108
100
  this.host.targetElement = newTarget ?? null;
109
- this.connectToTarget();
110
101
  this.host.requestUpdate("targetElement");
111
102
  }
112
103
  }
113
- connectToTarget() {
114
- if (this.host.targetElement instanceof LitElement) {
115
- this.targetController = new TargetUpdateController(this.host);
116
- this.host.targetElement.addController(this.targetController);
117
- }
118
- }
119
- disconnectFromTarget() {
120
- if (this.host.targetElement instanceof LitElement && this.targetController) {
121
- this.host.targetElement.removeController(this.targetController);
122
- this.targetController = null;
123
- }
124
- }
125
104
  get registry() {
126
105
  return getRegistry(this.host.getRootNode());
127
106
  }
128
- hostDisconnected() {
129
- this.disconnectFromTarget();
130
- }
107
+ hostDisconnected() {}
131
108
  hostConnected() {
132
109
  this.updateTarget();
133
110
  }
@@ -1 +1 @@
1
- {"version":3,"file":"TargetController.js","names":["#registry","host: LitElement"],"sources":["../../src/elements/TargetController.ts"],"sourcesContent":["import { LitElement, type ReactiveController } from \"lit\";\n\ntype Constructor<T = {}> = new (...args: any[]) => T;\n\n// Symbol to identify elements that can be targeted\nconst EF_TARGETABLE = Symbol(\"EF_TARGETABLE\");\n\nclass TargetRegistry {\n private idMap = new Map<string, LitElement>();\n private callbacks = new Map<\n string,\n Set<(target: LitElement | undefined) => void>\n >();\n\n subscribe(id: string, callback: (target: LitElement | undefined) => void) {\n this.callbacks.set(id, this.callbacks.get(id) ?? new Set());\n this.callbacks.get(id)?.add(callback);\n }\n\n unsubscribe(\n id: string | null,\n callback: (target: LitElement | undefined) => void,\n ) {\n if (id === null) {\n return;\n }\n this.callbacks.get(id)?.delete(callback);\n if (this.callbacks.get(id)?.size === 0) {\n this.callbacks.delete(id);\n }\n }\n\n get(id: string) {\n return this.idMap.get(id);\n }\n\n register(id: string, target: LitElement) {\n this.idMap.set(id, target);\n for (const callback of this.callbacks.get(id) ?? []) {\n callback(target);\n }\n }\n\n unregister(id: string, target: LitElement) {\n if (this.idMap.get(id) !== target) {\n // Avoid unregistering a target that is not the current target\n return;\n }\n for (const callback of this.callbacks.get(id) ?? []) {\n callback(undefined);\n }\n this.idMap.delete(id);\n this.callbacks.delete(id);\n }\n}\n\n// Map of root nodes to their target registries\nconst documentRegistries = new WeakMap<Node, TargetRegistry>();\n\nconst getRegistry = (root: Node) => {\n let registry = documentRegistries.get(root);\n if (!registry) {\n registry = new TargetRegistry();\n documentRegistries.set(root, registry);\n }\n return registry;\n};\n\nexport declare class TargetableMixinInterface {\n id: string;\n}\n\nexport const isEFTargetable = (obj: any): obj is TargetableMixinInterface =>\n obj[EF_TARGETABLE];\n\nexport const EFTargetable = <T extends Constructor<LitElement>>(\n superClass: T,\n) => {\n class TargetableElement extends superClass {\n #registry: TargetRegistry | null = null;\n\n static get observedAttributes(): string[] {\n // Get parent's observed attributes\n const parentAttributes = (superClass as any).observedAttributes || [];\n // Add 'id' if not already present\n return [...new Set([...parentAttributes, \"id\"])];\n }\n\n private updateRegistry(oldValue: string, newValue: string) {\n if (!this.#registry) return;\n if (oldValue === newValue) return;\n\n if (oldValue) {\n this.#registry.unregister(oldValue, this);\n }\n if (newValue) {\n this.#registry.register(newValue, this);\n }\n }\n\n connectedCallback() {\n super.connectedCallback();\n this.#registry = getRegistry(this.getRootNode());\n const initialId = this.getAttribute(\"id\");\n if (initialId) {\n this.updateRegistry(\"\", initialId);\n }\n }\n\n attributeChangedCallback(\n name: string,\n old: string | null,\n value: string | null,\n ) {\n super.attributeChangedCallback(name, old, value);\n if (name === \"id\") {\n this.updateRegistry(old ?? \"\", value ?? \"\");\n }\n }\n\n disconnectedCallback() {\n if (this.#registry) {\n this.updateRegistry(this.id, \"\");\n this.#registry = null;\n }\n super.disconnectedCallback();\n }\n }\n\n Object.defineProperty(TargetableElement.prototype, EF_TARGETABLE, {\n value: true,\n });\n\n return TargetableElement as T;\n};\n\nclass TargetUpdateController implements ReactiveController {\n constructor(private host: LitElement) {}\n\n hostConnected() {\n this.host.requestUpdate();\n }\n\n hostDisconnected() {\n this.host.requestUpdate();\n }\n\n hostUpdate() {\n this.host.requestUpdate();\n }\n}\n\nexport class TargetController implements ReactiveController {\n private host: LitElement & { targetElement: Element | null; target: string };\n private targetController: ReactiveController | null = null;\n private currentTargetString: string | null = null;\n\n constructor(\n host: LitElement & { targetElement: Element | null; target: string },\n ) {\n this.host = host;\n this.host.addController(this);\n this.currentTargetString = this.host.target;\n if (this.currentTargetString) {\n this.registry.subscribe(this.currentTargetString, this.registryCallback);\n }\n }\n\n private registryCallback = (target: LitElement | undefined) => {\n this.host.targetElement = target ?? null;\n };\n\n private updateTarget() {\n // Only look up by ID if target string is non-empty\n // This preserves direct object bindings via .targetElement=${obj}\n if (!this.host.target) {\n return;\n }\n\n // First try the local registry (same root node)\n let newTarget = this.registry.get(this.host.target);\n\n // Fall back to document.getElementById for cross-shadow-root targeting\n if (!newTarget) {\n newTarget = document.getElementById(this.host.target) as LitElement | undefined;\n }\n\n if (this.host.targetElement !== newTarget) {\n this.disconnectFromTarget();\n this.host.targetElement = newTarget ?? (null as Element | null);\n this.connectToTarget();\n this.host.requestUpdate(\"targetElement\");\n }\n }\n\n private connectToTarget() {\n if (this.host.targetElement instanceof LitElement) {\n this.targetController = new TargetUpdateController(this.host);\n this.host.targetElement.addController(this.targetController);\n }\n }\n\n private disconnectFromTarget() {\n if (\n this.host.targetElement instanceof LitElement &&\n this.targetController\n ) {\n this.host.targetElement.removeController(this.targetController);\n this.targetController = null;\n }\n }\n\n private get registry() {\n const root = this.host.getRootNode();\n return getRegistry(root);\n }\n\n hostDisconnected() {\n this.disconnectFromTarget();\n }\n\n hostConnected() {\n this.updateTarget();\n }\n\n hostUpdate() {\n if (this.currentTargetString !== this.host.target) {\n this.registry.unsubscribe(\n this.currentTargetString,\n this.registryCallback,\n );\n this.registry.subscribe(this.host.target, this.registryCallback);\n this.updateTarget();\n this.currentTargetString = this.host.target;\n }\n }\n}\n"],"mappings":";;;AAKA,MAAM,gBAAgB,OAAO,gBAAgB;AAE7C,IAAM,iBAAN,MAAqB;;+BACH,IAAI,KAAyB;mCACzB,IAAI,KAGrB;;CAEH,UAAU,IAAY,UAAoD;AACxE,OAAK,UAAU,IAAI,IAAI,KAAK,UAAU,IAAI,GAAG,oBAAI,IAAI,KAAK,CAAC;AAC3D,OAAK,UAAU,IAAI,GAAG,EAAE,IAAI,SAAS;;CAGvC,YACE,IACA,UACA;AACA,MAAI,OAAO,KACT;AAEF,OAAK,UAAU,IAAI,GAAG,EAAE,OAAO,SAAS;AACxC,MAAI,KAAK,UAAU,IAAI,GAAG,EAAE,SAAS,EACnC,MAAK,UAAU,OAAO,GAAG;;CAI7B,IAAI,IAAY;AACd,SAAO,KAAK,MAAM,IAAI,GAAG;;CAG3B,SAAS,IAAY,QAAoB;AACvC,OAAK,MAAM,IAAI,IAAI,OAAO;AAC1B,OAAK,MAAM,YAAY,KAAK,UAAU,IAAI,GAAG,IAAI,EAAE,CACjD,UAAS,OAAO;;CAIpB,WAAW,IAAY,QAAoB;AACzC,MAAI,KAAK,MAAM,IAAI,GAAG,KAAK,OAEzB;AAEF,OAAK,MAAM,YAAY,KAAK,UAAU,IAAI,GAAG,IAAI,EAAE,CACjD,UAAS,OAAU;AAErB,OAAK,MAAM,OAAO,GAAG;AACrB,OAAK,UAAU,OAAO,GAAG;;;AAK7B,MAAM,qCAAqB,IAAI,SAA+B;AAE9D,MAAM,eAAe,SAAe;CAClC,IAAI,WAAW,mBAAmB,IAAI,KAAK;AAC3C,KAAI,CAAC,UAAU;AACb,aAAW,IAAI,gBAAgB;AAC/B,qBAAmB,IAAI,MAAM,SAAS;;AAExC,QAAO;;AAUT,MAAa,gBACX,eACG;CACH,MAAM,0BAA0B,WAAW;EACzC,YAAmC;EAEnC,WAAW,qBAA+B;GAExC,MAAM,mBAAoB,WAAmB,sBAAsB,EAAE;AAErE,UAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,kBAAkB,KAAK,CAAC,CAAC;;EAGlD,AAAQ,eAAe,UAAkB,UAAkB;AACzD,OAAI,CAAC,MAAKA,SAAW;AACrB,OAAI,aAAa,SAAU;AAE3B,OAAI,SACF,OAAKA,SAAU,WAAW,UAAU,KAAK;AAE3C,OAAI,SACF,OAAKA,SAAU,SAAS,UAAU,KAAK;;EAI3C,oBAAoB;AAClB,SAAM,mBAAmB;AACzB,SAAKA,WAAY,YAAY,KAAK,aAAa,CAAC;GAChD,MAAM,YAAY,KAAK,aAAa,KAAK;AACzC,OAAI,UACF,MAAK,eAAe,IAAI,UAAU;;EAItC,yBACE,MACA,KACA,OACA;AACA,SAAM,yBAAyB,MAAM,KAAK,MAAM;AAChD,OAAI,SAAS,KACX,MAAK,eAAe,OAAO,IAAI,SAAS,GAAG;;EAI/C,uBAAuB;AACrB,OAAI,MAAKA,UAAW;AAClB,SAAK,eAAe,KAAK,IAAI,GAAG;AAChC,UAAKA,WAAY;;AAEnB,SAAM,sBAAsB;;;AAIhC,QAAO,eAAe,kBAAkB,WAAW,eAAe,EAChE,OAAO,MACR,CAAC;AAEF,QAAO;;AAGT,IAAM,yBAAN,MAA2D;CACzD,YAAY,AAAQC,MAAkB;EAAlB;;CAEpB,gBAAgB;AACd,OAAK,KAAK,eAAe;;CAG3B,mBAAmB;AACjB,OAAK,KAAK,eAAe;;CAG3B,aAAa;AACX,OAAK,KAAK,eAAe;;;AAI7B,IAAa,mBAAb,MAA4D;CAK1D,YACE,MACA;0BALoD;6BACT;2BAajB,WAAmC;AAC7D,QAAK,KAAK,gBAAgB,UAAU;;AATpC,OAAK,OAAO;AACZ,OAAK,KAAK,cAAc,KAAK;AAC7B,OAAK,sBAAsB,KAAK,KAAK;AACrC,MAAI,KAAK,oBACP,MAAK,SAAS,UAAU,KAAK,qBAAqB,KAAK,iBAAiB;;CAQ5E,AAAQ,eAAe;AAGrB,MAAI,CAAC,KAAK,KAAK,OACb;EAIF,IAAI,YAAY,KAAK,SAAS,IAAI,KAAK,KAAK,OAAO;AAGnD,MAAI,CAAC,UACH,aAAY,SAAS,eAAe,KAAK,KAAK,OAAO;AAGvD,MAAI,KAAK,KAAK,kBAAkB,WAAW;AACzC,QAAK,sBAAsB;AAC3B,QAAK,KAAK,gBAAgB,aAAc;AACxC,QAAK,iBAAiB;AACtB,QAAK,KAAK,cAAc,gBAAgB;;;CAI5C,AAAQ,kBAAkB;AACxB,MAAI,KAAK,KAAK,yBAAyB,YAAY;AACjD,QAAK,mBAAmB,IAAI,uBAAuB,KAAK,KAAK;AAC7D,QAAK,KAAK,cAAc,cAAc,KAAK,iBAAiB;;;CAIhE,AAAQ,uBAAuB;AAC7B,MACE,KAAK,KAAK,yBAAyB,cACnC,KAAK,kBACL;AACA,QAAK,KAAK,cAAc,iBAAiB,KAAK,iBAAiB;AAC/D,QAAK,mBAAmB;;;CAI5B,IAAY,WAAW;AAErB,SAAO,YADM,KAAK,KAAK,aAAa,CACZ;;CAG1B,mBAAmB;AACjB,OAAK,sBAAsB;;CAG7B,gBAAgB;AACd,OAAK,cAAc;;CAGrB,aAAa;AACX,MAAI,KAAK,wBAAwB,KAAK,KAAK,QAAQ;AACjD,QAAK,SAAS,YACZ,KAAK,qBACL,KAAK,iBACN;AACD,QAAK,SAAS,UAAU,KAAK,KAAK,QAAQ,KAAK,iBAAiB;AAChE,QAAK,cAAc;AACnB,QAAK,sBAAsB,KAAK,KAAK"}
1
+ {"version":3,"file":"TargetController.js","names":["#registry"],"sources":["../../src/elements/TargetController.ts"],"sourcesContent":["import { LitElement, type ReactiveController } from \"lit\";\n\ntype Constructor<T = {}> = new (...args: any[]) => T;\n\n// Symbol to identify elements that can be targeted\nconst EF_TARGETABLE = Symbol(\"EF_TARGETABLE\");\n\nclass TargetRegistry {\n private idMap = new Map<string, LitElement>();\n private callbacks = new Map<\n string,\n Set<(target: LitElement | undefined) => void>\n >();\n\n subscribe(id: string, callback: (target: LitElement | undefined) => void) {\n this.callbacks.set(id, this.callbacks.get(id) ?? new Set());\n this.callbacks.get(id)?.add(callback);\n }\n\n unsubscribe(\n id: string | null,\n callback: (target: LitElement | undefined) => void,\n ) {\n if (id === null) {\n return;\n }\n this.callbacks.get(id)?.delete(callback);\n if (this.callbacks.get(id)?.size === 0) {\n this.callbacks.delete(id);\n }\n }\n\n get(id: string) {\n return this.idMap.get(id);\n }\n\n register(id: string, target: LitElement) {\n this.idMap.set(id, target);\n for (const callback of this.callbacks.get(id) ?? []) {\n callback(target);\n }\n }\n\n unregister(id: string, target: LitElement) {\n if (this.idMap.get(id) !== target) {\n // Avoid unregistering a target that is not the current target\n return;\n }\n for (const callback of this.callbacks.get(id) ?? []) {\n callback(undefined);\n }\n this.idMap.delete(id);\n this.callbacks.delete(id);\n }\n}\n\n// Map of root nodes to their target registries\nconst documentRegistries = new WeakMap<Node, TargetRegistry>();\n\nconst getRegistry = (root: Node) => {\n let registry = documentRegistries.get(root);\n if (!registry) {\n registry = new TargetRegistry();\n documentRegistries.set(root, registry);\n }\n return registry;\n};\n\nexport declare class TargetableMixinInterface {\n id: string;\n}\n\nexport const isEFTargetable = (obj: any): obj is TargetableMixinInterface =>\n obj[EF_TARGETABLE];\n\nexport const EFTargetable = <T extends Constructor<LitElement>>(\n superClass: T,\n) => {\n class TargetableElement extends superClass {\n #registry: TargetRegistry | null = null;\n\n static get observedAttributes(): string[] {\n // Get parent's observed attributes\n const parentAttributes = (superClass as any).observedAttributes || [];\n // Add 'id' if not already present\n return [...new Set([...parentAttributes, \"id\"])];\n }\n\n private updateRegistry(oldValue: string, newValue: string) {\n if (!this.#registry) return;\n if (oldValue === newValue) return;\n\n if (oldValue) {\n this.#registry.unregister(oldValue, this);\n }\n if (newValue) {\n this.#registry.register(newValue, this);\n }\n }\n\n connectedCallback() {\n super.connectedCallback();\n this.#registry = getRegistry(this.getRootNode());\n const initialId = this.getAttribute(\"id\");\n if (initialId) {\n this.updateRegistry(\"\", initialId);\n }\n }\n\n attributeChangedCallback(\n name: string,\n old: string | null,\n value: string | null,\n ) {\n super.attributeChangedCallback(name, old, value);\n if (name === \"id\") {\n this.updateRegistry(old ?? \"\", value ?? \"\");\n }\n }\n\n disconnectedCallback() {\n if (this.#registry) {\n this.updateRegistry(this.id, \"\");\n this.#registry = null;\n }\n super.disconnectedCallback();\n }\n }\n\n Object.defineProperty(TargetableElement.prototype, EF_TARGETABLE, {\n value: true,\n });\n\n return TargetableElement as T;\n};\n\nexport class TargetController implements ReactiveController {\n private host: LitElement & { targetElement: Element | null; target: string };\n private currentTargetString: string | null = null;\n\n constructor(\n host: LitElement & { targetElement: Element | null; target: string },\n ) {\n this.host = host;\n this.host.addController(this);\n this.currentTargetString = this.host.target;\n if (this.currentTargetString) {\n this.registry.subscribe(this.currentTargetString, this.registryCallback);\n }\n }\n\n private registryCallback = (target: LitElement | undefined) => {\n this.host.targetElement = target ?? null;\n };\n\n private updateTarget() {\n if (!this.host.target) {\n if (this.host.targetElement !== null) {\n this.host.targetElement = null;\n this.host.requestUpdate(\"targetElement\");\n }\n return;\n }\n\n let newTarget = this.registry.get(this.host.target);\n\n if (!newTarget) {\n const container =\n (this.host as Element).closest(\"ef-timegroup, ef-configuration\") ||\n (this.host as Element).getRootNode();\n\n if (container && \"querySelector\" in container) {\n newTarget = (container as Element).querySelector(\n `#${CSS.escape(this.host.target)}`,\n ) as LitElement | undefined;\n }\n }\n\n if (!newTarget) {\n newTarget = document.getElementById(this.host.target) as\n | LitElement\n | undefined;\n }\n\n if (this.host.targetElement !== newTarget) {\n this.host.targetElement = newTarget ?? (null as Element | null);\n this.host.requestUpdate(\"targetElement\");\n }\n }\n\n private get registry() {\n const root = this.host.getRootNode();\n return getRegistry(root);\n }\n\n hostDisconnected() {}\n\n hostConnected() {\n this.updateTarget();\n }\n\n hostUpdate() {\n if (this.currentTargetString !== this.host.target) {\n this.registry.unsubscribe(\n this.currentTargetString,\n this.registryCallback,\n );\n this.registry.subscribe(this.host.target, this.registryCallback);\n this.updateTarget();\n this.currentTargetString = this.host.target;\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TimegroupController.js","names":["host: EFTimegroup","child: { currentTimeMs: number; startTimeMs?: number } & LitElement"],"sources":["../../src/elements/TimegroupController.ts"],"sourcesContent":["import type { LitElement, ReactiveController } from \"lit\";\nimport type { EFTimegroup } from \"./EFTimegroup.js\";\n\nexport class TimegroupController implements ReactiveController {\n constructor(\n private host: EFTimegroup,\n private child: { currentTimeMs: number; startTimeMs?: number } & LitElement,\n ) {\n this.host.addController(this);\n }\n\n remove() {\n this.host.removeController(this);\n }\n\n hostDisconnected(): void {\n this.host.removeController(this);\n }\n\n hostUpdated(): void {\n // Defer update to avoid Lit warning about scheduling updates after update completed\n // This batches updates and prevents cascading update cycles\n Promise.resolve().then(() => {\n // Skip setting currentTimeMs for timegroup children - they compute ownCurrentTimeMs\n // from the root timegroup via EFTemporal. Setting it directly causes an infinite loop\n // because the @property decorator on currentTime triggers reactive updates.\n if ('mode' in this.child && 'isRootTimegroup' in this.child) {\n // Child is a timegroup - just request update, don't set currentTimeMs\n this.child.requestUpdate();\n return;\n }\n\n this.child.requestUpdate();\n const newChildTimeMs =\n this.host.currentTimeMs - (this.child.startTimeMs ?? 0);\n this.child.currentTimeMs = newChildTimeMs;\n });\n }\n}\n"],"mappings":";AAGA,IAAa,sBAAb,MAA+D;CAC7D,YACE,AAAQA,MACR,AAAQC,OACR;EAFQ;EACA;AAER,OAAK,KAAK,cAAc,KAAK;;CAG/B,SAAS;AACP,OAAK,KAAK,iBAAiB,KAAK;;CAGlC,mBAAyB;AACvB,OAAK,KAAK,iBAAiB,KAAK;;CAGlC,cAAoB;AAGlB,UAAQ,SAAS,CAAC,WAAW;AAI3B,OAAI,UAAU,KAAK,SAAS,qBAAqB,KAAK,OAAO;AAE3D,SAAK,MAAM,eAAe;AAC1B;;AAGF,QAAK,MAAM,eAAe;GAC1B,MAAM,iBACJ,KAAK,KAAK,iBAAiB,KAAK,MAAM,eAAe;AACvD,QAAK,MAAM,gBAAgB;IAC3B"}
1
+ {"version":3,"file":"TimegroupController.js","names":["host: EFTimegroup","child: { currentTimeMs: number; startTimeMs?: number } & LitElement"],"sources":["../../src/elements/TimegroupController.ts"],"sourcesContent":["import type { LitElement, ReactiveController } from \"lit\";\nimport type { EFTimegroup } from \"./EFTimegroup.js\";\n\nexport class TimegroupController implements ReactiveController {\n constructor(\n private host: EFTimegroup,\n private child: { currentTimeMs: number; startTimeMs?: number } & LitElement,\n ) {\n this.host.addController(this);\n }\n\n remove() {\n this.host.removeController(this);\n }\n\n hostDisconnected(): void {\n this.host.removeController(this);\n }\n\n hostUpdated(): void {\n // Defer update to avoid Lit warning about scheduling updates after update completed\n // This batches updates and prevents cascading update cycles\n Promise.resolve().then(() => {\n // Skip setting currentTimeMs for timegroup children - they compute ownCurrentTimeMs\n // from the root timegroup via EFTemporal. Setting it directly causes an infinite loop\n // because the @property decorator on currentTime triggers reactive updates.\n if (\"mode\" in this.child && \"isRootTimegroup\" in this.child) {\n // Child is a timegroup - just request update, don't set currentTimeMs\n this.child.requestUpdate();\n return;\n }\n\n this.child.requestUpdate();\n const newChildTimeMs =\n this.host.currentTimeMs - (this.child.startTimeMs ?? 0);\n this.child.currentTimeMs = newChildTimeMs;\n });\n }\n}\n"],"mappings":";AAGA,IAAa,sBAAb,MAA+D;CAC7D,YACE,AAAQA,MACR,AAAQC,OACR;EAFQ;EACA;AAER,OAAK,KAAK,cAAc,KAAK;;CAG/B,SAAS;AACP,OAAK,KAAK,iBAAiB,KAAK;;CAGlC,mBAAyB;AACvB,OAAK,KAAK,iBAAiB,KAAK;;CAGlC,cAAoB;AAGlB,UAAQ,SAAS,CAAC,WAAW;AAI3B,OAAI,UAAU,KAAK,SAAS,qBAAqB,KAAK,OAAO;AAE3D,SAAK,MAAM,eAAe;AAC1B;;AAGF,QAAK,MAAM,eAAe;GAC1B,MAAM,iBACJ,KAAK,KAAK,iBAAiB,KAAK,MAAM,eAAe;AACvD,QAAK,MAAM,gBAAgB;IAC3B"}
@@ -0,0 +1,14 @@
1
+ import { EFTimegroup } from "./EFTimegroup.js";
2
+
3
+ //#region src/elements/cloneFactoryRegistry.d.ts
4
+ type CloneFactoryResult = {
5
+ timegroup: EFTimegroup;
6
+ cleanup: () => void;
7
+ };
8
+ type CloneFactory = (container: HTMLElement) => CloneFactoryResult;
9
+ declare function registerCloneFactory(element: EFTimegroup, factory: CloneFactory): void;
10
+ declare function unregisterCloneFactory(element: EFTimegroup): void;
11
+ declare function getCloneFactory(element: EFTimegroup): CloneFactory | undefined;
12
+ //#endregion
13
+ export { CloneFactory, CloneFactoryResult, getCloneFactory, registerCloneFactory, unregisterCloneFactory };
14
+ //# sourceMappingURL=cloneFactoryRegistry.d.ts.map
@@ -0,0 +1,15 @@
1
+ //#region src/elements/cloneFactoryRegistry.ts
2
+ const registry = /* @__PURE__ */ new WeakMap();
3
+ function registerCloneFactory(element, factory) {
4
+ registry.set(element, factory);
5
+ }
6
+ function unregisterCloneFactory(element) {
7
+ registry.delete(element);
8
+ }
9
+ function getCloneFactory(element) {
10
+ return registry.get(element);
11
+ }
12
+
13
+ //#endregion
14
+ export { getCloneFactory, registerCloneFactory, unregisterCloneFactory };
15
+ //# sourceMappingURL=cloneFactoryRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloneFactoryRegistry.js","names":[],"sources":["../../src/elements/cloneFactoryRegistry.ts"],"sourcesContent":["import type { EFTimegroup } from \"./EFTimegroup.js\";\n\nexport type CloneFactoryResult = {\n timegroup: EFTimegroup;\n cleanup: () => void;\n};\n\nexport type CloneFactory = (container: HTMLElement) => CloneFactoryResult;\n\nconst registry = new WeakMap<EFTimegroup, CloneFactory>();\n\nexport function registerCloneFactory(\n element: EFTimegroup,\n factory: CloneFactory,\n): void {\n registry.set(element, factory);\n}\n\nexport function unregisterCloneFactory(element: EFTimegroup): void {\n registry.delete(element);\n}\n\nexport function getCloneFactory(\n element: EFTimegroup,\n): CloneFactory | undefined {\n return registry.get(element);\n}\n"],"mappings":";AASA,MAAM,2BAAW,IAAI,SAAoC;AAEzD,SAAgB,qBACd,SACA,SACM;AACN,UAAS,IAAI,SAAS,QAAQ;;AAGhC,SAAgB,uBAAuB,SAA4B;AACjE,UAAS,OAAO,QAAQ;;AAG1B,SAAgB,gBACd,SAC0B;AAC1B,QAAO,SAAS,IAAI,QAAQ"}
@@ -1,3 +1,5 @@
1
+ import { logger } from "../preview/logger.js";
2
+
1
3
  //#region src/elements/renderTemporalAudio.ts
2
4
  async function renderTemporalAudio(host, fromMs, toMs, signal) {
3
5
  const aacFrames = 48e3 * ((toMs - fromMs) / 1e3) / 1024;
@@ -10,14 +12,14 @@ async function renderTemporalAudio(host, fromMs, toMs, signal) {
10
12
  signal?.throwIfAborted();
11
13
  }
12
14
  const mediaElements = host.getMediaElements();
13
- console.log(`[renderTemporalAudio] Found ${mediaElements.length} media elements, time range: ${fromMs}-${toMs}ms`);
15
+ logger.debug(`[renderTemporalAudio] Found ${mediaElements.length} media elements, time range: ${fromMs}-${toMs}ms`);
14
16
  await Promise.all(mediaElements.map(async (mediaElement) => {
15
- console.log(`[renderTemporalAudio] Checking ${mediaElement.tagName} at ${mediaElement.startTimeMs}-${mediaElement.endTimeMs}ms, mute=${mediaElement.mute}`);
17
+ logger.debug(`[renderTemporalAudio] Checking ${mediaElement.tagName} at ${mediaElement.startTimeMs}-${mediaElement.endTimeMs}ms, mute=${mediaElement.mute}`);
16
18
  if (mediaElement.mute) return;
17
19
  const mediaStartsBeforeEnd = mediaElement.startTimeMs <= toMs;
18
20
  const mediaEndsAfterStart = mediaElement.endTimeMs >= fromMs;
19
21
  if (!(mediaStartsBeforeEnd && mediaEndsAfterStart)) {
20
- console.log(`[renderTemporalAudio] ${mediaElement.tagName} does not overlap`);
22
+ logger.debug(`[renderTemporalAudio] ${mediaElement.tagName} does not overlap`);
21
23
  return;
22
24
  }
23
25
  const mediaLocalFromMs = Math.max(0, fromMs - mediaElement.startTimeMs);
@@ -27,13 +29,13 @@ async function renderTemporalAudio(host, fromMs, toMs, signal) {
27
29
  const mediaSourceFromMs = mediaLocalFromMs + sourceInMs;
28
30
  const mediaSourceToMs = mediaLocalToMs + sourceInMs;
29
31
  signal?.throwIfAborted();
30
- console.log(`[renderTemporalAudio] Fetching audio for ${mediaElement.tagName} from ${mediaSourceFromMs}-${mediaSourceToMs}ms`);
32
+ logger.debug(`[renderTemporalAudio] Fetching audio for ${mediaElement.tagName} from ${mediaSourceFromMs}-${mediaSourceToMs}ms`);
31
33
  const audio = await mediaElement.fetchAudioSpanningTime(mediaSourceFromMs, mediaSourceToMs, signal);
32
34
  if (!audio) {
33
- console.log(`[renderTemporalAudio] No audio returned for ${mediaElement.tagName}`);
35
+ logger.debug(`[renderTemporalAudio] No audio returned for ${mediaElement.tagName}`);
34
36
  return;
35
37
  }
36
- console.log(`[renderTemporalAudio] Got audio blob size: ${audio.blob.size}, range: ${audio.startMs}-${audio.endMs}ms`);
38
+ logger.debug(`[renderTemporalAudio] Got audio blob size: ${audio.blob.size}, range: ${audio.startMs}-${audio.endMs}ms`);
37
39
  const bufferSource = audioContext.createBufferSource();
38
40
  let decodedBuffer;
39
41
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"renderTemporalAudio.js","names":[],"sources":["../../src/elements/renderTemporalAudio.ts"],"sourcesContent":["import type { EFMedia } from \"./EFMedia.js\";\n\ninterface TemporalAudioHost {\n startTimeMs: number;\n endTimeMs: number;\n durationMs: number;\n getMediaElements(): EFMedia[];\n waitForMediaDurations?(): Promise<void>;\n}\n\nexport async function renderTemporalAudio(\n host: TemporalAudioHost,\n fromMs: number,\n toMs: number,\n signal?: AbortSignal,\n): Promise<AudioBuffer> {\n const durationMs = toMs - fromMs;\n const duration = durationMs / 1000;\n const exactSamples = 48000 * duration;\n const aacFrames = exactSamples / 1024;\n const alignedFrames = Math.round(aacFrames);\n const contextSize = alignedFrames * 1024;\n\n if (contextSize <= 0) {\n throw new Error(\n `Duration must be greater than 0 when rendering audio. ${contextSize}ms`,\n );\n }\n\n // Check abort before starting\n signal?.throwIfAborted();\n\n const audioContext = new OfflineAudioContext(2, contextSize, 48000);\n\n if (host.waitForMediaDurations) {\n await host.waitForMediaDurations();\n // Check abort after potentially slow operation\n signal?.throwIfAborted();\n }\n\n const mediaElements = host.getMediaElements();\n console.log(`[renderTemporalAudio] Found ${mediaElements.length} media elements, time range: ${fromMs}-${toMs}ms`);\n \n await Promise.all(\n mediaElements.map(async (mediaElement) => {\n console.log(`[renderTemporalAudio] Checking ${mediaElement.tagName} at ${mediaElement.startTimeMs}-${mediaElement.endTimeMs}ms, mute=${mediaElement.mute}`);\n \n if (mediaElement.mute) {\n return;\n }\n\n const mediaStartsBeforeEnd = mediaElement.startTimeMs <= toMs;\n const mediaEndsAfterStart = mediaElement.endTimeMs >= fromMs;\n const mediaOverlaps = mediaStartsBeforeEnd && mediaEndsAfterStart;\n if (!mediaOverlaps) {\n console.log(`[renderTemporalAudio] ${mediaElement.tagName} does not overlap`);\n return;\n }\n\n const mediaLocalFromMs = Math.max(0, fromMs - mediaElement.startTimeMs);\n const mediaLocalToMs = Math.min(\n mediaElement.endTimeMs - mediaElement.startTimeMs,\n toMs - mediaElement.startTimeMs,\n );\n\n if (mediaLocalFromMs >= mediaLocalToMs) {\n return;\n }\n\n const sourceInMs =\n mediaElement.sourceInMs || mediaElement.trimStartMs || 0;\n const mediaSourceFromMs = mediaLocalFromMs + sourceInMs;\n const mediaSourceToMs = mediaLocalToMs + sourceInMs;\n\n // Check abort before processing each media element\n signal?.throwIfAborted();\n \n console.log(`[renderTemporalAudio] Fetching audio for ${mediaElement.tagName} from ${mediaSourceFromMs}-${mediaSourceToMs}ms`);\n const audio = await mediaElement.fetchAudioSpanningTime(\n mediaSourceFromMs,\n mediaSourceToMs,\n signal,\n );\n if (!audio) {\n console.log(`[renderTemporalAudio] No audio returned for ${mediaElement.tagName}`);\n return;\n }\n console.log(`[renderTemporalAudio] Got audio blob size: ${audio.blob.size}, range: ${audio.startMs}-${audio.endMs}ms`);\n\n const bufferSource = audioContext.createBufferSource();\n \n // Decode audio data with error handling for invalid/incomplete audio\n let decodedBuffer;\n try {\n const arrayBuffer = await audio.blob.arrayBuffer();\n // Skip if buffer is too small to be valid audio\n if (arrayBuffer.byteLength < 100) {\n return;\n }\n decodedBuffer = await audioContext.decodeAudioData(arrayBuffer);\n } catch (decodeError) {\n // Unable to decode audio data - skip this segment silently\n // This can happen with corrupted/incomplete audio segments\n if (decodeError instanceof Error && \n decodeError.message.includes(\"Unable to decode audio data\")) {\n return;\n }\n throw decodeError;\n }\n \n bufferSource.buffer = decodedBuffer;\n bufferSource.connect(audioContext.destination);\n\n const ctxStartMs = Math.max(0, mediaElement.startTimeMs - fromMs);\n\n const requestedSourceFromMs = mediaSourceFromMs;\n const actualSourceStartMs = audio.startMs;\n const offsetInBufferMs = requestedSourceFromMs - actualSourceStartMs;\n\n const safeOffsetMs = Math.max(0, offsetInBufferMs);\n\n const requestedDurationMs = mediaSourceToMs - mediaSourceFromMs;\n const availableAudioMs = audio.endMs - audio.startMs;\n const actualDurationMs = Math.min(\n requestedDurationMs,\n availableAudioMs - safeOffsetMs,\n );\n\n if (actualDurationMs <= 0) {\n return;\n }\n\n bufferSource.start(\n ctxStartMs / 1000,\n safeOffsetMs / 1000,\n actualDurationMs / 1000,\n );\n }),\n );\n\n return audioContext.startRendering();\n}\n"],"mappings":";AAUA,eAAsB,oBACpB,MACA,QACA,MACA,QACsB;CAItB,MAAM,YADe,SAFF,OAAO,UACI,OAEG;CAEjC,MAAM,cADgB,KAAK,MAAM,UAAU,GACP;AAEpC,KAAI,eAAe,EACjB,OAAM,IAAI,MACR,yDAAyD,YAAY,IACtE;AAIH,SAAQ,gBAAgB;CAExB,MAAM,eAAe,IAAI,oBAAoB,GAAG,aAAa,KAAM;AAEnE,KAAI,KAAK,uBAAuB;AAC9B,QAAM,KAAK,uBAAuB;AAElC,UAAQ,gBAAgB;;CAG1B,MAAM,gBAAgB,KAAK,kBAAkB;AAC7C,SAAQ,IAAI,+BAA+B,cAAc,OAAO,+BAA+B,OAAO,GAAG,KAAK,IAAI;AAElH,OAAM,QAAQ,IACZ,cAAc,IAAI,OAAO,iBAAiB;AACxC,UAAQ,IAAI,kCAAkC,aAAa,QAAQ,MAAM,aAAa,YAAY,GAAG,aAAa,UAAU,WAAW,aAAa,OAAO;AAE3J,MAAI,aAAa,KACf;EAGF,MAAM,uBAAuB,aAAa,eAAe;EACzD,MAAM,sBAAsB,aAAa,aAAa;AAEtD,MAAI,EADkB,wBAAwB,sBAC1B;AAClB,WAAQ,IAAI,yBAAyB,aAAa,QAAQ,mBAAmB;AAC7E;;EAGF,MAAM,mBAAmB,KAAK,IAAI,GAAG,SAAS,aAAa,YAAY;EACvE,MAAM,iBAAiB,KAAK,IAC1B,aAAa,YAAY,aAAa,aACtC,OAAO,aAAa,YACrB;AAED,MAAI,oBAAoB,eACtB;EAGF,MAAM,aACJ,aAAa,cAAc,aAAa,eAAe;EACzD,MAAM,oBAAoB,mBAAmB;EAC7C,MAAM,kBAAkB,iBAAiB;AAGzC,UAAQ,gBAAgB;AAExB,UAAQ,IAAI,4CAA4C,aAAa,QAAQ,QAAQ,kBAAkB,GAAG,gBAAgB,IAAI;EAC9H,MAAM,QAAQ,MAAM,aAAa,uBAC/B,mBACA,iBACA,OACD;AACD,MAAI,CAAC,OAAO;AACV,WAAQ,IAAI,+CAA+C,aAAa,UAAU;AAClF;;AAEF,UAAQ,IAAI,8CAA8C,MAAM,KAAK,KAAK,WAAW,MAAM,QAAQ,GAAG,MAAM,MAAM,IAAI;EAEtH,MAAM,eAAe,aAAa,oBAAoB;EAGtD,IAAI;AACJ,MAAI;GACF,MAAM,cAAc,MAAM,MAAM,KAAK,aAAa;AAElD,OAAI,YAAY,aAAa,IAC3B;AAEF,mBAAgB,MAAM,aAAa,gBAAgB,YAAY;WACxD,aAAa;AAGpB,OAAI,uBAAuB,SACvB,YAAY,QAAQ,SAAS,8BAA8B,CAC7D;AAEF,SAAM;;AAGR,eAAa,SAAS;AACtB,eAAa,QAAQ,aAAa,YAAY;EAE9C,MAAM,aAAa,KAAK,IAAI,GAAG,aAAa,cAAc,OAAO;EAIjE,MAAM,mBAFwB,oBACF,MAAM;EAGlC,MAAM,eAAe,KAAK,IAAI,GAAG,iBAAiB;EAElD,MAAM,sBAAsB,kBAAkB;EAC9C,MAAM,mBAAmB,MAAM,QAAQ,MAAM;EAC7C,MAAM,mBAAmB,KAAK,IAC5B,qBACA,mBAAmB,aACpB;AAED,MAAI,oBAAoB,EACtB;AAGF,eAAa,MACX,aAAa,KACb,eAAe,KACf,mBAAmB,IACpB;GACD,CACH;AAED,QAAO,aAAa,gBAAgB"}
1
+ {"version":3,"file":"renderTemporalAudio.js","names":[],"sources":["../../src/elements/renderTemporalAudio.ts"],"sourcesContent":["import type { EFMedia } from \"./EFMedia.js\";\nimport { logger } from \"../preview/logger.js\";\n\ninterface TemporalAudioHost {\n startTimeMs: number;\n endTimeMs: number;\n durationMs: number;\n getMediaElements(): EFMedia[];\n waitForMediaDurations?(): Promise<void>;\n}\n\nexport async function renderTemporalAudio(\n host: TemporalAudioHost,\n fromMs: number,\n toMs: number,\n signal?: AbortSignal,\n): Promise<AudioBuffer> {\n const durationMs = toMs - fromMs;\n const duration = durationMs / 1000;\n const exactSamples = 48000 * duration;\n const aacFrames = exactSamples / 1024;\n const alignedFrames = Math.round(aacFrames);\n const contextSize = alignedFrames * 1024;\n\n if (contextSize <= 0) {\n throw new Error(\n `Duration must be greater than 0 when rendering audio. ${contextSize}ms`,\n );\n }\n\n // Check abort before starting\n signal?.throwIfAborted();\n\n const audioContext = new OfflineAudioContext(2, contextSize, 48000);\n\n if (host.waitForMediaDurations) {\n await host.waitForMediaDurations();\n // Check abort after potentially slow operation\n signal?.throwIfAborted();\n }\n\n const mediaElements = host.getMediaElements();\n logger.debug(\n `[renderTemporalAudio] Found ${mediaElements.length} media elements, time range: ${fromMs}-${toMs}ms`,\n );\n\n await Promise.all(\n mediaElements.map(async (mediaElement) => {\n logger.debug(\n `[renderTemporalAudio] Checking ${mediaElement.tagName} at ${mediaElement.startTimeMs}-${mediaElement.endTimeMs}ms, mute=${mediaElement.mute}`,\n );\n\n if (mediaElement.mute) {\n return;\n }\n\n const mediaStartsBeforeEnd = mediaElement.startTimeMs <= toMs;\n const mediaEndsAfterStart = mediaElement.endTimeMs >= fromMs;\n const mediaOverlaps = mediaStartsBeforeEnd && mediaEndsAfterStart;\n if (!mediaOverlaps) {\n logger.debug(\n `[renderTemporalAudio] ${mediaElement.tagName} does not overlap`,\n );\n return;\n }\n\n const mediaLocalFromMs = Math.max(0, fromMs - mediaElement.startTimeMs);\n const mediaLocalToMs = Math.min(\n mediaElement.endTimeMs - mediaElement.startTimeMs,\n toMs - mediaElement.startTimeMs,\n );\n\n if (mediaLocalFromMs >= mediaLocalToMs) {\n return;\n }\n\n const sourceInMs =\n mediaElement.sourceInMs || mediaElement.trimStartMs || 0;\n const mediaSourceFromMs = mediaLocalFromMs + sourceInMs;\n const mediaSourceToMs = mediaLocalToMs + sourceInMs;\n\n // Check abort before processing each media element\n signal?.throwIfAborted();\n\n logger.debug(\n `[renderTemporalAudio] Fetching audio for ${mediaElement.tagName} from ${mediaSourceFromMs}-${mediaSourceToMs}ms`,\n );\n const audio = await mediaElement.fetchAudioSpanningTime(\n mediaSourceFromMs,\n mediaSourceToMs,\n signal,\n );\n if (!audio) {\n logger.debug(\n `[renderTemporalAudio] No audio returned for ${mediaElement.tagName}`,\n );\n return;\n }\n logger.debug(\n `[renderTemporalAudio] Got audio blob size: ${audio.blob.size}, range: ${audio.startMs}-${audio.endMs}ms`,\n );\n\n const bufferSource = audioContext.createBufferSource();\n\n // Decode audio data with error handling for invalid/incomplete audio\n let decodedBuffer;\n try {\n const arrayBuffer = await audio.blob.arrayBuffer();\n // Skip if buffer is too small to be valid audio\n if (arrayBuffer.byteLength < 100) {\n return;\n }\n decodedBuffer = await audioContext.decodeAudioData(arrayBuffer);\n } catch (decodeError) {\n // Unable to decode audio data - skip this segment silently\n // This can happen with corrupted/incomplete audio segments\n if (\n decodeError instanceof Error &&\n decodeError.message.includes(\"Unable to decode audio data\")\n ) {\n return;\n }\n throw decodeError;\n }\n\n bufferSource.buffer = decodedBuffer;\n bufferSource.connect(audioContext.destination);\n\n const ctxStartMs = Math.max(0, mediaElement.startTimeMs - fromMs);\n\n const requestedSourceFromMs = mediaSourceFromMs;\n const actualSourceStartMs = audio.startMs;\n const offsetInBufferMs = requestedSourceFromMs - actualSourceStartMs;\n\n const safeOffsetMs = Math.max(0, offsetInBufferMs);\n\n const requestedDurationMs = mediaSourceToMs - mediaSourceFromMs;\n const availableAudioMs = audio.endMs - audio.startMs;\n const actualDurationMs = Math.min(\n requestedDurationMs,\n availableAudioMs - safeOffsetMs,\n );\n\n if (actualDurationMs <= 0) {\n return;\n }\n\n bufferSource.start(\n ctxStartMs / 1000,\n safeOffsetMs / 1000,\n actualDurationMs / 1000,\n );\n }),\n );\n\n return audioContext.startRendering();\n}\n"],"mappings":";;;AAWA,eAAsB,oBACpB,MACA,QACA,MACA,QACsB;CAItB,MAAM,YADe,SAFF,OAAO,UACI,OAEG;CAEjC,MAAM,cADgB,KAAK,MAAM,UAAU,GACP;AAEpC,KAAI,eAAe,EACjB,OAAM,IAAI,MACR,yDAAyD,YAAY,IACtE;AAIH,SAAQ,gBAAgB;CAExB,MAAM,eAAe,IAAI,oBAAoB,GAAG,aAAa,KAAM;AAEnE,KAAI,KAAK,uBAAuB;AAC9B,QAAM,KAAK,uBAAuB;AAElC,UAAQ,gBAAgB;;CAG1B,MAAM,gBAAgB,KAAK,kBAAkB;AAC7C,QAAO,MACL,+BAA+B,cAAc,OAAO,+BAA+B,OAAO,GAAG,KAAK,IACnG;AAED,OAAM,QAAQ,IACZ,cAAc,IAAI,OAAO,iBAAiB;AACxC,SAAO,MACL,kCAAkC,aAAa,QAAQ,MAAM,aAAa,YAAY,GAAG,aAAa,UAAU,WAAW,aAAa,OACzI;AAED,MAAI,aAAa,KACf;EAGF,MAAM,uBAAuB,aAAa,eAAe;EACzD,MAAM,sBAAsB,aAAa,aAAa;AAEtD,MAAI,EADkB,wBAAwB,sBAC1B;AAClB,UAAO,MACL,yBAAyB,aAAa,QAAQ,mBAC/C;AACD;;EAGF,MAAM,mBAAmB,KAAK,IAAI,GAAG,SAAS,aAAa,YAAY;EACvE,MAAM,iBAAiB,KAAK,IAC1B,aAAa,YAAY,aAAa,aACtC,OAAO,aAAa,YACrB;AAED,MAAI,oBAAoB,eACtB;EAGF,MAAM,aACJ,aAAa,cAAc,aAAa,eAAe;EACzD,MAAM,oBAAoB,mBAAmB;EAC7C,MAAM,kBAAkB,iBAAiB;AAGzC,UAAQ,gBAAgB;AAExB,SAAO,MACL,4CAA4C,aAAa,QAAQ,QAAQ,kBAAkB,GAAG,gBAAgB,IAC/G;EACD,MAAM,QAAQ,MAAM,aAAa,uBAC/B,mBACA,iBACA,OACD;AACD,MAAI,CAAC,OAAO;AACV,UAAO,MACL,+CAA+C,aAAa,UAC7D;AACD;;AAEF,SAAO,MACL,8CAA8C,MAAM,KAAK,KAAK,WAAW,MAAM,QAAQ,GAAG,MAAM,MAAM,IACvG;EAED,MAAM,eAAe,aAAa,oBAAoB;EAGtD,IAAI;AACJ,MAAI;GACF,MAAM,cAAc,MAAM,MAAM,KAAK,aAAa;AAElD,OAAI,YAAY,aAAa,IAC3B;AAEF,mBAAgB,MAAM,aAAa,gBAAgB,YAAY;WACxD,aAAa;AAGpB,OACE,uBAAuB,SACvB,YAAY,QAAQ,SAAS,8BAA8B,CAE3D;AAEF,SAAM;;AAGR,eAAa,SAAS;AACtB,eAAa,QAAQ,aAAa,YAAY;EAE9C,MAAM,aAAa,KAAK,IAAI,GAAG,aAAa,cAAc,OAAO;EAIjE,MAAM,mBAFwB,oBACF,MAAM;EAGlC,MAAM,eAAe,KAAK,IAAI,GAAG,iBAAiB;EAElD,MAAM,sBAAsB,kBAAkB;EAC9C,MAAM,mBAAmB,MAAM,QAAQ,MAAM;EAC7C,MAAM,mBAAmB,KAAK,IAC5B,qBACA,mBAAmB,aACpB;AAED,MAAI,oBAAoB,EACtB;AAGF,eAAa,MACX,aAAa,KACb,eAAe,KACf,mBAAmB,IACpB;GACD,CACH;AAED,QAAO,aAAa,gBAAgB"}
@@ -0,0 +1,62 @@
1
+ //#region src/elements/setupTemporalHierarchy.ts
2
+ /**
3
+ * Manually wire up the temporal element hierarchy (parent/root timegroup references).
4
+ *
5
+ * ## Why this is needed
6
+ *
7
+ * Lit Context (`@provide`/`@consume`) relies on `context-request` DOM events. When a consumer
8
+ * connects, it dispatches a `context-request` event that bubbles up to find a provider.
9
+ *
10
+ * **The problem:** When HTML is loaded via `loadURL()` (our rendering path), custom elements
11
+ * connect in depth-first order - children BEFORE parents. This means:
12
+ * 1. `ef-video` connects → dispatches `context-request` event
13
+ * 2. `ef-timegroup` hasn't connected yet → no provider listening → event is lost
14
+ * 3. `ef-timegroup` connects → creates provider → but too late
15
+ *
16
+ * DOM events are point-in-time; once missed, they're gone. There's no retroactive discovery.
17
+ *
18
+ * Without proper parent/root timegroup references:
19
+ * - Child temporal elements can't compute their `ownCurrentTimeMs` (stays at 0)
20
+ * - Video/audio elements can't seek to correct timestamps
21
+ * - Nested timegroups can't calculate their start times correctly
22
+ *
23
+ * ## When to use
24
+ *
25
+ * Call this function after:
26
+ * 1. All elements are connected to DOM (`connectedCallback` has fired)
27
+ * 2. All custom elements have upgraded (`updateComplete` has resolved)
28
+ * 3. Before any time-based operations (seeking, rendering)
29
+ *
30
+ * ## Implementation
31
+ *
32
+ * This function manually traverses the DOM tree and sets:
33
+ * - `parentTimegroup`: The immediate parent timegroup (used for start time calculations)
34
+ * - `rootTimegroup`: The root timegroup (used for ownCurrentTimeMs calculations)
35
+ *
36
+ * This replicates what Lit Context's `@provide`/`@consume` decorators would do if elements
37
+ * connected in parent-first order (as happens with declarative HTML parsing).
38
+ *
39
+ * @param searchRoot - The root element to search within (typically document.body or ef-workbench)
40
+ * @param rootTimegroup - The root timegroup element that should be the hierarchy root
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const rootTimegroup = document.querySelector('ef-timegroup');
45
+ * await rootTimegroup.updateComplete;
46
+ * setupTemporalHierarchy(document.body, rootTimegroup);
47
+ * await rootTimegroup.seekForRender(100); // Now all children can compute correct times
48
+ * ```
49
+ */
50
+ function setupTemporalHierarchy(searchRoot, rootTimegroup) {
51
+ const temporals = searchRoot.querySelectorAll("ef-video, ef-audio, ef-image, ef-text, ef-waveform, ef-timegroup");
52
+ for (const el of temporals) {
53
+ if (el === rootTimegroup || !el.isConnected) continue;
54
+ const parentTg = el.parentElement?.closest("ef-timegroup") || rootTimegroup;
55
+ if ("parentTimegroup" in el) el.parentTimegroup = parentTg;
56
+ if ("rootTimegroup" in el) el.rootTimegroup = rootTimegroup;
57
+ }
58
+ }
59
+
60
+ //#endregion
61
+ export { setupTemporalHierarchy };
62
+ //# sourceMappingURL=setupTemporalHierarchy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setupTemporalHierarchy.js","names":[],"sources":["../../src/elements/setupTemporalHierarchy.ts"],"sourcesContent":["/**\n * Manually wire up the temporal element hierarchy (parent/root timegroup references).\n *\n * ## Why this is needed\n *\n * Lit Context (`@provide`/`@consume`) relies on `context-request` DOM events. When a consumer\n * connects, it dispatches a `context-request` event that bubbles up to find a provider.\n *\n * **The problem:** When HTML is loaded via `loadURL()` (our rendering path), custom elements\n * connect in depth-first order - children BEFORE parents. This means:\n * 1. `ef-video` connects → dispatches `context-request` event\n * 2. `ef-timegroup` hasn't connected yet → no provider listening → event is lost\n * 3. `ef-timegroup` connects → creates provider → but too late\n *\n * DOM events are point-in-time; once missed, they're gone. There's no retroactive discovery.\n *\n * Without proper parent/root timegroup references:\n * - Child temporal elements can't compute their `ownCurrentTimeMs` (stays at 0)\n * - Video/audio elements can't seek to correct timestamps\n * - Nested timegroups can't calculate their start times correctly\n *\n * ## When to use\n *\n * Call this function after:\n * 1. All elements are connected to DOM (`connectedCallback` has fired)\n * 2. All custom elements have upgraded (`updateComplete` has resolved)\n * 3. Before any time-based operations (seeking, rendering)\n *\n * ## Implementation\n *\n * This function manually traverses the DOM tree and sets:\n * - `parentTimegroup`: The immediate parent timegroup (used for start time calculations)\n * - `rootTimegroup`: The root timegroup (used for ownCurrentTimeMs calculations)\n *\n * This replicates what Lit Context's `@provide`/`@consume` decorators would do if elements\n * connected in parent-first order (as happens with declarative HTML parsing).\n *\n * @param searchRoot - The root element to search within (typically document.body or ef-workbench)\n * @param rootTimegroup - The root timegroup element that should be the hierarchy root\n *\n * @example\n * ```typescript\n * const rootTimegroup = document.querySelector('ef-timegroup');\n * await rootTimegroup.updateComplete;\n * setupTemporalHierarchy(document.body, rootTimegroup);\n * await rootTimegroup.seekForRender(100); // Now all children can compute correct times\n * ```\n */\nexport function setupTemporalHierarchy(\n searchRoot: Element,\n rootTimegroup: any,\n): void {\n const temporalSelectors =\n \"ef-video, ef-audio, ef-image, ef-text, ef-waveform, ef-timegroup\";\n const temporals = searchRoot.querySelectorAll(temporalSelectors);\n\n for (const el of temporals) {\n // Skip the root timegroup itself and disconnected elements\n if (el === rootTimegroup || !(el as any).isConnected) {\n continue;\n }\n\n // Find immediate parent timegroup (may be nested, e.g., timegroup within timegroup)\n // Use closest() starting from parentElement to avoid matching the element itself\n const parentTg =\n (el.parentElement?.closest(\"ef-timegroup\") as any) || rootTimegroup;\n\n // Set both parent and root references\n // - parentTimegroup: Used for start time calculations (evaluateStartTime)\n // - rootTimegroup: Used for ownCurrentTimeMs calculations (determineCurrentTimeSource)\n if (\"parentTimegroup\" in el) {\n (el as any).parentTimegroup = parentTg;\n }\n if (\"rootTimegroup\" in el) {\n (el as any).rootTimegroup = rootTimegroup;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,SAAgB,uBACd,YACA,eACM;CAGN,MAAM,YAAY,WAAW,iBAD3B,mEAC8D;AAEhE,MAAK,MAAM,MAAM,WAAW;AAE1B,MAAI,OAAO,iBAAiB,CAAE,GAAW,YACvC;EAKF,MAAM,WACH,GAAG,eAAe,QAAQ,eAAe,IAAY;AAKxD,MAAI,qBAAqB,GACvB,CAAC,GAAW,kBAAkB;AAEhC,MAAI,mBAAmB,GACrB,CAAC,GAAW,gBAAgB"}
@@ -26,15 +26,9 @@ const domStructureChanged = /* @__PURE__ */ new WeakMap();
26
26
  const lastAnimationCount = /* @__PURE__ */ new WeakMap();
27
27
  /**
28
28
  * Tracks which animations have already been validated to avoid duplicate warnings.
29
+ * Uses animation name + duration as the unique key.
29
30
  */
30
- const validatedAnimations = /* @__PURE__ */ new WeakSet();
31
- /**
32
- * Checks if an element is in a render clone (static DOM context).
33
- * Render clones are in containers with class "ef-render-clone-container".
34
- */
35
- const isRenderClone = (element) => {
36
- return element.closest(".ef-render-clone-container") !== null;
37
- };
31
+ const validatedAnimations = /* @__PURE__ */ new Set();
38
32
  /**
39
33
  * Validates that an animation is still valid and controllable.
40
34
  * Animations become invalid when:
@@ -65,22 +59,14 @@ const isAnimationValid = (animation, currentAnimations) => {
65
59
  * For prime timeline (interactive), discovery is responsive to DOM changes.
66
60
  *
67
61
  * Also cleans up invalid animations (cancelled, removed from DOM, etc.)
68
- *
62
+ *
69
63
  * @param providedAnimations - Optional pre-discovered animations to avoid redundant getAnimations() calls
70
64
  */
71
65
  const discoverAndTrackAnimations = (element, providedAnimations) => {
72
- const isClone = isRenderClone(element);
73
- const hasTrackedAnimations = animationTracker.has(element);
66
+ animationTracker.has(element);
74
67
  const structureChanged = domStructureChanged.get(element) ?? true;
75
- if (isClone && hasTrackedAnimations && !structureChanged) {
76
- const rootTracked$1 = animationTracker.get(element);
77
- return {
78
- tracked: rootTracked$1,
79
- current: Array.from(rootTracked$1)
80
- };
81
- }
82
68
  const currentAnimations = providedAnimations ?? element.getAnimations({ subtree: true });
83
- if (isClone) domStructureChanged.set(element, false);
69
+ domStructureChanged.set(element, false);
84
70
  lastAnimationCount.set(element, currentAnimations.length);
85
71
  for (const animation of currentAnimations) {
86
72
  const effect = animation.effect;
@@ -114,13 +100,11 @@ const discoverAndTrackAnimations = (element, providedAnimations) => {
114
100
  anims.push(animation);
115
101
  }
116
102
  }
117
- const subtreeElements = element.querySelectorAll("*");
118
- for (const el of subtreeElements) {
119
- const tracked = animationTracker.get(el);
120
- if (tracked) {
121
- const currentElAnimations = elementAnimationsMap.get(el) || [];
122
- for (const animation of tracked) if (!isAnimationValid(animation, currentElAnimations)) tracked.delete(animation);
123
- if (tracked.size === 0) animationTracker.delete(el);
103
+ if (structureChanged) for (const [el, tracked] of elementAnimationsMap) {
104
+ const existingTracked = animationTracker.get(el);
105
+ if (existingTracked) {
106
+ for (const animation of existingTracked) if (!isAnimationValid(animation, tracked)) existingTracked.delete(animation);
107
+ if (existingTracked.size === 0) animationTracker.delete(el);
124
108
  }
125
109
  }
126
110
  return {
@@ -187,21 +171,7 @@ var VisibilityPolicy = class {
187
171
  return element.tagName === "EF-TEXT-SEGMENT";
188
172
  }
189
173
  };
190
- /**
191
- * Animation policy: determines when animations should be coordinated.
192
- *
193
- * WHY: When an animation reaches exactly the end time of an element, using exclusive
194
- * end would make the element invisible, causing the animation to be removed from the
195
- * DOM and creating a visual jump. By using inclusive end, we ensure animations remain
196
- * coordinated even at exact boundary times, providing smooth visual transitions.
197
- */
198
- var AnimationPolicy = class {
199
- shouldIncludeEndBoundary(_element) {
200
- return true;
201
- }
202
- };
203
174
  const visibilityPolicy = new VisibilityPolicy();
204
- const animationPolicy = new AnimationPolicy();
205
175
  /**
206
176
  * Determines if an element should be visible based on its phase and visibility policy.
207
177
  */
@@ -212,11 +182,20 @@ const shouldBeVisible = (phase, element) => {
212
182
  };
213
183
  /**
214
184
  * Determines if animations should be coordinated based on element phase and animation policy.
185
+ *
186
+ * CRITICAL: Always returns true to support scrubbing to arbitrary times.
187
+ *
188
+ * Previously, this function skipped coordination for before-start and after-end phases as an
189
+ * optimization for live playback. However, this broke scrubbing scenarios where we seek to
190
+ * arbitrary times (timeline scrubbing, thumbnails, video export).
191
+ *
192
+ * The performance cost of always coordinating is minimal:
193
+ * - Animations only update when element time changes
194
+ * - Paused animation updates are optimized by the browser
195
+ * - The benefit is correct animation state at all times, regardless of phase
215
196
  */
216
- const shouldCoordinateAnimations = (phase, element) => {
217
- if (phase === "before-start" || phase === "after-end") return false;
218
- if (phase === "active") return true;
219
- return animationPolicy.shouldIncludeEndBoundary(element);
197
+ const shouldCoordinateAnimations = (_phase, _element) => {
198
+ return true;
220
199
  };
221
200
  /**
222
201
  * Evaluates what the element's state should be based on the timeline.
@@ -236,21 +215,6 @@ const evaluateTemporalState = (element) => {
236
215
  };
237
216
  };
238
217
  /**
239
- * Evaluates element visibility state specifically for animation coordination.
240
- * Uses inclusive end boundaries to prevent animation jumps at exact boundaries.
241
- *
242
- * This is exported for external use cases that need animation-specific visibility
243
- * evaluation without the full ElementUpdateContext.
244
- */
245
- const evaluateAnimationVisibilityState = (element) => {
246
- const state = evaluateTemporalState(element);
247
- const shouldCoordinate = shouldCoordinateAnimations(state.phase, element);
248
- return {
249
- ...state,
250
- isVisible: shouldCoordinate
251
- };
252
- };
253
- /**
254
218
  * Capability check: determines if an element supports stagger offset.
255
219
  * Encapsulates the knowledge of which element types support this feature.
256
220
  */
@@ -422,22 +386,20 @@ const hasTransformAnimation = (keyframes) => {
422
386
  };
423
387
  /**
424
388
  * Validates CSS animation fill-mode to prevent flashing issues.
425
- *
389
+ *
426
390
  * CRITICAL: Editframe's timeline system pauses animations and manually controls them
427
391
  * via animation.currentTime. This means elements exist in the DOM before their animations
428
392
  * start. Without proper fill-mode, elements will "flash" to their natural state before
429
393
  * the animation begins.
430
- *
394
+ *
431
395
  * Common issues:
432
396
  * - Delayed animations without 'backwards': Element shows natural state during delay
433
397
  * - Fade-in without 'backwards': Element visible before fade starts
434
398
  * - Fade-out without 'forwards': Element snaps back after fade completes
435
- *
399
+ *
436
400
  * Only runs in development mode to avoid performance impact in production.
437
401
  */
438
402
  const validateAnimationFillMode = (animation, timing) => {
439
- if (validatedAnimations.has(animation)) return;
440
- validatedAnimations.add(animation);
441
403
  const effect = animation.effect;
442
404
  if (!validateAnimationEffect(effect)) return;
443
405
  const fill = effect.getTiming().fill || "none";
@@ -448,18 +410,21 @@ const validateAnimationFillMode = (animation, timing) => {
448
410
  const animationNameValue = window.getComputedStyle(target).animationName;
449
411
  if (animationNameValue && animationNameValue !== "none") animationName = animationNameValue.split(",")[0]?.trim() || "unknown";
450
412
  }
413
+ const validationKey = `${animationName}-${timing.duration}`;
414
+ if (validatedAnimations.has(validationKey)) return;
415
+ validatedAnimations.add(validationKey);
451
416
  const warnings = [];
452
417
  if (timing.delay > 0 && fill !== "backwards" && fill !== "both") warnings.push(`⚠️ Animation "${animationName}" has a ${timing.delay}ms delay but no 'backwards' fill-mode.`, ` This will cause the element to show its natural state during the delay, then suddenly jump when the animation starts.`, ` Fix: Add 'backwards' or 'both' to the animation shorthand.`, ` Example: animation: ${animationName} ${timing.duration}ms ${timing.delay}ms backwards;`);
453
418
  try {
454
419
  const keyframes = effect.getKeyframes();
455
420
  const fadePattern = detectFadePattern(keyframes);
456
421
  const hasTransform = hasTransformAnimation(keyframes);
457
- if ((fadePattern === "fade-in" || hasTransform) && fill !== "backwards" && fill !== "both") warnings.push(`⚠️ Animation "${animationName}" appears to be a fade-in or slide-in effect but lacks 'backwards' fill-mode.`, ` The element will be visible in its natural state before the animation starts.`, ` Fix: Add 'backwards' or 'both' to the animation.`, ` Example: animation: ${animationName} ${timing.duration}ms backwards;`);
458
- if (fadePattern === "fade-out" && fill !== "forwards" && fill !== "both") warnings.push(`⚠️ Animation "${animationName}" appears to be a fade-out effect but lacks 'forwards' fill-mode.`, ` The element will snap back to its natural state after the animation completes.`, ` Fix: Add 'forwards' or 'both' to the animation.`, ` Example: animation: ${animationName} ${timing.duration}ms forwards;`);
459
- if (fadePattern === "both" && fill !== "both") warnings.push(`⚠️ Animation "${animationName}" has both fade-in and fade-out keyframes but doesn't use 'both' fill-mode.`, ` Fix: Use 'both' to apply initial and final states.`, ` Example: animation: ${animationName} ${timing.duration}ms both;`);
422
+ if ((fadePattern === "fade-in" || hasTransform) && fill !== "backwards" && fill !== "both") warnings.push(`⚠️ Animation "${animationName}" modifies initial state but lacks 'backwards' fill-mode.`, ` The element will be visible in its natural state before the animation starts.`, ` Fix: Add 'backwards' or 'both' to the animation.`, ` Example: animation: ${animationName} ${timing.duration}ms backwards;`);
423
+ if (fadePattern === "fade-out" && fill !== "forwards" && fill !== "both") warnings.push(`⚠️ Animation "${animationName}" modifies final state but lacks 'forwards' fill-mode.`, ` The element will snap back to its natural state after the animation completes.`, ` Fix: Add 'forwards' or 'both' to the animation.`, ` Example: animation: ${animationName} ${timing.duration}ms forwards;`);
424
+ if (fadePattern === "both" && fill !== "both") warnings.push(`⚠️ Animation "${animationName}" modifies both initial and final state but doesn't use 'both' fill-mode.`, ` Fix: Use 'both' to apply initial and final states.`, ` Example: animation: ${animationName} ${timing.duration}ms both;`);
460
425
  } catch (e) {}
461
- if (warnings.length > 0) {
462
- console.group("%c🎬 Editframe Animation Fill-Mode Warning", "color: #f59e0b; font-weight: bold");
426
+ if (warnings.length > 0 && typeof window !== "undefined" && window.__EDITFRAME_FILL_MODE_WARNINGS__) {
427
+ console.groupCollapsed("%c🎬 Editframe Animation Fill-Mode Warning", "color: #f59e0b; font-weight: bold");
463
428
  warnings.forEach((warning) => console.log(warning));
464
429
  console.log("\n📚 Learn more: https://developer.mozilla.org/en-US/docs/Web/CSS/animation-fill-mode");
465
430
  console.groupEnd();
@@ -602,31 +567,41 @@ const evaluateElementState = (element) => {
602
567
  */
603
568
  const updateAnimations = (element) => {
604
569
  const allAnimations = element.getAnimations({ subtree: true });
605
- const animationsByElement = /* @__PURE__ */ new Map();
570
+ const rootContext = evaluateElementState(element);
571
+ const timelineTimeMs = (element.rootTimegroup ?? element).currentTimeMs;
572
+ const { elements: collectedElements, pruned } = deepGetTemporalElements(element, timelineTimeMs);
573
+ for (const prunedElement of pruned) prunedElement.style.setProperty("display", "none");
574
+ const childContexts = [];
575
+ for (const temporalElement of collectedElements) if (!pruned.has(temporalElement)) childContexts.push(evaluateElementState(temporalElement));
576
+ const visibleChildContexts = [];
577
+ for (const ctx of childContexts) if (shouldBeVisible(ctx.state.phase, ctx.element)) visibleChildContexts.push(ctx);
578
+ const temporalSet = new Set(visibleChildContexts.map((c) => c.element));
579
+ temporalSet.add(element);
580
+ const childAnimations = /* @__PURE__ */ new Map();
606
581
  for (const animation of allAnimations) {
607
582
  const effect = animation.effect;
608
- if (effect && effect instanceof KeyframeEffect && effect.target) {
609
- const target = effect.target;
610
- let anims = animationsByElement.get(target);
611
- if (!anims) {
612
- anims = [];
613
- animationsByElement.set(target, anims);
583
+ const target = effect && effect instanceof KeyframeEffect ? effect.target : null;
584
+ if (!target || !(target instanceof Element)) continue;
585
+ let node = target;
586
+ while (node) {
587
+ if (temporalSet.has(node)) {
588
+ let anims = childAnimations.get(node);
589
+ if (!anims) {
590
+ anims = [];
591
+ childAnimations.set(node, anims);
592
+ }
593
+ anims.push(animation);
594
+ break;
614
595
  }
615
- anims.push(animation);
596
+ node = node.parentElement;
616
597
  }
617
598
  }
618
- const rootContext = evaluateElementState(element);
619
- const childContexts = deepGetTemporalElements(element).map((temporalElement) => evaluateElementState(temporalElement));
620
- applyAnimationCoordination(rootContext.element, rootContext.state.phase, animationsByElement.get(rootContext.element));
621
- childContexts.forEach((context) => {
622
- applyAnimationCoordination(context.element, context.state.phase, animationsByElement.get(context.element));
623
- });
599
+ applyAnimationCoordination(rootContext.element, rootContext.state.phase, allAnimations);
600
+ for (const context of visibleChildContexts) applyAnimationCoordination(context.element, context.state.phase, childAnimations.get(context.element) || []);
624
601
  applyVisualState(rootContext.element, rootContext.state);
625
- childContexts.forEach((context) => {
626
- applyVisualState(context.element, context.state);
627
- });
602
+ for (const context of childContexts) applyVisualState(context.element, context.state);
628
603
  };
629
604
 
630
605
  //#endregion
631
- export { cleanupTrackedAnimations, evaluateAnimationVisibilityState, updateAnimations };
606
+ export { cleanupTrackedAnimations, updateAnimations };
632
607
  //# sourceMappingURL=updateAnimations.js.map