@editframe/elements 0.30.2-beta.0 → 0.31.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EF_FRAMEGEN.d.ts +5 -0
- package/dist/EF_FRAMEGEN.js +20 -4
- package/dist/EF_FRAMEGEN.js.map +1 -1
- package/dist/EF_INTERACTIVE.js.map +1 -1
- package/dist/_virtual/rolldown_runtime.js +27 -0
- package/dist/canvas/EFCanvas.d.ts +311 -0
- package/dist/canvas/EFCanvas.js +1089 -0
- package/dist/canvas/EFCanvas.js.map +1 -0
- package/dist/canvas/EFCanvasItem.d.ts +55 -0
- package/dist/canvas/EFCanvasItem.js +72 -0
- package/dist/canvas/EFCanvasItem.js.map +1 -0
- package/dist/canvas/api/CanvasAPI.d.ts +115 -0
- package/dist/canvas/api/CanvasAPI.js +182 -0
- package/dist/canvas/api/CanvasAPI.js.map +1 -0
- package/dist/canvas/api/types.d.ts +42 -0
- package/dist/canvas/coordinateTransform.js +90 -0
- package/dist/canvas/coordinateTransform.js.map +1 -0
- package/dist/canvas/getElementBounds.js +40 -0
- package/dist/canvas/getElementBounds.js.map +1 -0
- package/dist/canvas/overlays/SelectionOverlay.js +265 -0
- package/dist/canvas/overlays/SelectionOverlay.js.map +1 -0
- package/dist/canvas/overlays/overlayState.js +153 -0
- package/dist/canvas/overlays/overlayState.js.map +1 -0
- package/dist/canvas/selection/SelectionController.js +105 -0
- package/dist/canvas/selection/SelectionController.js.map +1 -0
- package/dist/canvas/selection/SelectionModel.d.ts +98 -0
- package/dist/canvas/selection/SelectionModel.js +229 -0
- package/dist/canvas/selection/SelectionModel.js.map +1 -0
- package/dist/canvas/selection/selectionContext.d.ts +31 -0
- package/dist/canvas/selection/selectionContext.js +12 -0
- package/dist/canvas/selection/selectionContext.js.map +1 -0
- package/dist/elements/ContainerInfo.d.ts +29 -0
- package/dist/elements/ContainerInfo.js +30 -0
- package/dist/elements/ContainerInfo.js.map +1 -0
- package/dist/elements/EFAudio.d.ts +13 -3
- package/dist/elements/EFAudio.js +64 -10
- package/dist/elements/EFAudio.js.map +1 -1
- package/dist/elements/EFCaptions.d.ts +18 -16
- package/dist/elements/EFCaptions.js +110 -19
- package/dist/elements/EFCaptions.js.map +1 -1
- package/dist/elements/EFImage.d.ts +12 -2
- package/dist/elements/EFImage.js +79 -9
- package/dist/elements/EFImage.js.map +1 -1
- package/dist/elements/EFMedia/AssetIdMediaEngine.js +51 -4
- package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/AssetMediaEngine.js +125 -52
- package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/BaseMediaEngine.js +24 -6
- package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/JitMediaEngine.js +12 -8
- package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +46 -7
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +98 -73
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +28 -5
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +18 -6
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +8 -2
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +31 -6
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +28 -5
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +97 -72
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js.map +1 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/BufferUtils.js +1 -1
- package/dist/elements/EFMedia/shared/BufferUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +25 -14
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +47 -16
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +37 -19
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +65 -21
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +8 -3
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js +32 -9
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +33 -10
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +23 -8
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js +34 -10
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +31 -8
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +31 -114
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +44 -8
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js.map +1 -1
- package/dist/elements/EFMedia.d.ts +18 -7
- package/dist/elements/EFMedia.js +23 -3
- package/dist/elements/EFMedia.js.map +1 -1
- package/dist/elements/EFPanZoom.d.ts +96 -0
- package/dist/elements/EFPanZoom.js +290 -0
- package/dist/elements/EFPanZoom.js.map +1 -0
- package/dist/elements/EFSourceMixin.js +7 -6
- package/dist/elements/EFSourceMixin.js.map +1 -1
- package/dist/elements/EFSurface.d.ts +6 -6
- package/dist/elements/EFSurface.js +7 -2
- package/dist/elements/EFSurface.js.map +1 -1
- package/dist/elements/EFTemporal.d.ts +2 -1
- package/dist/elements/EFTemporal.js +192 -71
- package/dist/elements/EFTemporal.js.map +1 -1
- package/dist/elements/EFText.d.ts +5 -4
- package/dist/elements/EFText.js +102 -13
- package/dist/elements/EFText.js.map +1 -1
- package/dist/elements/EFTextSegment.d.ts +32 -6
- package/dist/elements/EFTextSegment.js +53 -15
- package/dist/elements/EFTextSegment.js.map +1 -1
- package/dist/elements/EFThumbnailStrip.d.ts +118 -56
- package/dist/elements/EFThumbnailStrip.js +522 -358
- package/dist/elements/EFThumbnailStrip.js.map +1 -1
- package/dist/elements/EFTimegroup.d.ts +223 -27
- package/dist/elements/EFTimegroup.js +850 -147
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/dist/elements/EFVideo.d.ts +42 -5
- package/dist/elements/EFVideo.js +165 -11
- package/dist/elements/EFVideo.js.map +1 -1
- package/dist/elements/EFWaveform.d.ts +6 -6
- package/dist/elements/EFWaveform.js +2 -1
- package/dist/elements/EFWaveform.js.map +1 -1
- package/dist/elements/ElementPositionInfo.d.ts +35 -0
- package/dist/elements/ElementPositionInfo.js +49 -0
- package/dist/elements/ElementPositionInfo.js.map +1 -0
- package/dist/elements/FetchMixin.js +16 -1
- package/dist/elements/FetchMixin.js.map +1 -1
- package/dist/elements/SessionThumbnailCache.js +152 -0
- package/dist/elements/SessionThumbnailCache.js.map +1 -0
- package/dist/elements/TargetController.js +3 -1
- package/dist/elements/TargetController.js.map +1 -1
- package/dist/elements/TimegroupController.js +9 -3
- package/dist/elements/TimegroupController.js.map +1 -1
- package/dist/elements/findRootTemporal.js +30 -0
- package/dist/elements/findRootTemporal.js.map +1 -0
- package/dist/elements/renderTemporalAudio.js +18 -5
- package/dist/elements/renderTemporalAudio.js.map +1 -1
- package/dist/elements/updateAnimations.js +171 -28
- package/dist/elements/updateAnimations.js.map +1 -1
- package/dist/getRenderInfo.d.ts +2 -2
- package/dist/gui/ContextMixin.js +4 -2
- package/dist/gui/ContextMixin.js.map +1 -1
- package/dist/gui/Controllable.js +74 -1
- package/dist/gui/Controllable.js.map +1 -1
- package/dist/gui/EFActiveRootTemporal.d.ts +50 -0
- package/dist/gui/EFActiveRootTemporal.js +94 -0
- package/dist/gui/EFActiveRootTemporal.js.map +1 -0
- package/dist/gui/EFConfiguration.d.ts +11 -5
- package/dist/gui/EFConfiguration.js.map +1 -1
- package/dist/gui/EFControls.d.ts +2 -2
- package/dist/gui/EFControls.js +109 -13
- package/dist/gui/EFControls.js.map +1 -1
- package/dist/gui/EFDial.d.ts +4 -4
- package/dist/gui/EFFilmstrip.d.ts +11 -214
- package/dist/gui/EFFilmstrip.js +53 -1152
- package/dist/gui/EFFilmstrip.js.map +1 -1
- package/dist/gui/EFFitScale.d.ts +3 -3
- package/dist/gui/EFFitScale.js +39 -12
- package/dist/gui/EFFitScale.js.map +1 -1
- package/dist/gui/EFFocusOverlay.d.ts +4 -4
- package/dist/gui/EFOverlayItem.d.ts +48 -0
- package/dist/gui/EFOverlayItem.js +97 -0
- package/dist/gui/EFOverlayItem.js.map +1 -0
- package/dist/gui/EFOverlayLayer.d.ts +70 -0
- package/dist/gui/EFOverlayLayer.js +104 -0
- package/dist/gui/EFOverlayLayer.js.map +1 -0
- package/dist/gui/EFPause.d.ts +4 -4
- package/dist/gui/EFPlay.d.ts +4 -4
- package/dist/gui/EFResizableBox.d.ts +12 -16
- package/dist/gui/EFResizableBox.js +109 -451
- package/dist/gui/EFResizableBox.js.map +1 -1
- package/dist/gui/EFScrubber.d.ts +30 -5
- package/dist/gui/EFScrubber.js +224 -31
- package/dist/gui/EFScrubber.js.map +1 -1
- package/dist/gui/EFTimeDisplay.d.ts +4 -4
- package/dist/gui/EFTimeDisplay.js +4 -1
- package/dist/gui/EFTimeDisplay.js.map +1 -1
- package/dist/gui/EFTimelineRuler.d.ts +71 -0
- package/dist/gui/EFTimelineRuler.js +320 -0
- package/dist/gui/EFTimelineRuler.js.map +1 -0
- package/dist/gui/EFToggleLoop.d.ts +4 -4
- package/dist/gui/EFTogglePlay.d.ts +4 -4
- package/dist/gui/EFTransformHandles.d.ts +91 -0
- package/dist/gui/EFTransformHandles.js +393 -0
- package/dist/gui/EFTransformHandles.js.map +1 -0
- package/dist/gui/EFWorkbench.d.ts +182 -4
- package/dist/gui/EFWorkbench.js +2067 -22
- package/dist/gui/EFWorkbench.js.map +1 -1
- package/dist/gui/FitScaleHelpers.d.ts +31 -0
- package/dist/gui/FitScaleHelpers.js +41 -0
- package/dist/gui/FitScaleHelpers.js.map +1 -0
- package/dist/gui/PlaybackController.d.ts +2 -1
- package/dist/gui/PlaybackController.js +46 -15
- package/dist/gui/PlaybackController.js.map +1 -1
- package/dist/gui/TWMixin.js +1 -1
- package/dist/gui/TWMixin.js.map +1 -1
- package/dist/gui/hierarchy/EFHierarchy.d.ts +65 -0
- package/dist/gui/hierarchy/EFHierarchy.js +338 -0
- package/dist/gui/hierarchy/EFHierarchy.js.map +1 -0
- package/dist/gui/hierarchy/EFHierarchyItem.d.ts +118 -0
- package/dist/gui/hierarchy/EFHierarchyItem.js +551 -0
- package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -0
- package/dist/gui/hierarchy/hierarchyContext.d.ts +38 -0
- package/dist/gui/hierarchy/hierarchyContext.js +8 -0
- package/dist/gui/hierarchy/hierarchyContext.js.map +1 -0
- package/dist/gui/icons.js +34 -0
- package/dist/gui/icons.js.map +1 -0
- package/dist/gui/panZoomTransformContext.js +12 -0
- package/dist/gui/panZoomTransformContext.js.map +1 -0
- package/dist/gui/previewSettingsContext.js +12 -0
- package/dist/gui/previewSettingsContext.js.map +1 -0
- package/dist/gui/timeline/EFTimeline.d.ts +270 -0
- package/dist/gui/timeline/EFTimeline.js +1369 -0
- package/dist/gui/timeline/EFTimeline.js.map +1 -0
- package/dist/gui/timeline/EFTimelineRow.js +374 -0
- package/dist/gui/timeline/EFTimelineRow.js.map +1 -0
- package/dist/gui/timeline/TrimHandles.d.ts +36 -0
- package/dist/gui/timeline/TrimHandles.js +204 -0
- package/dist/gui/timeline/TrimHandles.js.map +1 -0
- package/dist/gui/timeline/flattenHierarchy.js +31 -0
- package/dist/gui/timeline/flattenHierarchy.js.map +1 -0
- package/dist/gui/timeline/timelineStateContext.d.ts +26 -0
- package/dist/gui/timeline/timelineStateContext.js +42 -0
- package/dist/gui/timeline/timelineStateContext.js.map +1 -0
- package/dist/gui/timeline/tracks/AudioTrack.js +264 -0
- package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/CaptionsTrack.js +595 -0
- package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/HTMLTrack.js +19 -0
- package/dist/gui/timeline/tracks/HTMLTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/ImageTrack.js +53 -0
- package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TextTrack.js +250 -0
- package/dist/gui/timeline/tracks/TextTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TimegroupTrack.js +143 -0
- package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TrackItem.js +269 -0
- package/dist/gui/timeline/tracks/TrackItem.js.map +1 -0
- package/dist/gui/timeline/tracks/VideoTrack.js +265 -0
- package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/WaveformTrack.js +19 -0
- package/dist/gui/timeline/tracks/WaveformTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/ensureTrackItemInit.js +1 -0
- package/dist/gui/timeline/tracks/preloadTracks.js +9 -0
- package/dist/gui/timeline/tracks/renderTrackChildren.js +119 -0
- package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -0
- package/dist/gui/timeline/tracks/waveformUtils.js +80 -0
- package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -0
- package/dist/gui/transformCalculations.js +217 -0
- package/dist/gui/transformCalculations.js.map +1 -0
- package/dist/gui/transformUtils.d.ts +37 -0
- package/dist/gui/transformUtils.js +77 -0
- package/dist/gui/transformUtils.js.map +1 -0
- package/dist/gui/tree/EFTree.d.ts +59 -0
- package/dist/gui/tree/EFTree.js +174 -0
- package/dist/gui/tree/EFTree.js.map +1 -0
- package/dist/gui/tree/EFTreeItem.d.ts +38 -0
- package/dist/gui/tree/EFTreeItem.js +146 -0
- package/dist/gui/tree/EFTreeItem.js.map +1 -0
- package/dist/gui/tree/treeContext.d.ts +60 -0
- package/dist/gui/tree/treeContext.js +23 -0
- package/dist/gui/tree/treeContext.js.map +1 -0
- package/dist/index.d.ts +32 -8
- package/dist/index.js +30 -6
- package/dist/index.js.map +1 -1
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +688 -0
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +1 -0
- package/dist/node_modules/react/cjs/react.development.js +1521 -0
- package/dist/node_modules/react/cjs/react.development.js.map +1 -0
- package/dist/node_modules/react/index.js +13 -0
- package/dist/node_modules/react/index.js.map +1 -0
- package/dist/node_modules/react/jsx-runtime.js +13 -0
- package/dist/node_modules/react/jsx-runtime.js.map +1 -0
- package/dist/preview/AdaptiveResolutionTracker.js +228 -0
- package/dist/preview/AdaptiveResolutionTracker.js.map +1 -0
- package/dist/preview/RenderProfiler.js +135 -0
- package/dist/preview/RenderProfiler.js.map +1 -0
- package/dist/preview/previewSettings.js +131 -0
- package/dist/preview/previewSettings.js.map +1 -0
- package/dist/preview/previewTypes.js +64 -0
- package/dist/preview/previewTypes.js.map +1 -0
- package/dist/preview/renderTimegroupPreview.js +656 -0
- package/dist/preview/renderTimegroupPreview.js.map +1 -0
- package/dist/preview/renderTimegroupToCanvas.d.ts +37 -0
- package/dist/preview/renderTimegroupToCanvas.js +840 -0
- package/dist/preview/renderTimegroupToCanvas.js.map +1 -0
- package/dist/preview/renderTimegroupToVideo.d.ts +39 -0
- package/dist/preview/renderTimegroupToVideo.js +274 -0
- package/dist/preview/renderTimegroupToVideo.js.map +1 -0
- package/dist/preview/renderers.js +16 -0
- package/dist/preview/renderers.js.map +1 -0
- package/dist/preview/statsTrackingStrategy.js +201 -0
- package/dist/preview/statsTrackingStrategy.js.map +1 -0
- package/dist/preview/thumbnailCacheSettings.js +52 -0
- package/dist/preview/thumbnailCacheSettings.js.map +1 -0
- package/dist/preview/workers/WorkerPool.js +178 -0
- package/dist/preview/workers/WorkerPool.js.map +1 -0
- package/dist/sandbox/PlaybackControls.js +10 -0
- package/dist/sandbox/PlaybackControls.js.map +1 -0
- package/dist/sandbox/ScenarioRunner.js +1 -0
- package/dist/sandbox/index.js +2 -0
- package/dist/style.css +68 -67
- package/dist/transcoding/types/index.d.ts +2 -1
- package/dist/transcoding/utils/UrlGenerator.d.ts +6 -1
- package/dist/transcoding/utils/UrlGenerator.js +12 -3
- package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
- package/dist/utils/LRUCache.js +1 -375
- package/dist/utils/LRUCache.js.map +1 -1
- package/dist/utils/frameTime.js +14 -0
- package/dist/utils/frameTime.js.map +1 -0
- package/package.json +3 -3
- package/test/profilingPlugin.ts +223 -0
- package/test/recordReplayProxyPlugin.js +22 -27
- package/test/thumbnail-performance-test.html +116 -0
- package/test/visualRegressionUtils.ts +286 -0
- package/types.json +1 -1
- package/dist/elements/TimegroupController.d.ts +0 -18
- package/dist/msToTimeCode.js +0 -17
- package/dist/msToTimeCode.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextMixin.js","names":["#getTokenCacheKey","#parseTokenExpiration","#apiHost","#targetTemporal","#onControllerUpdate","#controllerSubscribed","#targetTemporalProvider","#loop","#playingProvider","#loopProvider","#currentTimeMsProvider","#signingURL","#timegroupObserver"],"sources":["../../src/gui/ContextMixin.ts"],"sourcesContent":["import { ContextProvider, consume, createContext, provide } from \"@lit/context\";\nimport type { LitElement } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\nimport { EF_RENDERING } from \"../EF_RENDERING.ts\";\nimport {\n isEFTemporal,\n type TemporalMixinInterface,\n} from \"../elements/EFTemporal.js\";\nimport { globalURLTokenDeduplicator } from \"../transcoding/cache/URLTokenDeduplicator.js\";\nimport { currentTimeContext } from \"./currentTimeContext.js\";\nimport { durationContext } from \"./durationContext.js\";\nimport {\n type EFConfiguration,\n efConfigurationContext,\n} from \"./EFConfiguration.ts\";\nimport { efContext } from \"./efContext.js\";\nimport { fetchContext } from \"./fetchContext.js\";\nimport { type FocusContext, focusContext } from \"./focusContext.js\";\nimport { focusedElementContext } from \"./focusedElementContext.js\";\nimport { loopContext, playingContext } from \"./playingContext.js\";\n\nexport const targetTemporalContext =\n createContext<TemporalMixinInterface | null>(Symbol(\"target-temporal\"));\n\nexport declare class ContextMixinInterface extends LitElement {\n signingURL?: string;\n apiHost?: string;\n rendering: boolean;\n playing: boolean;\n loop: boolean;\n currentTimeMs: number;\n focusedElement?: HTMLElement;\n targetTemporal: TemporalMixinInterface | null;\n play(): Promise<void>;\n pause(): void;\n}\n\nconst contextMixinSymbol = Symbol(\"contextMixin\");\n\nexport function isContextMixin(value: any): value is ContextMixinInterface {\n return (\n typeof value === \"object\" &&\n value !== null &&\n contextMixinSymbol in value.constructor\n );\n}\n\ntype Constructor<T = {}> = new (...args: any[]) => T;\nexport function ContextMixin<T extends Constructor<LitElement>>(superClass: T) {\n class ContextElement extends superClass {\n static [contextMixinSymbol] = true;\n\n @consume({ context: efConfigurationContext, subscribe: true })\n efConfiguration: EFConfiguration | null = null;\n\n @provide({ context: focusContext })\n focusContext = this as FocusContext;\n\n @provide({ context: focusedElementContext })\n @state()\n focusedElement?: HTMLElement;\n\n #playingProvider!: ContextProvider<typeof playingContext>;\n #loopProvider!: ContextProvider<typeof loopContext>;\n #currentTimeMsProvider!: ContextProvider<typeof currentTimeContext>;\n #targetTemporalProvider!: ContextProvider<typeof targetTemporalContext>;\n\n #loop = false;\n\n #apiHost?: string;\n @property({ type: String, attribute: \"api-host\" })\n get apiHost() {\n return this.#apiHost ?? this.efConfiguration?.apiHost ?? \"\";\n }\n\n set apiHost(value: string) {\n this.#apiHost = value;\n }\n\n @provide({ context: efContext })\n efContext = this;\n\n #targetTemporal: TemporalMixinInterface | null = null;\n\n @state()\n get targetTemporal(): TemporalMixinInterface | null {\n return this.#targetTemporal;\n }\n #controllerSubscribed = false;\n\n /**\n * Find the first root temporal element (recursively searches through children)\n * Supports ef-timegroup, ef-video, ef-audio, and any other temporal elements\n * even when they're wrapped in non-temporal elements like divs\n */\n private findRootTemporal(): TemporalMixinInterface | null {\n const findRecursive = (\n element: Element,\n ): TemporalMixinInterface | null => {\n if (isEFTemporal(element)) {\n return element as TemporalMixinInterface & HTMLElement;\n }\n\n for (const child of element.children) {\n const found = findRecursive(child);\n if (found) return found;\n }\n\n return null;\n };\n\n for (const child of this.children) {\n const found = findRecursive(child);\n if (found) return found;\n }\n\n return null;\n }\n\n set targetTemporal(value: TemporalMixinInterface | null) {\n if (this.#targetTemporal === value) return;\n\n // Unsubscribe from old controller updates\n if (this.#targetTemporal?.playbackController) {\n this.#targetTemporal.playbackController.removeListener(\n this.#onControllerUpdate,\n );\n this.#controllerSubscribed = false;\n }\n\n this.#targetTemporal = value;\n this.#targetTemporalProvider?.setValue(value);\n\n // Sync all provided contexts\n this.requestUpdate(\"targetTemporal\");\n this.requestUpdate(\"playing\");\n this.requestUpdate(\"loop\");\n this.requestUpdate(\"currentTimeMs\");\n\n // If the new targetTemporal has a playbackController, apply stored loop value immediately\n if (value?.playbackController && this.#loop) {\n value.playbackController.setLoop(this.#loop);\n }\n\n // If the new targetTemporal doesn't have a playbackController yet,\n // wait for it to complete its updates (it might be initializing)\n if (value && !value.playbackController) {\n // Wait for the temporal element to initialize\n (value as any).updateComplete?.then(() => {\n if (value === this.#targetTemporal && !this.#controllerSubscribed) {\n this.requestUpdate();\n }\n });\n }\n }\n\n #onControllerUpdate = (\n event: import(\"./PlaybackController.js\").PlaybackControllerUpdateEvent,\n ) => {\n switch (event.property) {\n case \"playing\":\n this.#playingProvider.setValue(event.value as boolean);\n break;\n case \"loop\":\n this.#loopProvider.setValue(event.value as boolean);\n break;\n case \"currentTimeMs\":\n this.#currentTimeMsProvider.setValue(event.value as number);\n break;\n }\n };\n\n // Add reactive properties that depend on the targetTemporal\n @provide({ context: durationContext })\n @property({ type: Number })\n durationMs = 0;\n\n @property({ type: Number })\n endTimeMs = 0;\n\n @provide({ context: fetchContext })\n fetch = async (url: string, init: RequestInit = {}) => {\n init.headers ||= {};\n Object.assign(init.headers, {\n \"Content-Type\": \"application/json\",\n });\n\n if (!EF_RENDERING() && this.signingURL) {\n const { cacheKey, signingPayload } = this.#getTokenCacheKey(url);\n\n // Use global token deduplicator to share tokens across all context providers\n const urlToken = await globalURLTokenDeduplicator.getToken(\n cacheKey,\n async () => {\n try {\n const response = await fetch(this.signingURL, {\n method: \"POST\",\n body: JSON.stringify(signingPayload),\n });\n\n if (response.ok) {\n const tokenData = await response.json();\n return tokenData.token;\n }\n throw new Error(\n `Failed to sign URL: ${url}. SigningURL: ${this.signingURL} ${response.status} ${response.statusText}`,\n );\n } catch (error) {\n console.error(\"ContextMixin urlToken fetch error\", url, error);\n throw error;\n }\n },\n (token: string) => this.#parseTokenExpiration(token),\n );\n\n Object.assign(init.headers, {\n authorization: `Bearer ${urlToken}`,\n });\n } else {\n init.credentials = \"include\";\n }\n\n try {\n const fetchPromise = fetch(url, init);\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 console.error(\n \"ContextMixin fetch error\",\n url,\n error,\n window.location.href,\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 enhancedError.name = error.name;\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(\n \"ContextMixin fetch error (synchronous)\",\n url,\n error,\n window.location.href,\n );\n throw error;\n }\n };\n\n // Note: URL token caching is now handled globally via URLTokenDeduplicator\n // Keeping these for any potential backwards compatibility, but they're no longer used\n\n /**\n * Generate a cache key for URL token based on signing strategy\n *\n * Uses unified prefix + parameter matching approach:\n * - For transcode URLs: signs base \"/api/v1/transcode\" + params like {url: \"source.mp4\"}\n * - For regular URLs: signs full URL with empty params {}\n * - All validation uses prefix matching + exhaustive parameter matching\n * - Multiple transcode segments with same source share one token (reduces round-trips)\n */\n #getTokenCacheKey(url: string): {\n cacheKey: string;\n signingPayload: { url: string; params?: Record<string, string> };\n } {\n try {\n const urlObj = new URL(url);\n\n // Check if this is a transcode URL pattern\n if (urlObj.pathname.includes(\"/api/v1/transcode/\")) {\n const urlParam = urlObj.searchParams.get(\"url\");\n if (urlParam) {\n // For transcode URLs, sign the base path + url parameter\n const basePath = `${urlObj.origin}/api/v1/transcode`;\n const cacheKey = `${basePath}?url=${urlParam}`;\n return {\n cacheKey,\n signingPayload: { url: basePath, params: { url: urlParam } },\n };\n }\n }\n\n // For non-transcode URLs, use full URL (existing behavior)\n return {\n cacheKey: url,\n signingPayload: { url },\n };\n } catch {\n // If URL parsing fails, fall back to full URL\n return {\n cacheKey: url,\n signingPayload: { url },\n };\n }\n }\n\n /**\n * Parse JWT token to extract safe expiration time (with buffer)\n * @param token JWT token string\n * @returns Safe expiration timestamp in milliseconds (actual expiry minus buffer), or 0 if parsing fails\n */\n #parseTokenExpiration(token: string): number {\n try {\n // JWT has 3 parts separated by dots: header.payload.signature\n const parts = token.split(\".\");\n if (parts.length !== 3) return 0;\n\n // Decode the payload (second part)\n const payload = parts[1];\n if (!payload) return 0;\n\n const decoded = atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n const parsed = JSON.parse(decoded);\n\n // Extract timestamps (in seconds)\n const exp = parsed.exp;\n const iat = parsed.iat;\n if (!exp) return 0;\n\n // Calculate token lifetime and buffer\n const lifetimeSeconds = iat ? exp - iat : 3600; // Default to 1 hour if no iat\n const tenPercentBufferMs = lifetimeSeconds * 0.1 * 1000; // 10% of lifetime in ms\n const fiveMinutesMs = 5 * 60 * 1000; // 5 minutes in ms\n\n // Use whichever buffer is smaller (more conservative)\n const bufferMs = Math.min(fiveMinutesMs, tenPercentBufferMs);\n\n // Return expiration time minus buffer\n return exp * 1000 - bufferMs;\n } catch {\n return 0;\n }\n }\n\n #signingURL?: string;\n /**\n * A URL that will be used to generated signed tokens for accessing media files from the\n * editframe API. This is used to authenticate media requests per-user.\n */\n @property({ type: String, attribute: \"signing-url\" })\n get signingURL() {\n return this.#signingURL ?? this.efConfiguration?.signingURL ?? \"\";\n }\n set signingURL(value: string) {\n this.#signingURL = value;\n }\n\n @property({ type: Boolean, reflect: true })\n get playing(): boolean {\n return this.targetTemporal?.playbackController?.playing ?? false;\n }\n set playing(value: boolean) {\n if (this.targetTemporal?.playbackController) {\n this.targetTemporal.playbackController.setPlaying(value);\n }\n }\n\n @property({ type: Boolean, reflect: true, attribute: \"loop\" })\n get loop(): boolean {\n return this.targetTemporal?.playbackController?.loop ?? this.#loop;\n }\n set loop(value: boolean) {\n const oldValue = this.#loop;\n this.#loop = value;\n if (this.targetTemporal?.playbackController) {\n this.targetTemporal.playbackController.setLoop(value);\n }\n this.requestUpdate(\"loop\", oldValue);\n }\n\n @property({ type: Boolean })\n rendering = false;\n\n @property({ type: Number })\n get currentTimeMs(): number {\n return (\n this.targetTemporal?.playbackController?.currentTimeMs ?? Number.NaN\n );\n }\n set currentTimeMs(value: number) {\n if (this.targetTemporal?.playbackController) {\n this.targetTemporal.playbackController.setCurrentTimeMs(value);\n }\n }\n\n #timegroupObserver = new MutationObserver((mutations) => {\n let shouldUpdate = false;\n\n for (const mutation of mutations) {\n if (mutation.type === \"childList\") {\n const newTemporal = this.findRootTemporal();\n if (newTemporal !== this.targetTemporal) {\n this.targetTemporal = newTemporal;\n shouldUpdate = true;\n } else if (\n mutation.target instanceof Element &&\n isEFTemporal(mutation.target)\n ) {\n // Handle childList changes within existing temporal elements\n shouldUpdate = true;\n }\n } else if (mutation.type === \"attributes\") {\n // Watch for attribute changes that might affect duration\n const durationAffectingAttributes = [\n \"duration\",\n \"mode\",\n \"trimstart\",\n \"trimend\",\n \"sourcein\",\n \"sourceout\",\n ];\n\n if (\n durationAffectingAttributes.includes(\n mutation.attributeName || \"\",\n ) ||\n (mutation.target instanceof Element &&\n isEFTemporal(mutation.target))\n ) {\n shouldUpdate = true;\n }\n }\n }\n\n if (shouldUpdate) {\n // Trigger an update to ensure reactive properties recalculate\n // Use a microtask to ensure DOM updates are complete\n queueMicrotask(() => {\n // Recalculate duration and endTime when temporal element changes\n this.updateDurationProperties();\n this.requestUpdate();\n // Also ensure the targetTemporal updates its computed properties\n if (this.targetTemporal) {\n (this.targetTemporal as any).requestUpdate();\n }\n });\n }\n });\n\n /**\n * Update duration properties when temporal element changes\n */\n updateDurationProperties(): void {\n const newDuration = this.targetTemporal?.durationMs ?? 0;\n const newEndTime = this.targetTemporal?.endTimeMs ?? 0;\n\n if (this.durationMs !== newDuration) {\n this.durationMs = newDuration;\n }\n\n if (this.endTimeMs !== newEndTime) {\n this.endTimeMs = newEndTime;\n }\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n\n // Create manual context providers for playback state\n this.#playingProvider = new ContextProvider(this, {\n context: playingContext,\n initialValue: this.playing,\n });\n this.#loopProvider = new ContextProvider(this, {\n context: loopContext,\n initialValue: this.loop,\n });\n this.#currentTimeMsProvider = new ContextProvider(this, {\n context: currentTimeContext,\n initialValue: this.currentTimeMs,\n });\n this.#targetTemporalProvider = new ContextProvider(this, {\n context: targetTemporalContext,\n initialValue: this.targetTemporal,\n });\n\n // Initialize targetTemporal to first root temporal element\n this.targetTemporal = this.findRootTemporal();\n // Initialize duration properties\n this.updateDurationProperties();\n\n this.#timegroupObserver.observe(this, {\n childList: true,\n subtree: true,\n attributes: true,\n });\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this.#timegroupObserver.disconnect();\n\n // Unsubscribe from controller\n if (this.#targetTemporal?.playbackController) {\n this.#targetTemporal.playbackController.removeListener(\n this.#onControllerUpdate,\n );\n this.#controllerSubscribed = false;\n }\n\n this.pause();\n }\n\n updated(changedProperties: Map<string | number | symbol, unknown>) {\n super.updated?.(changedProperties);\n\n // Subscribe to controller when it becomes available\n if (\n !this.#controllerSubscribed &&\n this.#targetTemporal?.playbackController\n ) {\n this.#targetTemporal.playbackController.addListener(\n this.#onControllerUpdate,\n );\n this.#controllerSubscribed = true;\n\n // Apply stored loop value when playbackController becomes available\n if (this.#loop) {\n this.#targetTemporal.playbackController.setLoop(this.#loop);\n }\n\n // Trigger initial sync of context providers\n this.#playingProvider.setValue(this.playing);\n this.#loopProvider.setValue(this.loop);\n this.#currentTimeMsProvider.setValue(this.currentTimeMs);\n }\n }\n\n async play() {\n // If targetTemporal is not set, try to find it now\n // This handles cases where the DOM may not have been fully ready during connectedCallback\n if (!this.targetTemporal) {\n // Wait for any temporal custom elements to be defined\n const potentialTemporalTags = Array.from(this.children)\n .map((el) => el.tagName.toLowerCase())\n .filter((tag) => tag.startsWith(\"ef-\"));\n\n await Promise.all(\n potentialTemporalTags.map((tag) =>\n customElements.whenDefined(tag).catch(() => {}),\n ),\n );\n\n const foundTemporal = this.findRootTemporal();\n if (foundTemporal) {\n this.targetTemporal = foundTemporal;\n // Wait for it to initialize\n await (foundTemporal as any).updateComplete;\n } else {\n console.warn(\"No temporal element found to play\");\n return;\n }\n }\n\n // If playbackController doesn't exist yet, wait for it\n if (!this.targetTemporal.playbackController) {\n await (this.targetTemporal as any).updateComplete;\n // After waiting, check again\n if (!this.targetTemporal.playbackController) {\n console.warn(\"PlaybackController not available for temporal element\");\n return;\n }\n }\n\n this.targetTemporal.playbackController.play();\n }\n\n pause() {\n if (this.targetTemporal?.playbackController) {\n this.targetTemporal.playbackController.pause();\n }\n }\n }\n\n return ContextElement as Constructor<ContextMixinInterface> & T;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ContextMixin.js","names":["#getTokenCacheKey","#parseTokenExpiration","#apiHost","#targetTemporal","#onControllerUpdate","#controllerSubscribed","#targetTemporalProvider","#loop","#playingProvider","#loopProvider","#currentTimeMsProvider","#signingURL","#timegroupObserver"],"sources":["../../src/gui/ContextMixin.ts"],"sourcesContent":["import { ContextProvider, consume, createContext, provide } from \"@lit/context\";\nimport type { LitElement } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\nimport { EF_RENDERING } from \"../EF_RENDERING.ts\";\nimport {\n isEFTemporal,\n type TemporalMixinInterface,\n} from \"../elements/EFTemporal.js\";\nimport { globalURLTokenDeduplicator } from \"../transcoding/cache/URLTokenDeduplicator.js\";\nimport { currentTimeContext } from \"./currentTimeContext.js\";\nimport { durationContext } from \"./durationContext.js\";\nimport {\n type EFConfiguration,\n efConfigurationContext,\n} from \"./EFConfiguration.ts\";\nimport { efContext } from \"./efContext.js\";\nimport { fetchContext } from \"./fetchContext.js\";\nimport { type FocusContext, focusContext } from \"./focusContext.js\";\nimport { focusedElementContext } from \"./focusedElementContext.js\";\nimport { loopContext, playingContext } from \"./playingContext.js\";\n\nexport const targetTemporalContext =\n createContext<TemporalMixinInterface | null>(Symbol(\"target-temporal\"));\n\nexport declare class ContextMixinInterface extends LitElement {\n signingURL?: string;\n apiHost?: string;\n rendering: boolean;\n playing: boolean;\n loop: boolean;\n currentTimeMs: number;\n focusedElement?: HTMLElement;\n targetTemporal: TemporalMixinInterface | null;\n play(): Promise<void>;\n pause(): void;\n}\n\nconst contextMixinSymbol = Symbol(\"contextMixin\");\n\nexport function isContextMixin(value: any): value is ContextMixinInterface {\n return (\n typeof value === \"object\" &&\n value !== null &&\n contextMixinSymbol in value.constructor\n );\n}\n\ntype Constructor<T = {}> = new (...args: any[]) => T;\nexport function ContextMixin<T extends Constructor<LitElement>>(superClass: T) {\n class ContextElement extends superClass {\n static [contextMixinSymbol] = true;\n\n @consume({ context: efConfigurationContext, subscribe: true })\n efConfiguration: EFConfiguration | null = null;\n\n @provide({ context: focusContext })\n focusContext = this as FocusContext;\n\n @provide({ context: focusedElementContext })\n @state()\n focusedElement?: HTMLElement;\n\n #playingProvider!: ContextProvider<typeof playingContext>;\n #loopProvider!: ContextProvider<typeof loopContext>;\n #currentTimeMsProvider!: ContextProvider<typeof currentTimeContext>;\n #targetTemporalProvider!: ContextProvider<typeof targetTemporalContext>;\n\n #loop = false;\n\n #apiHost?: string;\n @property({ type: String, attribute: \"api-host\" })\n get apiHost() {\n return this.#apiHost ?? this.efConfiguration?.apiHost ?? \"\";\n }\n\n set apiHost(value: string) {\n this.#apiHost = value;\n }\n\n @provide({ context: efContext })\n efContext = this;\n\n #targetTemporal: TemporalMixinInterface | null = null;\n\n @state()\n get targetTemporal(): TemporalMixinInterface | null {\n return this.#targetTemporal;\n }\n #controllerSubscribed = false;\n\n /**\n * Find the first root temporal element (recursively searches through children)\n * Supports ef-timegroup, ef-video, ef-audio, and any other temporal elements\n * even when they're wrapped in non-temporal elements like divs\n */\n private findRootTemporal(): TemporalMixinInterface | null {\n const findRecursive = (\n element: Element,\n ): TemporalMixinInterface | null => {\n if (isEFTemporal(element)) {\n return element as TemporalMixinInterface & HTMLElement;\n }\n\n for (const child of element.children) {\n const found = findRecursive(child);\n if (found) return found;\n }\n\n return null;\n };\n\n for (const child of this.children) {\n const found = findRecursive(child);\n if (found) return found;\n }\n\n return null;\n }\n\n set targetTemporal(value: TemporalMixinInterface | null) {\n if (this.#targetTemporal === value) return;\n\n // Unsubscribe from old controller updates\n if (this.#targetTemporal?.playbackController) {\n this.#targetTemporal.playbackController.removeListener(\n this.#onControllerUpdate,\n );\n this.#controllerSubscribed = false;\n }\n\n this.#targetTemporal = value;\n this.#targetTemporalProvider?.setValue(value);\n\n // Sync all provided contexts\n this.requestUpdate(\"targetTemporal\");\n this.requestUpdate(\"playing\");\n this.requestUpdate(\"loop\");\n this.requestUpdate(\"currentTimeMs\");\n\n // If the new targetTemporal has a playbackController, apply stored loop value immediately\n if (value?.playbackController && this.#loop) {\n value.playbackController.setLoop(this.#loop);\n }\n\n // If the new targetTemporal doesn't have a playbackController yet,\n // wait for it to complete its updates (it might be initializing)\n if (value && !value.playbackController) {\n // Wait for the temporal element to initialize\n (value as any).updateComplete?.then(() => {\n if (value === this.#targetTemporal && !this.#controllerSubscribed) {\n this.requestUpdate();\n }\n });\n }\n }\n\n #onControllerUpdate = (\n event: import(\"./PlaybackController.js\").PlaybackControllerUpdateEvent,\n ) => {\n switch (event.property) {\n case \"playing\":\n this.#playingProvider.setValue(event.value as boolean);\n break;\n case \"loop\":\n this.#loopProvider.setValue(event.value as boolean);\n break;\n case \"currentTimeMs\":\n this.#currentTimeMsProvider.setValue(event.value as number);\n break;\n }\n };\n\n // Add reactive properties that depend on the targetTemporal\n @provide({ context: durationContext })\n @property({ type: Number })\n durationMs = 0;\n\n @property({ type: Number })\n endTimeMs = 0;\n\n @provide({ context: fetchContext })\n fetch = async (url: string, init: RequestInit = {}) => {\n init.headers ||= {};\n Object.assign(init.headers, {\n \"Content-Type\": \"application/json\",\n });\n\n // Check if this is a local @ef-* endpoint that doesn't need authentication\n // These endpoints are handled by the Vite plugin locally and don't require signing\n const isLocalEndpoint = url.startsWith(\"/@ef-\");\n\n if (!EF_RENDERING() && this.signingURL && !isLocalEndpoint) {\n const { cacheKey, signingPayload } = this.#getTokenCacheKey(url);\n\n // Use global token deduplicator to share tokens across all context providers\n const urlToken = await globalURLTokenDeduplicator.getToken(\n cacheKey,\n async () => {\n try {\n const response = await fetch(this.signingURL, {\n method: \"POST\",\n body: JSON.stringify(signingPayload),\n });\n\n if (response.ok) {\n const tokenData = await response.json();\n return tokenData.token;\n }\n throw new Error(\n `Failed to sign URL: ${url}. SigningURL: ${this.signingURL} ${response.status} ${response.statusText}`,\n );\n } catch (error) {\n console.error(\"ContextMixin urlToken fetch error\", url, error);\n throw error;\n }\n },\n (token: string) => this.#parseTokenExpiration(token),\n );\n\n Object.assign(init.headers, {\n authorization: `Bearer ${urlToken}`,\n });\n } else {\n init.credentials = \"include\";\n }\n\n try {\n const fetchPromise = fetch(url, init);\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 // For AbortErrors, re-throw directly without modification\n // DOMException properties like 'name' are read-only\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n \n console.error(\n \"ContextMixin fetch error\",\n url,\n error,\n window.location.href,\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 (except for DOMException which has read-only properties)\n if (error instanceof Error && !(error instanceof DOMException)) {\n enhancedError.name = error.name;\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(\n \"ContextMixin fetch error (synchronous)\",\n url,\n error,\n window.location.href,\n );\n throw error;\n }\n };\n\n // Note: URL token caching is now handled globally via URLTokenDeduplicator\n // Keeping these for any potential backwards compatibility, but they're no longer used\n\n /**\n * Generate a cache key for URL token based on signing strategy\n *\n * Uses unified prefix + parameter matching approach:\n * - For transcode URLs: signs base \"/api/v1/transcode\" + params like {url: \"source.mp4\"}\n * - For regular URLs: signs full URL with empty params {}\n * - All validation uses prefix matching + exhaustive parameter matching\n * - Multiple transcode segments with same source share one token (reduces round-trips)\n */\n #getTokenCacheKey(url: string): {\n cacheKey: string;\n signingPayload: { url: string; params?: Record<string, string> };\n } {\n try {\n const urlObj = new URL(url);\n\n // Check if this is a transcode URL pattern\n if (urlObj.pathname.includes(\"/api/v1/transcode/\")) {\n const urlParam = urlObj.searchParams.get(\"url\");\n if (urlParam) {\n // For transcode URLs, sign the base path + url parameter\n const basePath = `${urlObj.origin}/api/v1/transcode`;\n const cacheKey = `${basePath}?url=${urlParam}`;\n return {\n cacheKey,\n signingPayload: { url: basePath, params: { url: urlParam } },\n };\n }\n }\n\n // For non-transcode URLs, use full URL (existing behavior)\n return {\n cacheKey: url,\n signingPayload: { url },\n };\n } catch {\n // If URL parsing fails, fall back to full URL\n return {\n cacheKey: url,\n signingPayload: { url },\n };\n }\n }\n\n /**\n * Parse JWT token to extract safe expiration time (with buffer)\n * @param token JWT token string\n * @returns Safe expiration timestamp in milliseconds (actual expiry minus buffer), or 0 if parsing fails\n */\n #parseTokenExpiration(token: string): number {\n try {\n // JWT has 3 parts separated by dots: header.payload.signature\n const parts = token.split(\".\");\n if (parts.length !== 3) return 0;\n\n // Decode the payload (second part)\n const payload = parts[1];\n if (!payload) return 0;\n\n const decoded = atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n const parsed = JSON.parse(decoded);\n\n // Extract timestamps (in seconds)\n const exp = parsed.exp;\n const iat = parsed.iat;\n if (!exp) return 0;\n\n // Calculate token lifetime and buffer\n const lifetimeSeconds = iat ? exp - iat : 3600; // Default to 1 hour if no iat\n const tenPercentBufferMs = lifetimeSeconds * 0.1 * 1000; // 10% of lifetime in ms\n const fiveMinutesMs = 5 * 60 * 1000; // 5 minutes in ms\n\n // Use whichever buffer is smaller (more conservative)\n const bufferMs = Math.min(fiveMinutesMs, tenPercentBufferMs);\n\n // Return expiration time minus buffer\n return exp * 1000 - bufferMs;\n } catch {\n return 0;\n }\n }\n\n #signingURL?: string;\n /**\n * A URL that will be used to generated signed tokens for accessing media files from the\n * editframe API. This is used to authenticate media requests per-user.\n */\n @property({ type: String, attribute: \"signing-url\" })\n get signingURL() {\n return this.#signingURL ?? this.efConfiguration?.signingURL ?? \"\";\n }\n set signingURL(value: string) {\n this.#signingURL = value;\n }\n\n @property({ type: Boolean, reflect: true })\n get playing(): boolean {\n return this.targetTemporal?.playbackController?.playing ?? false;\n }\n set playing(value: boolean) {\n if (this.targetTemporal?.playbackController) {\n this.targetTemporal.playbackController.setPlaying(value);\n }\n }\n\n @property({ type: Boolean, reflect: true, attribute: \"loop\" })\n get loop(): boolean {\n return this.targetTemporal?.playbackController?.loop ?? this.#loop;\n }\n set loop(value: boolean) {\n const oldValue = this.#loop;\n this.#loop = value;\n if (this.targetTemporal?.playbackController) {\n this.targetTemporal.playbackController.setLoop(value);\n }\n this.requestUpdate(\"loop\", oldValue);\n }\n\n @property({ type: Boolean })\n rendering = false;\n\n @property({ type: Number })\n get currentTimeMs(): number {\n return (\n this.targetTemporal?.playbackController?.currentTimeMs ?? Number.NaN\n );\n }\n set currentTimeMs(value: number) {\n if (this.targetTemporal?.playbackController) {\n this.targetTemporal.playbackController.setCurrentTimeMs(value);\n }\n }\n\n #timegroupObserver = new MutationObserver((mutations) => {\n let shouldUpdate = false;\n\n for (const mutation of mutations) {\n if (mutation.type === \"childList\") {\n const newTemporal = this.findRootTemporal();\n if (newTemporal !== this.targetTemporal) {\n this.targetTemporal = newTemporal;\n shouldUpdate = true;\n } else if (\n mutation.target instanceof Element &&\n isEFTemporal(mutation.target)\n ) {\n // Handle childList changes within existing temporal elements\n shouldUpdate = true;\n }\n } else if (mutation.type === \"attributes\") {\n // Watch for attribute changes that might affect duration\n const durationAffectingAttributes = [\n \"duration\",\n \"mode\",\n \"trimstart\",\n \"trimend\",\n \"sourcein\",\n \"sourceout\",\n ];\n\n if (\n durationAffectingAttributes.includes(\n mutation.attributeName || \"\",\n ) ||\n (mutation.target instanceof Element &&\n isEFTemporal(mutation.target))\n ) {\n shouldUpdate = true;\n }\n }\n }\n\n if (shouldUpdate) {\n // Trigger an update to ensure reactive properties recalculate\n // Use a microtask to ensure DOM updates are complete\n queueMicrotask(() => {\n // Recalculate duration and endTime when temporal element changes\n this.updateDurationProperties();\n this.requestUpdate();\n // Also ensure the targetTemporal updates its computed properties\n if (this.targetTemporal) {\n (this.targetTemporal as any).requestUpdate();\n }\n });\n }\n });\n\n /**\n * Update duration properties when temporal element changes\n */\n updateDurationProperties(): void {\n const newDuration = this.targetTemporal?.durationMs ?? 0;\n const newEndTime = this.targetTemporal?.endTimeMs ?? 0;\n\n if (this.durationMs !== newDuration) {\n this.durationMs = newDuration;\n }\n\n if (this.endTimeMs !== newEndTime) {\n this.endTimeMs = newEndTime;\n }\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n\n // Create manual context providers for playback state\n this.#playingProvider = new ContextProvider(this, {\n context: playingContext,\n initialValue: this.playing,\n });\n this.#loopProvider = new ContextProvider(this, {\n context: loopContext,\n initialValue: this.loop,\n });\n this.#currentTimeMsProvider = new ContextProvider(this, {\n context: currentTimeContext,\n initialValue: this.currentTimeMs,\n });\n this.#targetTemporalProvider = new ContextProvider(this, {\n context: targetTemporalContext,\n initialValue: this.targetTemporal,\n });\n\n // Initialize targetTemporal to first root temporal element\n this.targetTemporal = this.findRootTemporal();\n // Initialize duration properties\n this.updateDurationProperties();\n\n this.#timegroupObserver.observe(this, {\n childList: true,\n subtree: true,\n attributes: true,\n });\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this.#timegroupObserver.disconnect();\n\n // Unsubscribe from controller\n if (this.#targetTemporal?.playbackController) {\n this.#targetTemporal.playbackController.removeListener(\n this.#onControllerUpdate,\n );\n this.#controllerSubscribed = false;\n }\n\n this.pause();\n }\n\n updated(changedProperties: Map<string | number | symbol, unknown>) {\n super.updated?.(changedProperties);\n\n // Subscribe to controller when it becomes available\n if (\n !this.#controllerSubscribed &&\n this.#targetTemporal?.playbackController\n ) {\n this.#targetTemporal.playbackController.addListener(\n this.#onControllerUpdate,\n );\n this.#controllerSubscribed = true;\n\n // Apply stored loop value when playbackController becomes available\n if (this.#loop) {\n this.#targetTemporal.playbackController.setLoop(this.#loop);\n }\n\n // Trigger initial sync of context providers\n this.#playingProvider.setValue(this.playing);\n this.#loopProvider.setValue(this.loop);\n this.#currentTimeMsProvider.setValue(this.currentTimeMs);\n }\n }\n\n async play() {\n // If targetTemporal is not set, try to find it now\n // This handles cases where the DOM may not have been fully ready during connectedCallback\n if (!this.targetTemporal) {\n // Wait for any temporal custom elements to be defined\n const potentialTemporalTags = Array.from(this.children)\n .map((el) => el.tagName.toLowerCase())\n .filter((tag) => tag.startsWith(\"ef-\"));\n\n await Promise.all(\n potentialTemporalTags.map((tag) =>\n customElements.whenDefined(tag).catch(() => {}),\n ),\n );\n\n const foundTemporal = this.findRootTemporal();\n if (foundTemporal) {\n this.targetTemporal = foundTemporal;\n // Wait for it to initialize\n await (foundTemporal as any).updateComplete;\n } else {\n console.warn(\"No temporal element found to play\");\n return;\n }\n }\n\n // If playbackController doesn't exist yet, wait for it\n if (!this.targetTemporal.playbackController) {\n await (this.targetTemporal as any).updateComplete;\n // After waiting, check again\n if (!this.targetTemporal.playbackController) {\n console.warn(\"PlaybackController not available for temporal element\");\n return;\n }\n }\n\n this.targetTemporal.playbackController.play();\n }\n\n pause() {\n if (this.targetTemporal?.playbackController) {\n this.targetTemporal.playbackController.pause();\n }\n }\n }\n\n return ContextElement as Constructor<ContextMixinInterface> & T;\n}\n"],"mappings":"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"}
|
package/dist/gui/Controllable.js
CHANGED
|
@@ -8,7 +8,80 @@ function isControllable(value) {
|
|
|
8
8
|
if (isEFTemporal(value)) return value.playbackController !== void 0;
|
|
9
9
|
return false;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Determines the type of controllable target for subscription purposes.
|
|
13
|
+
*
|
|
14
|
+
* - "context-provider": Target is a ContextMixin (like EFPreview) that provides contexts
|
|
15
|
+
* - "direct-temporal": Target is a root temporal element with its own playbackController
|
|
16
|
+
* - "none": Target is not controllable (null, undefined, or nested temporal)
|
|
17
|
+
*/
|
|
18
|
+
function determineTargetType(target) {
|
|
19
|
+
if (!target) return "none";
|
|
20
|
+
if (isContextMixin(target)) return "context-provider";
|
|
21
|
+
if (isEFTemporal(target)) {
|
|
22
|
+
if (target.playbackController) return "direct-temporal";
|
|
23
|
+
}
|
|
24
|
+
return "none";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Creates a subscription to a direct temporal element's playback controller.
|
|
28
|
+
* Used when EFControls targets a temporal element directly (not wrapped in EFPreview).
|
|
29
|
+
*/
|
|
30
|
+
function createDirectTemporalSubscription(target, callbacks) {
|
|
31
|
+
const controller = target.playbackController;
|
|
32
|
+
callbacks.onPlayingChange(controller.playing);
|
|
33
|
+
callbacks.onLoopChange(controller.loop);
|
|
34
|
+
callbacks.onCurrentTimeMsChange(controller.currentTimeMs);
|
|
35
|
+
callbacks.onDurationMsChange(target.durationMs);
|
|
36
|
+
callbacks.onTargetTemporalChange(target);
|
|
37
|
+
const listener = (event) => {
|
|
38
|
+
switch (event.property) {
|
|
39
|
+
case "playing":
|
|
40
|
+
callbacks.onPlayingChange(event.value);
|
|
41
|
+
break;
|
|
42
|
+
case "loop":
|
|
43
|
+
callbacks.onLoopChange(event.value);
|
|
44
|
+
break;
|
|
45
|
+
case "currentTimeMs":
|
|
46
|
+
callbacks.onCurrentTimeMsChange(event.value);
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
controller.addListener(listener);
|
|
51
|
+
const durationObserver = new MutationObserver(() => {
|
|
52
|
+
callbacks.onDurationMsChange(target.durationMs);
|
|
53
|
+
});
|
|
54
|
+
durationObserver.observe(target, {
|
|
55
|
+
attributes: true,
|
|
56
|
+
attributeFilter: [
|
|
57
|
+
"duration",
|
|
58
|
+
"trimstart",
|
|
59
|
+
"trimend",
|
|
60
|
+
"sourcein",
|
|
61
|
+
"sourceout"
|
|
62
|
+
],
|
|
63
|
+
subtree: true
|
|
64
|
+
});
|
|
65
|
+
let lastKnownDuration = target.durationMs;
|
|
66
|
+
let durationPollInterval = null;
|
|
67
|
+
if (lastKnownDuration === 0) durationPollInterval = setInterval(() => {
|
|
68
|
+
const currentDuration = target.durationMs;
|
|
69
|
+
if (currentDuration !== lastKnownDuration) {
|
|
70
|
+
lastKnownDuration = currentDuration;
|
|
71
|
+
callbacks.onDurationMsChange(currentDuration);
|
|
72
|
+
if (currentDuration > 0 && durationPollInterval) {
|
|
73
|
+
clearInterval(durationPollInterval);
|
|
74
|
+
durationPollInterval = null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}, 100);
|
|
78
|
+
return { unsubscribe: () => {
|
|
79
|
+
controller.removeListener(listener);
|
|
80
|
+
durationObserver.disconnect();
|
|
81
|
+
if (durationPollInterval) clearInterval(durationPollInterval);
|
|
82
|
+
} };
|
|
83
|
+
}
|
|
11
84
|
|
|
12
85
|
//#endregion
|
|
13
|
-
export { isControllable };
|
|
86
|
+
export { createDirectTemporalSubscription, determineTargetType, isControllable };
|
|
14
87
|
//# sourceMappingURL=Controllable.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Controllable.js","names":[],"sources":["../../src/gui/Controllable.ts"],"sourcesContent":["import type { LitElement } from \"lit\";\n\nimport {\n isEFTemporal,\n type TemporalMixinInterface,\n} from \"../elements/EFTemporal.js\";\nimport { type ContextMixinInterface, isContextMixin } from \"./ContextMixin.js\";\n\nexport declare class ControllableInterface extends LitElement {\n playing: boolean;\n loop: boolean;\n currentTimeMs: number;\n durationMs: number;\n play(): void | Promise<void>;\n pause(): void;\n}\n\nexport function isControllable(value: any): value is ControllableInterface {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n if (isContextMixin(value)) {\n return true;\n }\n\n if (isEFTemporal(value)) {\n const temporal = value as TemporalMixinInterface;\n return temporal.playbackController !== undefined;\n }\n\n return false;\n}\n\nexport type ControllableElement =\n | ContextMixinInterface\n | (TemporalMixinInterface & {\n playbackController: NonNullable<\n TemporalMixinInterface[\"playbackController\"]\n >;\n });\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"Controllable.js","names":["durationPollInterval: ReturnType<typeof setInterval> | null"],"sources":["../../src/gui/Controllable.ts"],"sourcesContent":["import type { LitElement } from \"lit\";\n\nimport {\n isEFTemporal,\n type TemporalMixinInterface,\n} from \"../elements/EFTemporal.js\";\nimport { type ContextMixinInterface, isContextMixin } from \"./ContextMixin.js\";\nimport type { PlaybackControllerUpdateEvent } from \"./PlaybackController.js\";\n\nexport declare class ControllableInterface extends LitElement {\n playing: boolean;\n loop: boolean;\n currentTimeMs: number;\n durationMs: number;\n play(): void | Promise<void>;\n pause(): void;\n}\n\nexport function isControllable(value: any): value is ControllableInterface {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n if (isContextMixin(value)) {\n return true;\n }\n\n if (isEFTemporal(value)) {\n const temporal = value as TemporalMixinInterface;\n return temporal.playbackController !== undefined;\n }\n\n return false;\n}\n\nexport type ControllableElement =\n | ContextMixinInterface\n | (TemporalMixinInterface & {\n playbackController: NonNullable<\n TemporalMixinInterface[\"playbackController\"]\n >;\n });\n\n// ============================================================================\n// Core Concept: Controllable Target Type\n// ============================================================================\n// A controllable target is either a context-providing wrapper (EFPreview)\n// OR a direct temporal element with its own playback controller.\n// This enumeration makes the mental model explicit.\n// ============================================================================\n\nexport type ControllableTargetType = \"context-provider\" | \"direct-temporal\" | \"none\";\n\n/**\n * Determines the type of controllable target for subscription purposes.\n * \n * - \"context-provider\": Target is a ContextMixin (like EFPreview) that provides contexts\n * - \"direct-temporal\": Target is a root temporal element with its own playbackController\n * - \"none\": Target is not controllable (null, undefined, or nested temporal)\n */\nexport function determineTargetType(target: unknown): ControllableTargetType {\n if (!target) return \"none\";\n \n if (isContextMixin(target)) {\n return \"context-provider\";\n }\n \n if (isEFTemporal(target)) {\n const temporal = target as TemporalMixinInterface;\n // Only root temporal elements have playbackController\n // Nested elements delegate to their root\n if (temporal.playbackController) {\n return \"direct-temporal\";\n }\n }\n \n return \"none\";\n}\n\n// ============================================================================\n// Subscription Interface\n// ============================================================================\n// Abstracts the mechanism of subscribing to playback state updates.\n// Different target types use different mechanisms (context vs direct listener).\n// ============================================================================\n\nexport interface SubscriptionCallbacks {\n onPlayingChange(value: boolean): void;\n onLoopChange(value: boolean): void;\n onCurrentTimeMsChange(value: number): void;\n onDurationMsChange(value: number): void;\n onTargetTemporalChange(value: TemporalMixinInterface | null): void;\n onFocusedElementChange?(value: HTMLElement | undefined): void;\n}\n\nexport interface ControllableSubscription {\n unsubscribe(): void;\n}\n\n/**\n * Creates a subscription to a direct temporal element's playback controller.\n * Used when EFControls targets a temporal element directly (not wrapped in EFPreview).\n */\nexport function createDirectTemporalSubscription(\n target: TemporalMixinInterface & HTMLElement,\n callbacks: SubscriptionCallbacks,\n): ControllableSubscription {\n const controller = target.playbackController!;\n \n // Initial sync - propagate current state immediately\n callbacks.onPlayingChange(controller.playing);\n callbacks.onLoopChange(controller.loop);\n callbacks.onCurrentTimeMsChange(controller.currentTimeMs);\n callbacks.onDurationMsChange(target.durationMs);\n callbacks.onTargetTemporalChange(target);\n \n // Subscribe to playback controller updates\n const listener = (event: PlaybackControllerUpdateEvent) => {\n switch (event.property) {\n case \"playing\":\n callbacks.onPlayingChange(event.value as boolean);\n break;\n case \"loop\":\n callbacks.onLoopChange(event.value as boolean);\n break;\n case \"currentTimeMs\":\n callbacks.onCurrentTimeMsChange(event.value as number);\n break;\n }\n };\n controller.addListener(listener);\n \n // Watch for duration changes via MutationObserver on duration-affecting attributes\n const durationObserver = new MutationObserver(() => {\n callbacks.onDurationMsChange(target.durationMs);\n });\n durationObserver.observe(target, {\n attributes: true,\n attributeFilter: [\"duration\", \"trimstart\", \"trimend\", \"sourcein\", \"sourceout\"],\n subtree: true,\n });\n \n // For media elements (ef-video, ef-audio), also watch for intrinsic duration changes\n // The intrinsicDurationMs comes from mediaEngineTask which loads asynchronously\n let lastKnownDuration = target.durationMs;\n let durationPollInterval: ReturnType<typeof setInterval> | null = null;\n \n // If duration is currently 0, poll until it becomes available\n // This handles the case where media hasn't loaded yet\n if (lastKnownDuration === 0) {\n durationPollInterval = setInterval(() => {\n const currentDuration = target.durationMs;\n if (currentDuration !== lastKnownDuration) {\n lastKnownDuration = currentDuration;\n callbacks.onDurationMsChange(currentDuration);\n // Once we have a non-zero duration, stop polling\n if (currentDuration > 0 && durationPollInterval) {\n clearInterval(durationPollInterval);\n durationPollInterval = null;\n }\n }\n }, 100); // Check every 100ms\n }\n \n return {\n unsubscribe: () => {\n controller.removeListener(listener);\n durationObserver.disconnect();\n if (durationPollInterval) {\n clearInterval(durationPollInterval);\n }\n },\n };\n}\n"],"mappings":";;;;AAkBA,SAAgB,eAAe,OAA4C;AACzE,KAAI,CAAC,SAAS,OAAO,UAAU,SAC7B,QAAO;AAGT,KAAI,eAAe,MAAM,CACvB,QAAO;AAGT,KAAI,aAAa,MAAM,CAErB,QADiB,MACD,uBAAuB;AAGzC,QAAO;;;;;;;;;AA4BT,SAAgB,oBAAoB,QAAyC;AAC3E,KAAI,CAAC,OAAQ,QAAO;AAEpB,KAAI,eAAe,OAAO,CACxB,QAAO;AAGT,KAAI,aAAa,OAAO,EAItB;MAHiB,OAGJ,mBACX,QAAO;;AAIX,QAAO;;;;;;AA2BT,SAAgB,iCACd,QACA,WAC0B;CAC1B,MAAM,aAAa,OAAO;AAG1B,WAAU,gBAAgB,WAAW,QAAQ;AAC7C,WAAU,aAAa,WAAW,KAAK;AACvC,WAAU,sBAAsB,WAAW,cAAc;AACzD,WAAU,mBAAmB,OAAO,WAAW;AAC/C,WAAU,uBAAuB,OAAO;CAGxC,MAAM,YAAY,UAAyC;AACzD,UAAQ,MAAM,UAAd;GACE,KAAK;AACH,cAAU,gBAAgB,MAAM,MAAiB;AACjD;GACF,KAAK;AACH,cAAU,aAAa,MAAM,MAAiB;AAC9C;GACF,KAAK;AACH,cAAU,sBAAsB,MAAM,MAAgB;AACtD;;;AAGN,YAAW,YAAY,SAAS;CAGhC,MAAM,mBAAmB,IAAI,uBAAuB;AAClD,YAAU,mBAAmB,OAAO,WAAW;GAC/C;AACF,kBAAiB,QAAQ,QAAQ;EAC/B,YAAY;EACZ,iBAAiB;GAAC;GAAY;GAAa;GAAW;GAAY;GAAY;EAC9E,SAAS;EACV,CAAC;CAIF,IAAI,oBAAoB,OAAO;CAC/B,IAAIA,uBAA8D;AAIlE,KAAI,sBAAsB,EACxB,wBAAuB,kBAAkB;EACvC,MAAM,kBAAkB,OAAO;AAC/B,MAAI,oBAAoB,mBAAmB;AACzC,uBAAoB;AACpB,aAAU,mBAAmB,gBAAgB;AAE7C,OAAI,kBAAkB,KAAK,sBAAsB;AAC/C,kBAAc,qBAAqB;AACnC,2BAAuB;;;IAG1B,IAAI;AAGT,QAAO,EACL,mBAAmB;AACjB,aAAW,eAAe,SAAS;AACnC,mBAAiB,YAAY;AAC7B,MAAI,qBACF,eAAc,qBAAqB;IAGxC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as lit24 from "lit";
|
|
2
|
+
import { LitElement } from "lit";
|
|
3
|
+
import * as lit_html24 from "lit-html";
|
|
4
|
+
|
|
5
|
+
//#region src/gui/EFActiveRootTemporal.d.ts
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Displays the ID of the active root temporal element from a canvas.
|
|
9
|
+
* Automatically updates when selection changes.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```html
|
|
13
|
+
* <ef-active-root-temporal canvas="canvas"></ef-active-root-temporal>
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
declare class EFActiveRootTemporal extends LitElement {
|
|
17
|
+
static styles: lit24.CSSResult;
|
|
18
|
+
/**
|
|
19
|
+
* Canvas element ID or selector to bind to.
|
|
20
|
+
* If not specified, will search for the nearest ef-canvas ancestor.
|
|
21
|
+
*/
|
|
22
|
+
canvas: string;
|
|
23
|
+
private activeRootTemporal;
|
|
24
|
+
private canvasElement;
|
|
25
|
+
private activeroottemporalchangeHandler?;
|
|
26
|
+
connectedCallback(): void;
|
|
27
|
+
disconnectedCallback(): void;
|
|
28
|
+
protected updated(changedProperties: Map<string | number | symbol, unknown>): void;
|
|
29
|
+
/**
|
|
30
|
+
* Find the canvas element to bind to.
|
|
31
|
+
*/
|
|
32
|
+
private findCanvas;
|
|
33
|
+
/**
|
|
34
|
+
* Setup listener for activeroottemporalchange events.
|
|
35
|
+
*/
|
|
36
|
+
private setupListener;
|
|
37
|
+
/**
|
|
38
|
+
* Remove event listener.
|
|
39
|
+
*/
|
|
40
|
+
private removeListener;
|
|
41
|
+
render(): lit_html24.TemplateResult<1>;
|
|
42
|
+
}
|
|
43
|
+
declare global {
|
|
44
|
+
interface HTMLElementTagNameMap {
|
|
45
|
+
"ef-active-root-temporal": EFActiveRootTemporal;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { EFActiveRootTemporal };
|
|
50
|
+
//# sourceMappingURL=EFActiveRootTemporal.d.ts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js";
|
|
2
|
+
import { LitElement, css, html } from "lit";
|
|
3
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
4
|
+
|
|
5
|
+
//#region src/gui/EFActiveRootTemporal.ts
|
|
6
|
+
let EFActiveRootTemporal = class EFActiveRootTemporal$1 extends LitElement {
|
|
7
|
+
constructor(..._args) {
|
|
8
|
+
super(..._args);
|
|
9
|
+
this.canvas = "";
|
|
10
|
+
this.activeRootTemporal = null;
|
|
11
|
+
this.canvasElement = null;
|
|
12
|
+
}
|
|
13
|
+
static {
|
|
14
|
+
this.styles = css`
|
|
15
|
+
:host {
|
|
16
|
+
display: inline-block;
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
}
|
|
20
|
+
connectedCallback() {
|
|
21
|
+
super.connectedCallback();
|
|
22
|
+
this.findCanvas();
|
|
23
|
+
this.setupListener();
|
|
24
|
+
}
|
|
25
|
+
disconnectedCallback() {
|
|
26
|
+
super.disconnectedCallback();
|
|
27
|
+
this.removeListener();
|
|
28
|
+
}
|
|
29
|
+
updated(changedProperties) {
|
|
30
|
+
if (changedProperties.has("canvas")) {
|
|
31
|
+
this.findCanvas();
|
|
32
|
+
this.setupListener();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Find the canvas element to bind to.
|
|
37
|
+
*/
|
|
38
|
+
findCanvas() {
|
|
39
|
+
if (this.canvas) {
|
|
40
|
+
const byId = document.getElementById(this.canvas);
|
|
41
|
+
if (byId && byId.tagName === "EF-CANVAS") {
|
|
42
|
+
this.canvasElement = byId;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const bySelector = document.querySelector(this.canvas);
|
|
47
|
+
if (bySelector && bySelector.tagName === "EF-CANVAS") {
|
|
48
|
+
this.canvasElement = bySelector;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
} catch {}
|
|
52
|
+
}
|
|
53
|
+
const ancestor = this.closest("ef-canvas");
|
|
54
|
+
if (ancestor) {
|
|
55
|
+
this.canvasElement = ancestor;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.canvasElement = null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Setup listener for activeroottemporalchange events.
|
|
62
|
+
*/
|
|
63
|
+
setupListener() {
|
|
64
|
+
this.removeListener();
|
|
65
|
+
if (!this.canvasElement) {
|
|
66
|
+
this.activeRootTemporal = null;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
this.activeRootTemporal = this.canvasElement.activeRootTemporal || null;
|
|
70
|
+
this.activeroottemporalchangeHandler = () => {
|
|
71
|
+
this.activeRootTemporal = this.canvasElement.activeRootTemporal || null;
|
|
72
|
+
};
|
|
73
|
+
this.canvasElement.addEventListener("activeroottemporalchange", this.activeroottemporalchangeHandler);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Remove event listener.
|
|
77
|
+
*/
|
|
78
|
+
removeListener() {
|
|
79
|
+
if (this.canvasElement && this.activeroottemporalchangeHandler) {
|
|
80
|
+
this.canvasElement.removeEventListener("activeroottemporalchange", this.activeroottemporalchangeHandler);
|
|
81
|
+
this.activeroottemporalchangeHandler = void 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
render() {
|
|
85
|
+
return html`<span>${this.activeRootTemporal?.id || this.textContent || "None"}</span>`;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
__decorate([property({ type: String })], EFActiveRootTemporal.prototype, "canvas", void 0);
|
|
89
|
+
__decorate([state()], EFActiveRootTemporal.prototype, "activeRootTemporal", void 0);
|
|
90
|
+
EFActiveRootTemporal = __decorate([customElement("ef-active-root-temporal")], EFActiveRootTemporal);
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
export { EFActiveRootTemporal };
|
|
94
|
+
//# sourceMappingURL=EFActiveRootTemporal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EFActiveRootTemporal.js","names":["EFActiveRootTemporal"],"sources":["../../src/gui/EFActiveRootTemporal.ts"],"sourcesContent":["import { css, html, LitElement } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport type { EFCanvas } from \"../canvas/EFCanvas.js\";\nimport type { TemporalMixinInterface } from \"../elements/EFTemporal.js\";\n\n/**\n * Displays the ID of the active root temporal element from a canvas.\n * Automatically updates when selection changes.\n * \n * @example\n * ```html\n * <ef-active-root-temporal canvas=\"canvas\"></ef-active-root-temporal>\n * ```\n */\n@customElement(\"ef-active-root-temporal\")\nexport class EFActiveRootTemporal extends LitElement {\n static styles = css`\n :host {\n display: inline-block;\n }\n `;\n\n /**\n * Canvas element ID or selector to bind to.\n * If not specified, will search for the nearest ef-canvas ancestor.\n */\n @property({ type: String })\n canvas = \"\";\n\n @state()\n private activeRootTemporal: (TemporalMixinInterface & HTMLElement) | null =\n null;\n\n private canvasElement: EFCanvas | null = null;\n private activeroottemporalchangeHandler?: () => void;\n\n connectedCallback(): void {\n super.connectedCallback();\n this.findCanvas();\n this.setupListener();\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeListener();\n }\n\n protected updated(changedProperties: Map<string | number | symbol, unknown>): void {\n if (changedProperties.has(\"canvas\")) {\n this.findCanvas();\n this.setupListener();\n }\n }\n\n /**\n * Find the canvas element to bind to.\n */\n private findCanvas(): void {\n // If canvas attribute is set, use it\n if (this.canvas) {\n const byId = document.getElementById(this.canvas);\n if (byId && byId.tagName === \"EF-CANVAS\") {\n this.canvasElement = byId as EFCanvas;\n return;\n }\n\n // Try as selector\n try {\n const bySelector = document.querySelector(this.canvas) as EFCanvas | null;\n if (bySelector && bySelector.tagName === \"EF-CANVAS\") {\n this.canvasElement = bySelector;\n return;\n }\n } catch {\n // Invalid selector, ignore\n }\n }\n\n // Fall back to nearest ancestor\n const ancestor = this.closest(\"ef-canvas\") as EFCanvas | null;\n if (ancestor) {\n this.canvasElement = ancestor;\n return;\n }\n\n this.canvasElement = null;\n }\n\n /**\n * Setup listener for activeroottemporalchange events.\n */\n private setupListener(): void {\n this.removeListener();\n\n if (!this.canvasElement) {\n this.activeRootTemporal = null;\n return;\n }\n\n // Get initial value\n const canvasEl = this.canvasElement as any;\n this.activeRootTemporal = canvasEl.activeRootTemporal || null;\n\n // Listen for changes\n this.activeroottemporalchangeHandler = () => {\n const canvasEl = this.canvasElement as any;\n this.activeRootTemporal = canvasEl.activeRootTemporal || null;\n };\n\n this.canvasElement.addEventListener(\n \"activeroottemporalchange\",\n this.activeroottemporalchangeHandler,\n );\n }\n\n /**\n * Remove event listener.\n */\n private removeListener(): void {\n if (this.canvasElement && this.activeroottemporalchangeHandler) {\n this.canvasElement.removeEventListener(\n \"activeroottemporalchange\",\n this.activeroottemporalchangeHandler,\n );\n this.activeroottemporalchangeHandler = undefined;\n }\n }\n\n render() {\n const displayText =\n this.activeRootTemporal?.id || this.textContent || \"None\";\n return html`<span>${displayText}</span>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-active-root-temporal\": EFActiveRootTemporal;\n }\n}\n\n"],"mappings":";;;;;AAeO,iCAAMA,+BAA6B,WAAW;;;gBAY1C;4BAIP;uBAEuC;;;gBAjBzB,GAAG;;;;;;CAoBnB,oBAA0B;AACxB,QAAM,mBAAmB;AACzB,OAAK,YAAY;AACjB,OAAK,eAAe;;CAGtB,uBAA6B;AAC3B,QAAM,sBAAsB;AAC5B,OAAK,gBAAgB;;CAGvB,AAAU,QAAQ,mBAAiE;AACjF,MAAI,kBAAkB,IAAI,SAAS,EAAE;AACnC,QAAK,YAAY;AACjB,QAAK,eAAe;;;;;;CAOxB,AAAQ,aAAmB;AAEzB,MAAI,KAAK,QAAQ;GACf,MAAM,OAAO,SAAS,eAAe,KAAK,OAAO;AACjD,OAAI,QAAQ,KAAK,YAAY,aAAa;AACxC,SAAK,gBAAgB;AACrB;;AAIF,OAAI;IACF,MAAM,aAAa,SAAS,cAAc,KAAK,OAAO;AACtD,QAAI,cAAc,WAAW,YAAY,aAAa;AACpD,UAAK,gBAAgB;AACrB;;WAEI;;EAMV,MAAM,WAAW,KAAK,QAAQ,YAAY;AAC1C,MAAI,UAAU;AACZ,QAAK,gBAAgB;AACrB;;AAGF,OAAK,gBAAgB;;;;;CAMvB,AAAQ,gBAAsB;AAC5B,OAAK,gBAAgB;AAErB,MAAI,CAAC,KAAK,eAAe;AACvB,QAAK,qBAAqB;AAC1B;;AAKF,OAAK,qBADY,KAAK,cACa,sBAAsB;AAGzD,OAAK,wCAAwC;AAE3C,QAAK,qBADY,KAAK,cACa,sBAAsB;;AAG3D,OAAK,cAAc,iBACjB,4BACA,KAAK,gCACN;;;;;CAMH,AAAQ,iBAAuB;AAC7B,MAAI,KAAK,iBAAiB,KAAK,iCAAiC;AAC9D,QAAK,cAAc,oBACjB,4BACA,KAAK,gCACN;AACD,QAAK,kCAAkC;;;CAI3C,SAAS;AAGP,SAAO,IAAI,SADT,KAAK,oBAAoB,MAAM,KAAK,eAAe,OACrB;;;YAzGjC,SAAS,EAAE,MAAM,QAAQ,CAAC;YAG1B,OAAO;mCAfT,cAAc,0BAA0B"}
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as lit11 from "lit";
|
|
2
2
|
import { LitElement } from "lit";
|
|
3
|
-
import * as
|
|
3
|
+
import * as lit_html11 from "lit-html";
|
|
4
4
|
|
|
5
5
|
//#region src/gui/EFConfiguration.d.ts
|
|
6
6
|
declare class EFConfiguration extends LitElement {
|
|
7
|
-
static styles:
|
|
7
|
+
static styles: lit11.CSSResult[];
|
|
8
8
|
efConfiguration: this;
|
|
9
9
|
apiHost?: string;
|
|
10
10
|
signingURL: string;
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Media engine selection:
|
|
13
|
+
* - "cloud": Use JitMediaEngine for http/https URLs, AssetMediaEngine for local paths (default)
|
|
14
|
+
* - "local": Force AssetMediaEngine for all sources (uses @ef-* URLs)
|
|
15
|
+
* - "jit": Force JitMediaEngine for all sources (uses /api/v1/transcode/* URLs)
|
|
16
|
+
*/
|
|
17
|
+
mediaEngine?: "cloud" | "local" | "jit";
|
|
18
|
+
render(): lit_html11.TemplateResult<1>;
|
|
13
19
|
}
|
|
14
20
|
declare global {
|
|
15
21
|
interface HTMLElementTagNameMap {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EFConfiguration.js","names":["EFConfiguration"],"sources":["../../src/gui/EFConfiguration.ts"],"sourcesContent":["import { createContext, provide } from \"@lit/context\";\nimport { css, html, LitElement } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\n\nexport const efConfigurationContext = createContext<EFConfiguration | null>(\n Symbol(\"efConfigurationContext\"),\n);\n\n@customElement(\"ef-configuration\")\nexport class EFConfiguration extends LitElement {\n static styles = [\n css`\n :host {\n display: contents;\n }\n `,\n ];\n\n @provide({ context: efConfigurationContext })\n efConfiguration = this;\n\n @property({ type: String, attribute: \"api-host\" })\n apiHost?: string;\n\n @property({ type: String, attribute: \"signing-url\" })\n signingURL = \"/@ef-sign-url\";\n\n @property({ type: String, attribute: \"media-engine\" })\n mediaEngine?: \"cloud\" | \"local\" = \"cloud\";\n\n render() {\n return html`<slot></slot>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-configuration\": EFConfiguration;\n }\n}\n"],"mappings":";;;;;;AAIA,MAAa,yBAAyB,cACpC,OAAO,yBAAyB,CACjC;AAGM,4BAAMA,0BAAwB,WAAW;;;yBAU5B;oBAML;
|
|
1
|
+
{"version":3,"file":"EFConfiguration.js","names":["EFConfiguration"],"sources":["../../src/gui/EFConfiguration.ts"],"sourcesContent":["import { createContext, provide } from \"@lit/context\";\nimport { css, html, LitElement } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\n\nexport const efConfigurationContext = createContext<EFConfiguration | null>(\n Symbol(\"efConfigurationContext\"),\n);\n\n@customElement(\"ef-configuration\")\nexport class EFConfiguration extends LitElement {\n static styles = [\n css`\n :host {\n display: contents;\n }\n `,\n ];\n\n @provide({ context: efConfigurationContext })\n efConfiguration = this;\n\n @property({ type: String, attribute: \"api-host\" })\n apiHost?: string;\n\n @property({ type: String, attribute: \"signing-url\" })\n signingURL = \"/@ef-sign-url\";\n\n /**\n * Media engine selection:\n * - \"cloud\": Use JitMediaEngine for http/https URLs, AssetMediaEngine for local paths (default)\n * - \"local\": Force AssetMediaEngine for all sources (uses @ef-* URLs)\n * - \"jit\": Force JitMediaEngine for all sources (uses /api/v1/transcode/* URLs)\n */\n @property({ type: String, attribute: \"media-engine\" })\n mediaEngine?: \"cloud\" | \"local\" | \"jit\" = \"cloud\";\n\n render() {\n return html`<slot></slot>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-configuration\": EFConfiguration;\n }\n}\n"],"mappings":";;;;;;AAIA,MAAa,yBAAyB,cACpC,OAAO,yBAAyB,CACjC;AAGM,4BAAMA,0BAAwB,WAAW;;;yBAU5B;oBAML;qBAS6B;;;gBAxB1B,CACd,GAAG;;;;MAKJ;;CAoBD,SAAS;AACP,SAAO,IAAI;;;YAnBZ,QAAQ,EAAE,SAAS,wBAAwB,CAAC;YAG5C,SAAS;CAAE,MAAM;CAAQ,WAAW;CAAY,CAAC;YAGjD,SAAS;CAAE,MAAM;CAAQ,WAAW;CAAe,CAAC;YASpD,SAAS;CAAE,MAAM;CAAQ,WAAW;CAAgB,CAAC;8BAzBvD,cAAc,mBAAmB"}
|
package/dist/gui/EFControls.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TemporalMixinInterface } from "../elements/EFTemporal.js";
|
|
2
2
|
import { ControllableInterface } from "./Controllable.js";
|
|
3
3
|
import { FocusContext } from "./focusContext.js";
|
|
4
|
-
import * as
|
|
4
|
+
import * as lit26 from "lit";
|
|
5
5
|
import { LitElement, PropertyValueMap } from "lit";
|
|
6
6
|
|
|
7
7
|
//#region src/gui/EFControls.d.ts
|
|
@@ -26,7 +26,7 @@ import { LitElement, PropertyValueMap } from "lit";
|
|
|
26
26
|
*/
|
|
27
27
|
declare class EFControls extends LitElement {
|
|
28
28
|
#private;
|
|
29
|
-
static styles:
|
|
29
|
+
static styles: lit26.CSSResult;
|
|
30
30
|
createRenderRoot(): this;
|
|
31
31
|
/**
|
|
32
32
|
* The ID of the ef-preview element to control
|
package/dist/gui/EFControls.js
CHANGED
|
@@ -2,11 +2,13 @@ import { currentTimeContext } from "./currentTimeContext.js";
|
|
|
2
2
|
import { durationContext } from "./durationContext.js";
|
|
3
3
|
import { loopContext, playingContext } from "./playingContext.js";
|
|
4
4
|
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js";
|
|
5
|
+
import { isEFTemporal } from "../elements/EFTemporal.js";
|
|
5
6
|
import { efContext } from "./efContext.js";
|
|
6
7
|
import { focusContext } from "./focusContext.js";
|
|
7
8
|
import { focusedElementContext } from "./focusedElementContext.js";
|
|
8
9
|
import { targetTemporalContext } from "./ContextMixin.js";
|
|
9
10
|
import { TargetController } from "../elements/TargetController.js";
|
|
11
|
+
import { createDirectTemporalSubscription, determineTargetType } from "./Controllable.js";
|
|
10
12
|
import { attachContextRoot } from "../attachContextRoot.js";
|
|
11
13
|
import { createContext, provide } from "@lit/context";
|
|
12
14
|
import { LitElement, css } from "lit";
|
|
@@ -59,23 +61,117 @@ let EFControls = class EFControls$1 extends LitElement {
|
|
|
59
61
|
return this;
|
|
60
62
|
}
|
|
61
63
|
#targetController = new TargetController(this);
|
|
62
|
-
#
|
|
64
|
+
#contextUnsubscribeMap = /* @__PURE__ */ new Map();
|
|
65
|
+
#directTemporalSubscription = null;
|
|
63
66
|
#unsubscribe() {
|
|
64
|
-
|
|
65
|
-
this.#
|
|
67
|
+
this.#subscribeAbortController?.abort();
|
|
68
|
+
this.#subscribeAbortController = null;
|
|
69
|
+
for (const unsubscribe of this.#contextUnsubscribeMap.values()) unsubscribe();
|
|
70
|
+
this.#contextUnsubscribeMap.clear();
|
|
71
|
+
if (this.#directTemporalSubscription) {
|
|
72
|
+
this.#directTemporalSubscription.unsubscribe();
|
|
73
|
+
this.#directTemporalSubscription = null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
#subscribeAbortController = null;
|
|
77
|
+
#subscribe() {
|
|
78
|
+
if (!this.targetElement) return;
|
|
79
|
+
this.#subscribeAbortController?.abort();
|
|
80
|
+
this.#subscribeAbortController = new AbortController();
|
|
81
|
+
switch (determineTargetType(this.targetElement)) {
|
|
82
|
+
case "context-provider":
|
|
83
|
+
this.#subscribeToContextProvider();
|
|
84
|
+
break;
|
|
85
|
+
case "direct-temporal":
|
|
86
|
+
this.#subscribeToDirectTemporal();
|
|
87
|
+
break;
|
|
88
|
+
case "none":
|
|
89
|
+
this.#waitForTemporalToInitialize();
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Wait for a temporal element to initialize its playbackController.
|
|
95
|
+
* This handles the case where we target a temporal element before it has
|
|
96
|
+
* completed initialization and created its playbackController.
|
|
97
|
+
*
|
|
98
|
+
* Note: playbackController is created in EFTemporal's connectedCallback via
|
|
99
|
+
* `this.updateComplete.then(() => this.didBecomeRoot())`, so we need to wait
|
|
100
|
+
* past updateComplete for the .then() callback to run.
|
|
101
|
+
*/
|
|
102
|
+
async #waitForTemporalToInitialize() {
|
|
103
|
+
if (!this.targetElement || !isEFTemporal(this.targetElement)) return;
|
|
104
|
+
const temporal = this.targetElement;
|
|
105
|
+
const signal = this.#subscribeAbortController?.signal;
|
|
106
|
+
await temporal.updateComplete;
|
|
107
|
+
if (signal?.aborted) return;
|
|
108
|
+
if (!temporal.playbackController) {
|
|
109
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
110
|
+
if (signal?.aborted) return;
|
|
111
|
+
await temporal.updateComplete;
|
|
112
|
+
if (signal?.aborted) return;
|
|
113
|
+
}
|
|
114
|
+
if (temporal.playbackController) this.#subscribeToDirectTemporal();
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Subscribe to a context-providing target (like EFPreview).
|
|
118
|
+
* Uses Lit Context dispatch mechanism.
|
|
119
|
+
*/
|
|
120
|
+
#subscribeToContextProvider() {
|
|
121
|
+
if (!this.targetElement) return;
|
|
122
|
+
for (const [callback, context] of proxiedContexts) {
|
|
123
|
+
const event = new ContextRequestEvent(context, this, (value, unsubscribe) => {
|
|
124
|
+
callback(this, value);
|
|
125
|
+
this.#contextUnsubscribeMap.set(context, unsubscribe);
|
|
126
|
+
}, true);
|
|
127
|
+
this.targetElement.dispatchEvent(event);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Subscribe to a direct temporal element's playback controller.
|
|
132
|
+
* Used when targeting ef-timegroup, ef-video, ef-audio directly without ef-preview wrapper.
|
|
133
|
+
*/
|
|
134
|
+
async #subscribeToDirectTemporal() {
|
|
135
|
+
if (!this.targetElement || !isEFTemporal(this.targetElement)) return;
|
|
136
|
+
const temporal = this.targetElement;
|
|
137
|
+
const signal = this.#subscribeAbortController?.signal;
|
|
138
|
+
await temporal.updateComplete;
|
|
139
|
+
if (signal?.aborted) return;
|
|
140
|
+
if (!temporal.playbackController) {
|
|
141
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
142
|
+
if (signal?.aborted) return;
|
|
143
|
+
await temporal.updateComplete;
|
|
144
|
+
if (signal?.aborted) return;
|
|
145
|
+
}
|
|
146
|
+
if (!temporal.playbackController) return;
|
|
147
|
+
this.#directTemporalSubscription = createDirectTemporalSubscription(temporal, {
|
|
148
|
+
onPlayingChange: (value) => {
|
|
149
|
+
this.playing = value;
|
|
150
|
+
},
|
|
151
|
+
onLoopChange: (value) => {
|
|
152
|
+
this.loop = value;
|
|
153
|
+
},
|
|
154
|
+
onCurrentTimeMsChange: (value) => {
|
|
155
|
+
this.currentTimeMs = value;
|
|
156
|
+
},
|
|
157
|
+
onDurationMsChange: (value) => {
|
|
158
|
+
this.durationMs = value;
|
|
159
|
+
},
|
|
160
|
+
onTargetTemporalChange: (value) => {
|
|
161
|
+
this.targetTemporal = value;
|
|
162
|
+
},
|
|
163
|
+
onFocusedElementChange: (value) => {
|
|
164
|
+
this.focusedElement = value;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
#resubscribe() {
|
|
169
|
+
this.#unsubscribe();
|
|
170
|
+
this.#subscribe();
|
|
66
171
|
}
|
|
67
172
|
updated(changedProperties) {
|
|
68
173
|
super.updated(changedProperties);
|
|
69
|
-
if (changedProperties.has("targetElement"))
|
|
70
|
-
this.#unsubscribe();
|
|
71
|
-
if (this.targetElement) for (const [callback, context] of proxiedContexts) {
|
|
72
|
-
const event = new ContextRequestEvent(context, this, (value, unsubscribe) => {
|
|
73
|
-
callback(this, value);
|
|
74
|
-
this.#proxyUnsubscribeMap.set(context, unsubscribe);
|
|
75
|
-
}, true);
|
|
76
|
-
this.targetElement.dispatchEvent(event);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
174
|
+
if (changedProperties.has("targetElement")) this.#resubscribe();
|
|
79
175
|
}
|
|
80
176
|
disconnectedCallback() {
|
|
81
177
|
super.disconnectedCallback();
|