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