@editframe/elements 0.37.3-beta → 0.38.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.js +17 -14
- package/dist/EF_FRAMEGEN.js.map +1 -1
- package/dist/EF_RENDERING.js.map +1 -1
- package/dist/canvas/EFCanvas.d.ts +9 -2
- package/dist/canvas/EFCanvas.js +14 -4
- package/dist/canvas/EFCanvas.js.map +1 -1
- package/dist/canvas/EFCanvasItem.d.ts +4 -4
- package/dist/canvas/overlays/SelectionOverlay.d.ts +10 -2
- package/dist/canvas/overlays/SelectionOverlay.js +5 -12
- package/dist/canvas/overlays/SelectionOverlay.js.map +1 -1
- package/dist/canvas/overlays/overlayState.js.map +1 -1
- package/dist/canvas/selection/SelectionController.js.map +1 -1
- package/dist/elements/EFAudio.d.ts +1 -11
- package/dist/elements/EFAudio.js +2 -10
- package/dist/elements/EFAudio.js.map +1 -1
- package/dist/elements/EFCaptions.d.ts +5 -9
- package/dist/elements/EFCaptions.js +34 -11
- package/dist/elements/EFCaptions.js.map +1 -1
- package/dist/elements/EFImage.d.ts +10 -8
- package/dist/elements/EFImage.js +117 -32
- package/dist/elements/EFImage.js.map +1 -1
- package/dist/elements/EFMedia/AssetMediaEngine.js +2 -2
- package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/BaseMediaEngine.js +15 -92
- package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/BufferedSeekingInput.js +10 -11
- package/dist/elements/EFMedia/BufferedSeekingInput.js.map +1 -1
- package/dist/elements/EFMedia/{AssetIdMediaEngine.js → FileMediaEngine.js} +44 -24
- package/dist/elements/EFMedia/FileMediaEngine.js.map +1 -0
- package/dist/elements/EFMedia/JitMediaEngine.js +14 -13
- package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -3
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +12 -7
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
- package/dist/elements/EFMedia/shared/timeoutUtils.js +44 -0
- package/dist/elements/EFMedia/shared/timeoutUtils.js.map +1 -0
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +1 -1
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +4 -4
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
- package/dist/elements/EFMedia.d.ts +14 -8
- package/dist/elements/EFMedia.js +52 -19
- package/dist/elements/EFMedia.js.map +1 -1
- package/dist/elements/EFPanZoom.d.ts +2 -2
- package/dist/elements/EFPanZoom.js +1 -1
- package/dist/elements/EFPanZoom.js.map +1 -1
- package/dist/elements/EFSourceMixin.js +16 -8
- package/dist/elements/EFSourceMixin.js.map +1 -1
- package/dist/elements/EFSurface.d.ts +7 -10
- package/dist/elements/EFSurface.js +4 -43
- package/dist/elements/EFSurface.js.map +1 -1
- package/dist/elements/EFTemporal.d.ts +33 -8
- package/dist/elements/EFTemporal.js +92 -40
- package/dist/elements/EFTemporal.js.map +1 -1
- package/dist/elements/EFText.d.ts +3 -0
- package/dist/elements/EFText.js +54 -21
- package/dist/elements/EFText.js.map +1 -1
- package/dist/elements/EFTextSegment.js +8 -4
- package/dist/elements/EFTextSegment.js.map +1 -1
- package/dist/elements/EFTimegroup.d.ts +26 -43
- package/dist/elements/EFTimegroup.js +295 -314
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/dist/elements/EFVideo.d.ts +44 -42
- package/dist/elements/EFVideo.js +259 -172
- package/dist/elements/EFVideo.js.map +1 -1
- package/dist/elements/EFWaveform.d.ts +3 -8
- package/dist/elements/EFWaveform.js +18 -13
- package/dist/elements/EFWaveform.js.map +1 -1
- package/dist/elements/ElementPositionInfo.js.map +1 -1
- package/dist/elements/FetchMixin.js.map +1 -1
- package/dist/elements/TargetController.d.ts +0 -3
- package/dist/elements/TargetController.js +12 -35
- package/dist/elements/TargetController.js.map +1 -1
- package/dist/elements/TimegroupController.js.map +1 -1
- package/dist/elements/cloneFactoryRegistry.d.ts +14 -0
- package/dist/elements/cloneFactoryRegistry.js +15 -0
- package/dist/elements/cloneFactoryRegistry.js.map +1 -0
- package/dist/elements/renderTemporalAudio.js +8 -6
- package/dist/elements/renderTemporalAudio.js.map +1 -1
- package/dist/elements/setupTemporalHierarchy.js +62 -0
- package/dist/elements/setupTemporalHierarchy.js.map +1 -0
- package/dist/elements/updateAnimations.js +62 -87
- package/dist/elements/updateAnimations.js.map +1 -1
- package/dist/getRenderInfo.d.ts +3 -2
- package/dist/getRenderInfo.js +20 -4
- package/dist/getRenderInfo.js.map +1 -1
- package/dist/gui/ContextMixin.js +68 -12
- package/dist/gui/ContextMixin.js.map +1 -1
- package/dist/gui/Controllable.js +1 -1
- package/dist/gui/Controllable.js.map +1 -1
- package/dist/gui/EFActiveRootTemporal.d.ts +4 -4
- package/dist/gui/EFActiveRootTemporal.js.map +1 -1
- package/dist/gui/EFControls.d.ts +2 -2
- package/dist/gui/EFControls.js +2 -2
- package/dist/gui/EFControls.js.map +1 -1
- package/dist/gui/EFDial.d.ts +4 -4
- package/dist/gui/EFDial.js +12 -9
- package/dist/gui/EFDial.js.map +1 -1
- package/dist/gui/EFFilmstrip.d.ts +2 -0
- package/dist/gui/EFFilmstrip.js +18 -10
- package/dist/gui/EFFilmstrip.js.map +1 -1
- package/dist/gui/EFFitScale.d.ts +28 -4
- package/dist/gui/EFFitScale.js +88 -26
- package/dist/gui/EFFitScale.js.map +1 -1
- package/dist/gui/EFFocusOverlay.d.ts +4 -4
- package/dist/gui/EFFocusOverlay.js +3 -3
- package/dist/gui/EFFocusOverlay.js.map +1 -1
- package/dist/gui/EFOverlayItem.d.ts +4 -4
- package/dist/gui/EFOverlayLayer.d.ts +4 -4
- package/dist/gui/EFPause.d.ts +4 -4
- package/dist/gui/EFPause.js +1 -1
- package/dist/gui/EFPlay.d.ts +4 -4
- package/dist/gui/EFPlay.js +1 -1
- package/dist/gui/EFPreview.js +1 -1
- package/dist/gui/EFResizableBox.d.ts +4 -4
- package/dist/gui/EFResizableBox.js +5 -5
- package/dist/gui/EFResizableBox.js.map +1 -1
- package/dist/gui/EFScrubber.d.ts +4 -4
- package/dist/gui/EFScrubber.js +8 -13
- package/dist/gui/EFScrubber.js.map +1 -1
- package/dist/gui/EFTimeDisplay.d.ts +8 -4
- package/dist/gui/EFTimeDisplay.js +25 -7
- package/dist/gui/EFTimeDisplay.js.map +1 -1
- package/dist/gui/EFTimelineRuler.d.ts +4 -4
- package/dist/gui/EFTimelineRuler.js +3 -3
- package/dist/gui/EFTimelineRuler.js.map +1 -1
- package/dist/gui/EFToggleLoop.d.ts +4 -4
- package/dist/gui/EFToggleLoop.js +1 -1
- package/dist/gui/EFTogglePlay.d.ts +4 -4
- package/dist/gui/EFTogglePlay.js +1 -1
- package/dist/gui/EFTransformHandles.d.ts +4 -4
- package/dist/gui/EFTransformHandles.js +6 -6
- package/dist/gui/EFTransformHandles.js.map +1 -1
- package/dist/gui/EFWorkbench.d.ts +40 -36
- package/dist/gui/EFWorkbench.js +436 -822
- package/dist/gui/EFWorkbench.js.map +1 -1
- package/dist/gui/FitScaleHelpers.js.map +1 -1
- package/dist/gui/PlaybackController.d.ts +3 -8
- package/dist/gui/PlaybackController.js +59 -56
- 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/TargetOrContextMixin.js +43 -6
- package/dist/gui/TargetOrContextMixin.js.map +1 -1
- package/dist/gui/ef-theme.css +136 -0
- package/dist/gui/hierarchy/EFHierarchy.d.ts +2 -2
- package/dist/gui/hierarchy/EFHierarchy.js +14 -24
- package/dist/gui/hierarchy/EFHierarchy.js.map +1 -1
- package/dist/gui/hierarchy/EFHierarchyItem.d.ts +3 -3
- package/dist/gui/hierarchy/EFHierarchyItem.js +22 -10
- package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -1
- package/dist/gui/icons.js.map +1 -1
- package/dist/gui/previewSettingsContext.d.ts +18 -0
- package/dist/gui/previewSettingsContext.js.map +1 -1
- package/dist/gui/theme.js +34 -0
- package/dist/gui/theme.js.map +1 -0
- package/dist/gui/timeline/EFTimeline.d.ts +2 -2
- package/dist/gui/timeline/EFTimeline.js +70 -52
- package/dist/gui/timeline/EFTimeline.js.map +1 -1
- package/dist/gui/timeline/EFTimelineRow.d.ts +3 -1
- package/dist/gui/timeline/EFTimelineRow.js +55 -32
- package/dist/gui/timeline/EFTimelineRow.js.map +1 -1
- package/dist/gui/timeline/TrimHandles.d.ts +23 -9
- package/dist/gui/timeline/TrimHandles.js +224 -51
- package/dist/gui/timeline/TrimHandles.js.map +1 -1
- package/dist/gui/timeline/flattenHierarchy.js.map +1 -1
- package/dist/gui/timeline/timelineEditingContext.d.ts +34 -0
- package/dist/gui/timeline/timelineEditingContext.js +24 -0
- package/dist/gui/timeline/timelineEditingContext.js.map +1 -0
- package/dist/gui/timeline/timelineStateContext.js.map +1 -1
- package/dist/gui/timeline/tracks/AudioTrack.js +1 -1
- package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/CaptionsTrack.d.ts +2 -3
- package/dist/gui/timeline/tracks/CaptionsTrack.js +17 -75
- package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/EFThumbnailStrip.d.ts +52 -0
- package/dist/gui/timeline/tracks/EFThumbnailStrip.js +596 -0
- package/dist/gui/timeline/tracks/EFThumbnailStrip.js.map +1 -0
- package/dist/gui/timeline/tracks/HTMLTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/TextTrack.d.ts +3 -2
- package/dist/gui/timeline/tracks/TextTrack.js +17 -43
- package/dist/gui/timeline/tracks/TextTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/TimegroupTrack.d.ts +3 -4
- package/dist/gui/timeline/tracks/TimegroupTrack.js +33 -23
- package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/TrackItem.d.ts +7 -9
- package/dist/gui/timeline/tracks/TrackItem.js +18 -17
- package/dist/gui/timeline/tracks/TrackItem.js.map +1 -1
- package/dist/gui/timeline/tracks/VideoTrack.d.ts +3 -3
- package/dist/gui/timeline/tracks/VideoTrack.js +11 -14
- package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/WaveformTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -1
- package/dist/gui/timeline/tracks/waveformUtils.js +1 -1
- package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -1
- package/dist/gui/tree/EFTree.d.ts +4 -4
- package/dist/gui/tree/EFTree.js +8 -14
- package/dist/gui/tree/EFTree.js.map +1 -1
- package/dist/gui/tree/EFTreeItem.d.ts +4 -4
- package/dist/gui/tree/EFTreeItem.js +3 -3
- package/dist/gui/tree/EFTreeItem.js.map +1 -1
- package/dist/gui/tree/treeContext.js.map +1 -1
- package/dist/index.d.ts +10 -8
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.js +2 -2
- package/dist/preview/AdaptiveResolutionTracker.js +3 -3
- package/dist/preview/AdaptiveResolutionTracker.js.map +1 -1
- package/dist/preview/FrameController.d.ts +2 -17
- package/dist/preview/FrameController.js +40 -63
- package/dist/preview/FrameController.js.map +1 -1
- package/dist/preview/QualityUpgradeScheduler.d.ts +76 -0
- package/dist/preview/QualityUpgradeScheduler.js +158 -0
- package/dist/preview/QualityUpgradeScheduler.js.map +1 -0
- package/dist/preview/RenderContext.d.ts +119 -1
- package/dist/preview/RenderContext.js +21 -3
- package/dist/preview/RenderContext.js.map +1 -1
- package/dist/preview/RenderProfiler.js.map +1 -1
- package/dist/preview/RenderStats.js +85 -0
- package/dist/preview/RenderStats.js.map +1 -0
- package/dist/preview/encoding/canvasEncoder.js +2 -52
- package/dist/preview/encoding/canvasEncoder.js.map +1 -1
- package/dist/preview/encoding/mainThreadEncoder.js.map +1 -1
- package/dist/preview/encoding/workerEncoder.js.map +1 -1
- package/dist/preview/logger.js.map +1 -1
- package/dist/preview/previewSettings.d.ts +34 -0
- package/dist/preview/previewSettings.js +29 -17
- package/dist/preview/previewSettings.js.map +1 -1
- package/dist/preview/previewTypes.js +4 -4
- package/dist/preview/previewTypes.js.map +1 -1
- package/dist/preview/renderElementToCanvas.d.ts +44 -0
- package/dist/preview/renderElementToCanvas.js +72 -0
- package/dist/preview/renderElementToCanvas.js.map +1 -0
- package/dist/preview/renderTimegroupToCanvas.js +267 -145
- package/dist/preview/renderTimegroupToCanvas.js.map +1 -1
- package/dist/preview/renderTimegroupToCanvas.types.d.ts +30 -0
- package/dist/preview/renderTimegroupToVideo.js +85 -105
- package/dist/preview/renderTimegroupToVideo.js.map +1 -1
- package/dist/preview/{renderTimegroupToVideo.d.ts → renderTimegroupToVideo.types.d.ts} +9 -9
- package/dist/preview/renderVideoToVideo.js +286 -0
- package/dist/preview/renderVideoToVideo.js.map +1 -0
- package/dist/preview/renderers.js.map +1 -1
- package/dist/preview/rendering/ScaleConfig.js +74 -0
- package/dist/preview/rendering/ScaleConfig.js.map +1 -0
- package/dist/preview/rendering/inlineImages.js +1 -44
- package/dist/preview/rendering/inlineImages.js.map +1 -1
- package/dist/preview/rendering/loadImage.js +22 -0
- package/dist/preview/rendering/loadImage.js.map +1 -0
- package/dist/preview/rendering/renderToImageNative.js +3 -3
- package/dist/preview/rendering/renderToImageNative.js.map +1 -1
- package/dist/preview/rendering/serializeTimelineDirect.js +224 -68
- package/dist/preview/rendering/serializeTimelineDirect.js.map +1 -1
- package/dist/preview/statsTrackingStrategy.js +1 -101
- package/dist/preview/statsTrackingStrategy.js.map +1 -1
- package/dist/preview/workers/WorkerPool.js +0 -1
- package/dist/preview/workers/WorkerPool.js.map +1 -1
- package/dist/preview/workers/encoderWorkerInline.js +21 -54
- package/dist/preview/workers/encoderWorkerInline.js.map +1 -1
- package/dist/render/EFRenderAPI.d.ts +2 -1
- package/dist/render/EFRenderAPI.js +12 -36
- package/dist/render/EFRenderAPI.js.map +1 -1
- package/dist/render/getRenderData.js +4 -4
- package/dist/render/getRenderData.js.map +1 -1
- package/dist/style.css +114 -163
- package/dist/transcoding/cache/RequestDeduplicator.js +1 -0
- package/dist/transcoding/cache/RequestDeduplicator.js.map +1 -1
- package/dist/transcoding/types/index.d.ts +1 -1
- package/dist/transcoding/utils/UrlGenerator.js +10 -3
- package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
- package/dist/utils/LRUCache.js +1 -0
- package/dist/utils/LRUCache.js.map +1 -1
- package/dist/utils/frameTime.js +23 -1
- package/dist/utils/frameTime.js.map +1 -1
- package/package.json +21 -8
- package/scripts/build-css.js +8 -1
- package/test/setup.ts +0 -1
- package/test/useAssetMSW.ts +50 -0
- package/test/visualRegressionUtils.ts +23 -9
- package/dist/_virtual/rolldown_runtime.js +0 -27
- package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +0 -1
- package/dist/elements/EFThumbnailStrip.d.ts +0 -167
- package/dist/elements/EFThumbnailStrip.js +0 -731
- package/dist/elements/EFThumbnailStrip.js.map +0 -1
- package/dist/elements/SessionThumbnailCache.js +0 -154
- package/dist/elements/SessionThumbnailCache.js.map +0 -1
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +0 -688
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +0 -1
- package/dist/node_modules/react/cjs/react.development.js +0 -1521
- package/dist/node_modules/react/cjs/react.development.js.map +0 -1
- package/dist/node_modules/react/index.js +0 -13
- package/dist/node_modules/react/index.js.map +0 -1
- package/dist/node_modules/react/jsx-runtime.js +0 -13
- package/dist/node_modules/react/jsx-runtime.js.map +0 -1
- package/dist/preview/encoding/types.d.ts +0 -1
- package/dist/preview/renderTimegroupPreview.js +0 -686
- package/dist/preview/renderTimegroupPreview.js.map +0 -1
- package/dist/preview/renderTimegroupToCanvas.d.ts +0 -42
- package/dist/preview/rendering/renderToImage.d.ts +0 -2
- package/dist/preview/rendering/renderToImage.js +0 -95
- package/dist/preview/rendering/renderToImage.js.map +0 -1
- package/dist/preview/rendering/renderToImageForeignObject.js +0 -163
- package/dist/preview/rendering/renderToImageForeignObject.js.map +0 -1
- package/dist/preview/rendering/renderToImageNative.d.ts +0 -1
- package/dist/preview/rendering/svgSerializer.js +0 -43
- package/dist/preview/rendering/svgSerializer.js.map +0 -1
- package/dist/preview/rendering/types.d.ts +0 -2
- package/dist/preview/thumbnailCacheSettings.js +0 -52
- package/dist/preview/thumbnailCacheSettings.js.map +0 -1
- package/dist/sandbox/PlaybackControls.d.ts +0 -1
- package/dist/sandbox/PlaybackControls.js +0 -10
- package/dist/sandbox/PlaybackControls.js.map +0 -1
- package/dist/sandbox/ScenarioRunner.d.ts +0 -1
- package/dist/sandbox/ScenarioRunner.js +0 -1
- package/dist/sandbox/defineSandbox.d.ts +0 -1
- package/dist/sandbox/index.d.ts +0 -3
- package/dist/sandbox/index.js +0 -2
- package/test/EFVideo.framegen.browsertest.ts +0 -80
- package/test/thumbnail-performance-test.html +0 -116
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
//#region src/preview/RenderStats.ts
|
|
2
|
+
/**
|
|
3
|
+
* RenderStats collects performance data from the rendering system.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* ```typescript
|
|
7
|
+
* const stats = new RenderStats(adaptiveTracker);
|
|
8
|
+
*
|
|
9
|
+
* // In render loop (always call this)
|
|
10
|
+
* stats.recordFrame(renderTime, timestamp, isAtRest);
|
|
11
|
+
*
|
|
12
|
+
* // In display (only when visible)
|
|
13
|
+
* const data = stats.getStats(renderWidth, renderHeight, resolutionScale);
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
var RenderStats = class {
|
|
17
|
+
constructor(adaptiveTracker) {
|
|
18
|
+
this.renderTimes = [];
|
|
19
|
+
this.frameIntervals = [];
|
|
20
|
+
this.lastFrameTime = 0;
|
|
21
|
+
this.ROLLING_WINDOW_SIZE = 30;
|
|
22
|
+
this.TARGET_FRAME_TIME_MS = 33.33;
|
|
23
|
+
this.adaptiveTracker = adaptiveTracker;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Record a completed frame render.
|
|
27
|
+
* Call this from the render loop after each frame completes.
|
|
28
|
+
*
|
|
29
|
+
* @param renderTime - Time spent rendering this frame (ms)
|
|
30
|
+
* @param timestamp - Current timestamp from performance.now() or rAF
|
|
31
|
+
* @param isAtRest - Whether the system is at rest (not playing/scrubbing)
|
|
32
|
+
*/
|
|
33
|
+
recordFrame(renderTime, timestamp, isAtRest) {
|
|
34
|
+
this.renderTimes.push(renderTime);
|
|
35
|
+
if (this.renderTimes.length > this.ROLLING_WINDOW_SIZE) this.renderTimes.shift();
|
|
36
|
+
if (this.lastFrameTime > 0) {
|
|
37
|
+
const interval = timestamp - this.lastFrameTime;
|
|
38
|
+
this.frameIntervals.push(interval);
|
|
39
|
+
if (this.frameIntervals.length > this.ROLLING_WINDOW_SIZE) this.frameIntervals.shift();
|
|
40
|
+
}
|
|
41
|
+
this.lastFrameTime = timestamp;
|
|
42
|
+
if (!isAtRest) this.adaptiveTracker.recordFrame(renderTime, timestamp);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get current statistics for display.
|
|
46
|
+
*
|
|
47
|
+
* @param renderWidth - Current render width in pixels
|
|
48
|
+
* @param renderHeight - Current render height in pixels
|
|
49
|
+
* @param resolutionScale - Current resolution scale (0-1), or null if not applicable
|
|
50
|
+
* @returns Current playback statistics
|
|
51
|
+
*/
|
|
52
|
+
getStats(renderWidth, renderHeight, resolutionScale = null) {
|
|
53
|
+
const avgRenderTime = this.renderTimes.length > 0 ? this.renderTimes.reduce((a, b) => a + b, 0) / this.renderTimes.length : null;
|
|
54
|
+
const avgFrameInterval = this.frameIntervals.length > 0 ? this.frameIntervals.reduce((a, b) => a + b, 0) / this.frameIntervals.length : 16.67;
|
|
55
|
+
const fps = avgFrameInterval > 0 ? 1e3 / avgFrameInterval : 0;
|
|
56
|
+
const headroom = avgRenderTime !== null ? this.TARGET_FRAME_TIME_MS - avgRenderTime : null;
|
|
57
|
+
const trackerStats = this.adaptiveTracker.getStats();
|
|
58
|
+
return {
|
|
59
|
+
fps,
|
|
60
|
+
avgRenderTime,
|
|
61
|
+
headroom,
|
|
62
|
+
pressureState: trackerStats.pressureState,
|
|
63
|
+
pressureHistory: trackerStats.pressureHistory,
|
|
64
|
+
renderWidth,
|
|
65
|
+
renderHeight,
|
|
66
|
+
resolutionScale,
|
|
67
|
+
samplesAtCurrentScale: trackerStats.samplesAtCurrentScale,
|
|
68
|
+
canScaleUp: trackerStats.canScaleUp,
|
|
69
|
+
canScaleDown: trackerStats.canScaleDown
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Reset all collected statistics.
|
|
74
|
+
* Useful when switching modes or starting a new session.
|
|
75
|
+
*/
|
|
76
|
+
reset() {
|
|
77
|
+
this.renderTimes = [];
|
|
78
|
+
this.frameIntervals = [];
|
|
79
|
+
this.lastFrameTime = 0;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
//#endregion
|
|
84
|
+
export { RenderStats };
|
|
85
|
+
//# sourceMappingURL=RenderStats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RenderStats.js","names":[],"sources":["../../src/preview/RenderStats.ts"],"sourcesContent":["/**\n * RenderStats: Always-on performance statistics collection\n *\n * This class continuously collects rendering performance data regardless of\n * whether stats are being displayed. It acts as a persistent data store that\n * the UI can read from at any time.\n *\n * Key principles:\n * - Collection is always active (not tied to display visibility)\n * - Data persists across mode changes and zoom operations\n * - Display is orthogonal to collection\n */\n\nimport type { AdaptiveResolutionTracker } from \"./AdaptiveResolutionTracker.js\";\n\n/**\n * Playback statistics for display.\n */\nexport interface PlaybackStats {\n fps: number;\n avgRenderTime: number | null;\n headroom: number | null;\n pressureState: string;\n pressureHistory: string[];\n renderWidth: number;\n renderHeight: number;\n resolutionScale: number | null;\n samplesAtCurrentScale?: number;\n canScaleUp?: boolean;\n canScaleDown?: boolean;\n}\n\n/**\n * RenderStats collects performance data from the rendering system.\n *\n * Usage:\n * ```typescript\n * const stats = new RenderStats(adaptiveTracker);\n *\n * // In render loop (always call this)\n * stats.recordFrame(renderTime, timestamp, isAtRest);\n *\n * // In display (only when visible)\n * const data = stats.getStats(renderWidth, renderHeight, resolutionScale);\n * ```\n */\nexport class RenderStats {\n private adaptiveTracker: AdaptiveResolutionTracker;\n\n // Frame timing data\n private renderTimes: number[] = [];\n private frameIntervals: number[] = [];\n private lastFrameTime = 0;\n\n private readonly ROLLING_WINDOW_SIZE = 30; // ~1 second at 30fps\n private readonly TARGET_FRAME_TIME_MS = 33.33; // 30fps target\n\n constructor(adaptiveTracker: AdaptiveResolutionTracker) {\n this.adaptiveTracker = adaptiveTracker;\n }\n\n /**\n * Record a completed frame render.\n * Call this from the render loop after each frame completes.\n *\n * @param renderTime - Time spent rendering this frame (ms)\n * @param timestamp - Current timestamp from performance.now() or rAF\n * @param isAtRest - Whether the system is at rest (not playing/scrubbing)\n */\n recordFrame(renderTime: number, timestamp: number, isAtRest: boolean): void {\n // Track render times for averaging\n this.renderTimes.push(renderTime);\n if (this.renderTimes.length > this.ROLLING_WINDOW_SIZE) {\n this.renderTimes.shift();\n }\n\n // Track frame intervals for FPS calculation\n if (this.lastFrameTime > 0) {\n const interval = timestamp - this.lastFrameTime;\n this.frameIntervals.push(interval);\n if (this.frameIntervals.length > this.ROLLING_WINDOW_SIZE) {\n this.frameIntervals.shift();\n }\n }\n this.lastFrameTime = timestamp;\n\n // Update adaptive tracker (only when in motion)\n if (!isAtRest) {\n this.adaptiveTracker.recordFrame(renderTime, timestamp);\n }\n }\n\n /**\n * Get current statistics for display.\n *\n * @param renderWidth - Current render width in pixels\n * @param renderHeight - Current render height in pixels\n * @param resolutionScale - Current resolution scale (0-1), or null if not applicable\n * @returns Current playback statistics\n */\n getStats(\n renderWidth: number,\n renderHeight: number,\n resolutionScale: number | null = null,\n ): PlaybackStats {\n // Calculate average render time\n const avgRenderTime =\n this.renderTimes.length > 0\n ? this.renderTimes.reduce((a, b) => a + b, 0) / this.renderTimes.length\n : null;\n\n // Calculate FPS from frame intervals\n const avgFrameInterval =\n this.frameIntervals.length > 0\n ? this.frameIntervals.reduce((a, b) => a + b, 0) /\n this.frameIntervals.length\n : 16.67;\n const fps = avgFrameInterval > 0 ? 1000 / avgFrameInterval : 0;\n\n // Calculate headroom (positive = faster than target, negative = slower)\n const headroom =\n avgRenderTime !== null ? this.TARGET_FRAME_TIME_MS - avgRenderTime : null;\n\n // Get adaptive tracker stats\n const trackerStats = this.adaptiveTracker.getStats();\n\n return {\n fps,\n avgRenderTime,\n headroom,\n pressureState: trackerStats.pressureState,\n pressureHistory: trackerStats.pressureHistory,\n renderWidth,\n renderHeight,\n resolutionScale,\n samplesAtCurrentScale: trackerStats.samplesAtCurrentScale,\n canScaleUp: trackerStats.canScaleUp,\n canScaleDown: trackerStats.canScaleDown,\n };\n }\n\n /**\n * Reset all collected statistics.\n * Useful when switching modes or starting a new session.\n */\n reset(): void {\n this.renderTimes = [];\n this.frameIntervals = [];\n this.lastFrameTime = 0;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AA8CA,IAAa,cAAb,MAAyB;CAWvB,YAAY,iBAA4C;qBAPxB,EAAE;wBACC,EAAE;uBACb;6BAEe;8BACC;AAGtC,OAAK,kBAAkB;;;;;;;;;;CAWzB,YAAY,YAAoB,WAAmB,UAAyB;AAE1E,OAAK,YAAY,KAAK,WAAW;AACjC,MAAI,KAAK,YAAY,SAAS,KAAK,oBACjC,MAAK,YAAY,OAAO;AAI1B,MAAI,KAAK,gBAAgB,GAAG;GAC1B,MAAM,WAAW,YAAY,KAAK;AAClC,QAAK,eAAe,KAAK,SAAS;AAClC,OAAI,KAAK,eAAe,SAAS,KAAK,oBACpC,MAAK,eAAe,OAAO;;AAG/B,OAAK,gBAAgB;AAGrB,MAAI,CAAC,SACH,MAAK,gBAAgB,YAAY,YAAY,UAAU;;;;;;;;;;CAY3D,SACE,aACA,cACA,kBAAiC,MAClB;EAEf,MAAM,gBACJ,KAAK,YAAY,SAAS,IACtB,KAAK,YAAY,QAAQ,GAAG,MAAM,IAAI,GAAG,EAAE,GAAG,KAAK,YAAY,SAC/D;EAGN,MAAM,mBACJ,KAAK,eAAe,SAAS,IACzB,KAAK,eAAe,QAAQ,GAAG,MAAM,IAAI,GAAG,EAAE,GAC9C,KAAK,eAAe,SACpB;EACN,MAAM,MAAM,mBAAmB,IAAI,MAAO,mBAAmB;EAG7D,MAAM,WACJ,kBAAkB,OAAO,KAAK,uBAAuB,gBAAgB;EAGvE,MAAM,eAAe,KAAK,gBAAgB,UAAU;AAEpD,SAAO;GACL;GACA;GACA;GACA,eAAe,aAAa;GAC5B,iBAAiB,aAAa;GAC9B;GACA;GACA;GACA,uBAAuB,aAAa;GACpC,YAAY,aAAa;GACzB,cAAc,aAAa;GAC5B;;;;;;CAOH,QAAc;AACZ,OAAK,cAAc,EAAE;AACrB,OAAK,iBAAiB,EAAE;AACxB,OAAK,gBAAgB"}
|
|
@@ -41,26 +41,12 @@ function getWorkerPool() {
|
|
|
41
41
|
return _workerPool;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
* Check if an element is an EFVideo.
|
|
45
|
-
*/
|
|
46
|
-
function isEFVideo(element) {
|
|
47
|
-
return element.tagName === "EF-VIDEO";
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Check if an element is an EFSurface.
|
|
51
|
-
*/
|
|
52
|
-
function isEFSurface(element) {
|
|
53
|
-
return element.tagName === "EF-SURFACE";
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
44
|
* Encode canvases to data URLs in parallel using worker pool.
|
|
57
45
|
* Falls back to main thread encoding if workers are unavailable.
|
|
58
|
-
*
|
|
46
|
+
*
|
|
59
47
|
* When RenderContext and sourceMap are provided:
|
|
60
48
|
* - Checks cache for static elements (ef-image, ef-waveform)
|
|
61
|
-
*
|
|
62
|
-
* - Shares cached frames for ef-surface elements targeting ef-video
|
|
63
|
-
*
|
|
49
|
+
*
|
|
64
50
|
* @param canvases - Array of canvases to encode
|
|
65
51
|
* @param options - Encoding options including optional renderContext and sourceMap
|
|
66
52
|
* @returns Promise resolving to array of encoded results
|
|
@@ -74,42 +60,6 @@ async function encodeCanvasesInParallel(canvases, options = {}) {
|
|
|
74
60
|
const preserveAlpha = canvas.dataset.preserveAlpha === "true";
|
|
75
61
|
const sourceElement = sourceMap?.get(canvas);
|
|
76
62
|
if (renderContext && sourceElement) {
|
|
77
|
-
if (isEFVideo(sourceElement)) {
|
|
78
|
-
const sourceTimeMs = sourceElement.currentSourceTimeMs;
|
|
79
|
-
try {
|
|
80
|
-
return {
|
|
81
|
-
canvas,
|
|
82
|
-
dataUrl: (await sourceElement.captureFrameAtSourceTime(sourceTimeMs, { quality: "main" })).dataUrl,
|
|
83
|
-
preserveAlpha: false
|
|
84
|
-
};
|
|
85
|
-
} catch (e) {
|
|
86
|
-
logger.warn("[canvasEncoder] Direct capture failed, falling back to canvas encoding:", e);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
if (isEFSurface(sourceElement)) {
|
|
90
|
-
const target = sourceElement.targetElement;
|
|
91
|
-
if (target && isEFVideo(target)) {
|
|
92
|
-
const videoTarget = target;
|
|
93
|
-
const sourceTimeMs = videoTarget.currentSourceTimeMs;
|
|
94
|
-
const cached = renderContext.getCachedVideoFrame(videoTarget, sourceTimeMs);
|
|
95
|
-
if (cached) return {
|
|
96
|
-
canvas,
|
|
97
|
-
dataUrl: cached.dataUrl,
|
|
98
|
-
preserveAlpha: false
|
|
99
|
-
};
|
|
100
|
-
try {
|
|
101
|
-
const frame = await videoTarget.captureFrameAtSourceTime(sourceTimeMs, { quality: "main" });
|
|
102
|
-
renderContext.setCachedVideoFrame(videoTarget, sourceTimeMs, frame);
|
|
103
|
-
return {
|
|
104
|
-
canvas,
|
|
105
|
-
dataUrl: frame.dataUrl,
|
|
106
|
-
preserveAlpha: false
|
|
107
|
-
};
|
|
108
|
-
} catch (e) {
|
|
109
|
-
logger.warn("[canvasEncoder] Direct capture for surface target failed:", e);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
63
|
const cachedDataUrl = renderContext.getCachedCanvasDataUrl(sourceElement);
|
|
114
64
|
if (cachedDataUrl) return {
|
|
115
65
|
canvas,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvasEncoder.js","names":["_workerPool: WorkerPool | null","dataUrl: string"],"sources":["../../../src/preview/encoding/canvasEncoder.ts"],"sourcesContent":["/**\n * Canvas encoding orchestration with worker pool support.\n * \n * Supports caching via RenderContext:\n * - For ef-image/ef-waveform: caches by element + renderVersion\n * - For ef-video: uses direct capture API with source timestamp caching\n */\n\nimport { logger } from \"../logger.js\";\nimport { WorkerPool } from \"../workers/WorkerPool.js\";\nimport { getEncoderWorkerUrl } from \"../workers/encoderWorkerInline.js\";\nimport { encodeCanvasOnMainThread } from \"./mainThreadEncoder.js\";\nimport { encodeCanvasInWorker } from \"./workerEncoder.js\";\nimport type { CanvasEncodeResult, CanvasEncodeOptions } from \"./types.js\";\nimport type { EFVideo } from \"../../elements/EFVideo.js\";\nimport type { EFSurface } from \"../../elements/EFSurface.js\";\n\n// Module-level worker pool state\nlet _workerPool: WorkerPool | null = null;\nlet _workerPoolWarningLogged = false;\n\n/**\n * Get or create the worker pool for canvas encoding.\n * Returns null if workers are not available.\n */\nfunction getWorkerPool(): WorkerPool | null {\n if (_workerPool) {\n return _workerPool;\n }\n\n // Check if workers are available\n if (\n typeof Worker === \"undefined\" ||\n typeof OffscreenCanvas === \"undefined\" ||\n typeof createImageBitmap === \"undefined\"\n ) {\n if (!_workerPoolWarningLogged) {\n _workerPoolWarningLogged = true;\n logger.warn(\n \"[canvasEncoder] Web Workers or OffscreenCanvas not available, using main thread fallback\",\n );\n }\n return null;\n }\n\n try {\n // Use inline worker URL - this works in any bundler environment\n // because the worker code is embedded in the bundle as a blob URL\n const workerUrl = getEncoderWorkerUrl();\n \n _workerPool = new WorkerPool(workerUrl);\n \n // Check if workers were actually created\n if (!_workerPool.isAvailable()) {\n const reason = _workerPool.workerCount === 0 \n ? \"no workers created (check console for errors)\" \n : \"workers not available\";\n _workerPool = null;\n if (!_workerPoolWarningLogged) {\n _workerPoolWarningLogged = true;\n logger.warn(\n `[canvasEncoder] Worker pool initialization failed (${reason}), using main thread fallback`,\n );\n }\n }\n } catch (error) {\n _workerPool = null;\n if (!_workerPoolWarningLogged) {\n _workerPoolWarningLogged = true;\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.warn(\n `[canvasEncoder] Failed to create worker pool: ${errorMessage} - using main thread fallback`,\n );\n }\n }\n\n return _workerPool;\n}\n\n/**\n * Check if an element is an EFVideo.\n */\nfunction isEFVideo(element: Element): element is EFVideo {\n return element.tagName === \"EF-VIDEO\";\n}\n\n/**\n * Check if an element is an EFSurface.\n */\nfunction isEFSurface(element: Element): element is EFSurface {\n return element.tagName === \"EF-SURFACE\";\n}\n\n/**\n * Encode canvases to data URLs in parallel using worker pool.\n * Falls back to main thread encoding if workers are unavailable.\n * \n * When RenderContext and sourceMap are provided:\n * - Checks cache for static elements (ef-image, ef-waveform)\n * - Uses direct capture API for ef-video elements\n * - Shares cached frames for ef-surface elements targeting ef-video\n * \n * @param canvases - Array of canvases to encode\n * @param options - Encoding options including optional renderContext and sourceMap\n * @returns Promise resolving to array of encoded results\n */\nexport async function encodeCanvasesInParallel(\n canvases: HTMLCanvasElement[],\n options: CanvasEncodeOptions = {},\n): Promise<CanvasEncodeResult[]> {\n const { scale: canvasScale = 1, renderContext, sourceMap } = options;\n const workerPool = getWorkerPool();\n\n // Helper to encode a single canvas (with caching)\n const encodeCanvas = async (canvas: HTMLCanvasElement): Promise<CanvasEncodeResult | null> => {\n try {\n if (canvas.width === 0 || canvas.height === 0) {\n return null;\n }\n\n const preserveAlpha = canvas.dataset.preserveAlpha === \"true\";\n const sourceElement = sourceMap?.get(canvas);\n \n // OPTIMIZATION: Check RenderContext cache for static elements\n if (renderContext && sourceElement) {\n // For ef-video, use direct capture API\n if (isEFVideo(sourceElement)) {\n const sourceTimeMs = sourceElement.currentSourceTimeMs;\n \n // Use direct capture API (bypasses frameTask)\n // Always use \"main\" quality for export - scrub track is only for preview scrubbing\n try {\n const frame = await sourceElement.captureFrameAtSourceTime(sourceTimeMs, { quality: \"main\" });\n return { canvas, dataUrl: frame.dataUrl, preserveAlpha: false };\n } catch (e) {\n // Fall back to normal encoding if direct capture fails\n logger.warn(\"[canvasEncoder] Direct capture failed, falling back to canvas encoding:\", e);\n }\n }\n \n // For ef-surface targeting ef-video, share the video's cached frame\n if (isEFSurface(sourceElement)) {\n const target = sourceElement.targetElement;\n if (target && isEFVideo(target as Element)) {\n const videoTarget = target as unknown as EFVideo;\n const sourceTimeMs = videoTarget.currentSourceTimeMs;\n const cached = renderContext.getCachedVideoFrame(videoTarget, sourceTimeMs);\n if (cached) {\n return { canvas, dataUrl: cached.dataUrl, preserveAlpha: false };\n }\n \n // Capture from the target video\n // Always use \"main\" quality for export\n try {\n const frame = await videoTarget.captureFrameAtSourceTime(sourceTimeMs, { quality: \"main\" });\n renderContext.setCachedVideoFrame(videoTarget, sourceTimeMs, frame);\n return { canvas, dataUrl: frame.dataUrl, preserveAlpha: false };\n } catch (e) {\n logger.warn(\"[canvasEncoder] Direct capture for surface target failed:\", e);\n }\n }\n }\n \n // For static elements (ef-image, ef-waveform), check version-based cache\n const cachedDataUrl = renderContext.getCachedCanvasDataUrl(sourceElement);\n if (cachedDataUrl) {\n return { canvas, dataUrl: cachedDataUrl, preserveAlpha };\n }\n }\n\n // Standard encoding path (fallback when no RenderContext cache)\n let sourceCanvas = canvas;\n\n // Handle canvas scaling on main thread before encoding\n if (canvasScale < 1) {\n const scaledWidth = Math.floor(canvas.width * canvasScale);\n const scaledHeight = Math.floor(canvas.height * canvasScale);\n const scaledCanvas = document.createElement(\"canvas\");\n scaledCanvas.width = scaledWidth;\n scaledCanvas.height = scaledHeight;\n const scaledCtx = scaledCanvas.getContext(\"2d\");\n if (scaledCtx) {\n scaledCtx.drawImage(canvas, 0, 0, scaledWidth, scaledHeight);\n sourceCanvas = scaledCanvas;\n }\n }\n\n let dataUrl: string;\n \n if (workerPool) {\n // Encode in worker\n dataUrl = await workerPool.execute((worker) =>\n encodeCanvasInWorker(worker, sourceCanvas, preserveAlpha),\n );\n } else {\n // Main thread fallback - warning already logged once in getWorkerPool()\n const encoded = encodeCanvasOnMainThread(sourceCanvas, canvasScale);\n if (!encoded) return null;\n dataUrl = encoded.dataUrl;\n }\n\n // Cache the result for static elements\n if (renderContext && sourceElement) {\n renderContext.setCachedCanvasDataUrl(sourceElement, dataUrl);\n }\n\n return { canvas, dataUrl, preserveAlpha };\n } catch (error) {\n // Fallback to main thread if worker encoding fails\n logger.warn(\"[canvasEncoder] Worker encoding failed, using main thread fallback:\", error);\n const encoded = encodeCanvasOnMainThread(canvas, canvasScale);\n if (encoded) {\n logger.warn(\"[canvasEncoder] Main thread fallback succeeded\");\n return { canvas, ...encoded };\n }\n \n // Cross-origin canvas or other error - skip\n logger.warn(\"[canvasEncoder] Main thread encoding also failed, skipping canvas:\", error);\n return null;\n }\n };\n\n // Encode all canvases in parallel\n const encodingTasks = canvases.map(encodeCanvas);\n const encodedResults = await Promise.all(encodingTasks);\n const validResults = encodedResults.filter(\n (r): r is CanvasEncodeResult => r !== null,\n );\n return validResults;\n}\n\n/**\n * Reset the worker pool state (for testing).\n */\nexport function resetWorkerPool(): void {\n if (_workerPool) {\n _workerPool.terminate();\n _workerPool = null;\n }\n _workerPoolWarningLogged = false;\n}\n"],"mappings":";;;;;;;AAkBA,IAAIA,cAAiC;AACrC,IAAI,2BAA2B;;;;;AAM/B,SAAS,gBAAmC;AAC1C,KAAI,YACF,QAAO;AAIT,KACE,OAAO,WAAW,eAClB,OAAO,oBAAoB,eAC3B,OAAO,sBAAsB,aAC7B;AACA,MAAI,CAAC,0BAA0B;AAC7B,8BAA2B;AAC3B,UAAO,KACL,2FACD;;AAEH,SAAO;;AAGT,KAAI;AAKF,gBAAc,IAAI,WAFA,qBAAqB,CAEA;AAGvC,MAAI,CAAC,YAAY,aAAa,EAAE;GAC9B,MAAM,SAAS,YAAY,gBAAgB,IACvC,kDACA;AACJ,iBAAc;AACd,OAAI,CAAC,0BAA0B;AAC7B,+BAA2B;AAC3B,WAAO,KACL,sDAAsD,OAAO,+BAC9D;;;UAGE,OAAO;AACd,gBAAc;AACd,MAAI,CAAC,0BAA0B;AAC7B,8BAA2B;GAC3B,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAC3E,UAAO,KACL,iDAAiD,aAAa,+BAC/D;;;AAIL,QAAO;;;;;AAMT,SAAS,UAAU,SAAsC;AACvD,QAAO,QAAQ,YAAY;;;;;AAM7B,SAAS,YAAY,SAAwC;AAC3D,QAAO,QAAQ,YAAY;;;;;;;;;;;;;;;AAgB7B,eAAsB,yBACpB,UACA,UAA+B,EAAE,EACF;CAC/B,MAAM,EAAE,OAAO,cAAc,GAAG,eAAe,cAAc;CAC7D,MAAM,aAAa,eAAe;CAGlC,MAAM,eAAe,OAAO,WAAkE;AAC5F,MAAI;AACF,OAAI,OAAO,UAAU,KAAK,OAAO,WAAW,EAC1C,QAAO;GAGT,MAAM,gBAAgB,OAAO,QAAQ,kBAAkB;GACvD,MAAM,gBAAgB,WAAW,IAAI,OAAO;AAG5C,OAAI,iBAAiB,eAAe;AAElC,QAAI,UAAU,cAAc,EAAE;KAC5B,MAAM,eAAe,cAAc;AAInC,SAAI;AAEF,aAAO;OAAE;OAAQ,UADH,MAAM,cAAc,yBAAyB,cAAc,EAAE,SAAS,QAAQ,CAAC,EAC7D;OAAS,eAAe;OAAO;cACxD,GAAG;AAEV,aAAO,KAAK,2EAA2E,EAAE;;;AAK7F,QAAI,YAAY,cAAc,EAAE;KAC9B,MAAM,SAAS,cAAc;AAC7B,SAAI,UAAU,UAAU,OAAkB,EAAE;MAC1C,MAAM,cAAc;MACpB,MAAM,eAAe,YAAY;MACjC,MAAM,SAAS,cAAc,oBAAoB,aAAa,aAAa;AAC3E,UAAI,OACF,QAAO;OAAE;OAAQ,SAAS,OAAO;OAAS,eAAe;OAAO;AAKlE,UAAI;OACF,MAAM,QAAQ,MAAM,YAAY,yBAAyB,cAAc,EAAE,SAAS,QAAQ,CAAC;AAC3F,qBAAc,oBAAoB,aAAa,cAAc,MAAM;AACnE,cAAO;QAAE;QAAQ,SAAS,MAAM;QAAS,eAAe;QAAO;eACxD,GAAG;AACV,cAAO,KAAK,6DAA6D,EAAE;;;;IAMjF,MAAM,gBAAgB,cAAc,uBAAuB,cAAc;AACzE,QAAI,cACF,QAAO;KAAE;KAAQ,SAAS;KAAe;KAAe;;GAK5D,IAAI,eAAe;AAGnB,OAAI,cAAc,GAAG;IACnB,MAAM,cAAc,KAAK,MAAM,OAAO,QAAQ,YAAY;IAC1D,MAAM,eAAe,KAAK,MAAM,OAAO,SAAS,YAAY;IAC5D,MAAM,eAAe,SAAS,cAAc,SAAS;AACrD,iBAAa,QAAQ;AACrB,iBAAa,SAAS;IACtB,MAAM,YAAY,aAAa,WAAW,KAAK;AAC/C,QAAI,WAAW;AACb,eAAU,UAAU,QAAQ,GAAG,GAAG,aAAa,aAAa;AAC5D,oBAAe;;;GAInB,IAAIC;AAEJ,OAAI,WAEF,WAAU,MAAM,WAAW,SAAS,WAClC,qBAAqB,QAAQ,cAAc,cAAc,CAC1D;QACI;IAEL,MAAM,UAAU,yBAAyB,cAAc,YAAY;AACnE,QAAI,CAAC,QAAS,QAAO;AACrB,cAAU,QAAQ;;AAIpB,OAAI,iBAAiB,cACnB,eAAc,uBAAuB,eAAe,QAAQ;AAG9D,UAAO;IAAE;IAAQ;IAAS;IAAe;WAClC,OAAO;AAEd,UAAO,KAAK,uEAAuE,MAAM;GACzF,MAAM,UAAU,yBAAyB,QAAQ,YAAY;AAC7D,OAAI,SAAS;AACX,WAAO,KAAK,iDAAiD;AAC7D,WAAO;KAAE;KAAQ,GAAG;KAAS;;AAI/B,UAAO,KAAK,sEAAsE,MAAM;AACxF,UAAO;;;CAKX,MAAM,gBAAgB,SAAS,IAAI,aAAa;AAKhD,SAJuB,MAAM,QAAQ,IAAI,cAAc,EACnB,QACjC,MAA+B,MAAM,KACvC"}
|
|
1
|
+
{"version":3,"file":"canvasEncoder.js","names":["_workerPool: WorkerPool | null","dataUrl: string"],"sources":["../../../src/preview/encoding/canvasEncoder.ts"],"sourcesContent":["/**\n * Canvas encoding orchestration with worker pool support.\n *\n * Supports caching via RenderContext:\n * - For ef-image/ef-waveform: caches by element + renderVersion\n */\n\nimport { logger } from \"../logger.js\";\nimport { WorkerPool } from \"../workers/WorkerPool.js\";\nimport { getEncoderWorkerUrl } from \"../workers/encoderWorkerInline.js\";\nimport { encodeCanvasOnMainThread } from \"./mainThreadEncoder.js\";\nimport { encodeCanvasInWorker } from \"./workerEncoder.js\";\nimport type { CanvasEncodeResult, CanvasEncodeOptions } from \"./types.js\";\n\n// Module-level worker pool state\nlet _workerPool: WorkerPool | null = null;\nlet _workerPoolWarningLogged = false;\n\n/**\n * Get or create the worker pool for canvas encoding.\n * Returns null if workers are not available.\n */\nfunction getWorkerPool(): WorkerPool | null {\n if (_workerPool) {\n return _workerPool;\n }\n\n // Check if workers are available\n if (\n typeof Worker === \"undefined\" ||\n typeof OffscreenCanvas === \"undefined\" ||\n typeof createImageBitmap === \"undefined\"\n ) {\n if (!_workerPoolWarningLogged) {\n _workerPoolWarningLogged = true;\n logger.warn(\n \"[canvasEncoder] Web Workers or OffscreenCanvas not available, using main thread fallback\",\n );\n }\n return null;\n }\n\n try {\n // Use inline worker URL - this works in any bundler environment\n // because the worker code is embedded in the bundle as a blob URL\n const workerUrl = getEncoderWorkerUrl();\n\n _workerPool = new WorkerPool(workerUrl);\n\n // Check if workers were actually created\n if (!_workerPool.isAvailable()) {\n const reason =\n _workerPool.workerCount === 0\n ? \"no workers created (check console for errors)\"\n : \"workers not available\";\n _workerPool = null;\n if (!_workerPoolWarningLogged) {\n _workerPoolWarningLogged = true;\n logger.warn(\n `[canvasEncoder] Worker pool initialization failed (${reason}), using main thread fallback`,\n );\n }\n }\n } catch (error) {\n _workerPool = null;\n if (!_workerPoolWarningLogged) {\n _workerPoolWarningLogged = true;\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n logger.warn(\n `[canvasEncoder] Failed to create worker pool: ${errorMessage} - using main thread fallback`,\n );\n }\n }\n\n return _workerPool;\n}\n\n/**\n * Encode canvases to data URLs in parallel using worker pool.\n * Falls back to main thread encoding if workers are unavailable.\n *\n * When RenderContext and sourceMap are provided:\n * - Checks cache for static elements (ef-image, ef-waveform)\n *\n * @param canvases - Array of canvases to encode\n * @param options - Encoding options including optional renderContext and sourceMap\n * @returns Promise resolving to array of encoded results\n */\nexport async function encodeCanvasesInParallel(\n canvases: HTMLCanvasElement[],\n options: CanvasEncodeOptions = {},\n): Promise<CanvasEncodeResult[]> {\n const { scale: canvasScale = 1, renderContext, sourceMap } = options;\n const workerPool = getWorkerPool();\n\n // Helper to encode a single canvas (with caching)\n const encodeCanvas = async (\n canvas: HTMLCanvasElement,\n ): Promise<CanvasEncodeResult | null> => {\n try {\n if (canvas.width === 0 || canvas.height === 0) {\n return null;\n }\n\n const preserveAlpha = canvas.dataset.preserveAlpha === \"true\";\n const sourceElement = sourceMap?.get(canvas);\n\n // Check RenderContext cache for static elements (ef-image, ef-waveform)\n if (renderContext && sourceElement) {\n const cachedDataUrl =\n renderContext.getCachedCanvasDataUrl(sourceElement);\n if (cachedDataUrl) {\n return { canvas, dataUrl: cachedDataUrl, preserveAlpha };\n }\n }\n\n // Standard encoding path (fallback when no RenderContext cache)\n let sourceCanvas = canvas;\n\n // Handle canvas scaling on main thread before encoding\n if (canvasScale < 1) {\n const scaledWidth = Math.floor(canvas.width * canvasScale);\n const scaledHeight = Math.floor(canvas.height * canvasScale);\n const scaledCanvas = document.createElement(\"canvas\");\n scaledCanvas.width = scaledWidth;\n scaledCanvas.height = scaledHeight;\n const scaledCtx = scaledCanvas.getContext(\"2d\");\n if (scaledCtx) {\n scaledCtx.drawImage(canvas, 0, 0, scaledWidth, scaledHeight);\n sourceCanvas = scaledCanvas;\n }\n }\n\n let dataUrl: string;\n\n if (workerPool) {\n // Encode in worker\n dataUrl = await workerPool.execute((worker) =>\n encodeCanvasInWorker(worker, sourceCanvas, preserveAlpha),\n );\n } else {\n // Main thread fallback - warning already logged once in getWorkerPool()\n const encoded = encodeCanvasOnMainThread(sourceCanvas, canvasScale);\n if (!encoded) return null;\n dataUrl = encoded.dataUrl;\n }\n\n // Cache the result for static elements\n if (renderContext && sourceElement) {\n renderContext.setCachedCanvasDataUrl(sourceElement, dataUrl);\n }\n\n return { canvas, dataUrl, preserveAlpha };\n } catch (error) {\n // Fallback to main thread if worker encoding fails\n logger.warn(\n \"[canvasEncoder] Worker encoding failed, using main thread fallback:\",\n error,\n );\n const encoded = encodeCanvasOnMainThread(canvas, canvasScale);\n if (encoded) {\n logger.warn(\"[canvasEncoder] Main thread fallback succeeded\");\n return { canvas, ...encoded };\n }\n\n // Cross-origin canvas or other error - skip\n logger.warn(\n \"[canvasEncoder] Main thread encoding also failed, skipping canvas:\",\n error,\n );\n return null;\n }\n };\n\n // Encode all canvases in parallel\n const encodingTasks = canvases.map(encodeCanvas);\n const encodedResults = await Promise.all(encodingTasks);\n const validResults = encodedResults.filter(\n (r): r is CanvasEncodeResult => r !== null,\n );\n return validResults;\n}\n\n/**\n * Reset the worker pool state (for testing).\n */\nexport function resetWorkerPool(): void {\n if (_workerPool) {\n _workerPool.terminate();\n _workerPool = null;\n }\n _workerPoolWarningLogged = false;\n}\n"],"mappings":";;;;;;;AAeA,IAAIA,cAAiC;AACrC,IAAI,2BAA2B;;;;;AAM/B,SAAS,gBAAmC;AAC1C,KAAI,YACF,QAAO;AAIT,KACE,OAAO,WAAW,eAClB,OAAO,oBAAoB,eAC3B,OAAO,sBAAsB,aAC7B;AACA,MAAI,CAAC,0BAA0B;AAC7B,8BAA2B;AAC3B,UAAO,KACL,2FACD;;AAEH,SAAO;;AAGT,KAAI;AAKF,gBAAc,IAAI,WAFA,qBAAqB,CAEA;AAGvC,MAAI,CAAC,YAAY,aAAa,EAAE;GAC9B,MAAM,SACJ,YAAY,gBAAgB,IACxB,kDACA;AACN,iBAAc;AACd,OAAI,CAAC,0BAA0B;AAC7B,+BAA2B;AAC3B,WAAO,KACL,sDAAsD,OAAO,+BAC9D;;;UAGE,OAAO;AACd,gBAAc;AACd,MAAI,CAAC,0BAA0B;AAC7B,8BAA2B;GAC3B,MAAM,eACJ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACxD,UAAO,KACL,iDAAiD,aAAa,+BAC/D;;;AAIL,QAAO;;;;;;;;;;;;;AAcT,eAAsB,yBACpB,UACA,UAA+B,EAAE,EACF;CAC/B,MAAM,EAAE,OAAO,cAAc,GAAG,eAAe,cAAc;CAC7D,MAAM,aAAa,eAAe;CAGlC,MAAM,eAAe,OACnB,WACuC;AACvC,MAAI;AACF,OAAI,OAAO,UAAU,KAAK,OAAO,WAAW,EAC1C,QAAO;GAGT,MAAM,gBAAgB,OAAO,QAAQ,kBAAkB;GACvD,MAAM,gBAAgB,WAAW,IAAI,OAAO;AAG5C,OAAI,iBAAiB,eAAe;IAClC,MAAM,gBACJ,cAAc,uBAAuB,cAAc;AACrD,QAAI,cACF,QAAO;KAAE;KAAQ,SAAS;KAAe;KAAe;;GAK5D,IAAI,eAAe;AAGnB,OAAI,cAAc,GAAG;IACnB,MAAM,cAAc,KAAK,MAAM,OAAO,QAAQ,YAAY;IAC1D,MAAM,eAAe,KAAK,MAAM,OAAO,SAAS,YAAY;IAC5D,MAAM,eAAe,SAAS,cAAc,SAAS;AACrD,iBAAa,QAAQ;AACrB,iBAAa,SAAS;IACtB,MAAM,YAAY,aAAa,WAAW,KAAK;AAC/C,QAAI,WAAW;AACb,eAAU,UAAU,QAAQ,GAAG,GAAG,aAAa,aAAa;AAC5D,oBAAe;;;GAInB,IAAIC;AAEJ,OAAI,WAEF,WAAU,MAAM,WAAW,SAAS,WAClC,qBAAqB,QAAQ,cAAc,cAAc,CAC1D;QACI;IAEL,MAAM,UAAU,yBAAyB,cAAc,YAAY;AACnE,QAAI,CAAC,QAAS,QAAO;AACrB,cAAU,QAAQ;;AAIpB,OAAI,iBAAiB,cACnB,eAAc,uBAAuB,eAAe,QAAQ;AAG9D,UAAO;IAAE;IAAQ;IAAS;IAAe;WAClC,OAAO;AAEd,UAAO,KACL,uEACA,MACD;GACD,MAAM,UAAU,yBAAyB,QAAQ,YAAY;AAC7D,OAAI,SAAS;AACX,WAAO,KAAK,iDAAiD;AAC7D,WAAO;KAAE;KAAQ,GAAG;KAAS;;AAI/B,UAAO,KACL,sEACA,MACD;AACD,UAAO;;;CAKX,MAAM,gBAAgB,SAAS,IAAI,aAAa;AAKhD,SAJuB,MAAM,QAAQ,IAAI,cAAc,EACnB,QACjC,MAA+B,MAAM,KACvC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mainThreadEncoder.js","names":["dataUrl: string"],"sources":["../../../src/preview/encoding/mainThreadEncoder.ts"],"sourcesContent":["/**\n * Main thread canvas encoding (fallback implementation).\n */\n\n// JPEG quality constants\nexport const JPEG_QUALITY_HIGH = 0.92;\nexport const JPEG_QUALITY_MEDIUM = 0.85;\n\n/**\n * Encode a single canvas to a data URL on the main thread (fallback).\n * @param canvas - The canvas to encode\n * @param canvasScale - Scale factor for encoding (default: 1)\n * @returns Encoded result or null if encoding fails\n */\nexport function encodeCanvasOnMainThread(\n canvas: HTMLCanvasElement,\n canvasScale: number,\n): { dataUrl: string; preserveAlpha: boolean } | null {\n try {\n if (canvas.width === 0 || canvas.height === 0) {\n return null;\n }\n\n const preserveAlpha = canvas.dataset.preserveAlpha === \"true\";\n let dataUrl: string;\n\n if (canvasScale < 1) {\n // Scale down canvas before encoding\n const scaledWidth = Math.floor(canvas.width * canvasScale);\n const scaledHeight = Math.floor(canvas.height * canvasScale);\n const scaledCanvas = document.createElement(\"canvas\");\n scaledCanvas.width = scaledWidth;\n scaledCanvas.height = scaledHeight;\n const scaledCtx = scaledCanvas.getContext(\"2d\");\n if (scaledCtx) {\n scaledCtx.drawImage(canvas, 0, 0, scaledWidth, scaledHeight);\n const quality
|
|
1
|
+
{"version":3,"file":"mainThreadEncoder.js","names":["dataUrl: string"],"sources":["../../../src/preview/encoding/mainThreadEncoder.ts"],"sourcesContent":["/**\n * Main thread canvas encoding (fallback implementation).\n */\n\n// JPEG quality constants\nexport const JPEG_QUALITY_HIGH = 0.92;\nexport const JPEG_QUALITY_MEDIUM = 0.85;\n\n/**\n * Encode a single canvas to a data URL on the main thread (fallback).\n * @param canvas - The canvas to encode\n * @param canvasScale - Scale factor for encoding (default: 1)\n * @returns Encoded result or null if encoding fails\n */\nexport function encodeCanvasOnMainThread(\n canvas: HTMLCanvasElement,\n canvasScale: number,\n): { dataUrl: string; preserveAlpha: boolean } | null {\n try {\n if (canvas.width === 0 || canvas.height === 0) {\n return null;\n }\n\n const preserveAlpha = canvas.dataset.preserveAlpha === \"true\";\n let dataUrl: string;\n\n if (canvasScale < 1) {\n // Scale down canvas before encoding\n const scaledWidth = Math.floor(canvas.width * canvasScale);\n const scaledHeight = Math.floor(canvas.height * canvasScale);\n const scaledCanvas = document.createElement(\"canvas\");\n scaledCanvas.width = scaledWidth;\n scaledCanvas.height = scaledHeight;\n const scaledCtx = scaledCanvas.getContext(\"2d\");\n if (scaledCtx) {\n scaledCtx.drawImage(canvas, 0, 0, scaledWidth, scaledHeight);\n const quality =\n canvasScale < 0.5 ? JPEG_QUALITY_MEDIUM : JPEG_QUALITY_HIGH;\n dataUrl = preserveAlpha\n ? scaledCanvas.toDataURL(\"image/png\")\n : scaledCanvas.toDataURL(\"image/jpeg\", quality);\n } else {\n dataUrl = preserveAlpha\n ? canvas.toDataURL(\"image/png\")\n : canvas.toDataURL(\"image/jpeg\", JPEG_QUALITY_HIGH);\n }\n } else {\n dataUrl = preserveAlpha\n ? canvas.toDataURL(\"image/png\")\n : canvas.toDataURL(\"image/jpeg\", JPEG_QUALITY_HIGH);\n }\n\n return { dataUrl, preserveAlpha };\n } catch (e) {\n // Cross-origin canvas or other error - skip\n return null;\n }\n}\n"],"mappings":";;;;AAKA,MAAa,oBAAoB;AACjC,MAAa,sBAAsB;;;;;;;AAQnC,SAAgB,yBACd,QACA,aACoD;AACpD,KAAI;AACF,MAAI,OAAO,UAAU,KAAK,OAAO,WAAW,EAC1C,QAAO;EAGT,MAAM,gBAAgB,OAAO,QAAQ,kBAAkB;EACvD,IAAIA;AAEJ,MAAI,cAAc,GAAG;GAEnB,MAAM,cAAc,KAAK,MAAM,OAAO,QAAQ,YAAY;GAC1D,MAAM,eAAe,KAAK,MAAM,OAAO,SAAS,YAAY;GAC5D,MAAM,eAAe,SAAS,cAAc,SAAS;AACrD,gBAAa,QAAQ;AACrB,gBAAa,SAAS;GACtB,MAAM,YAAY,aAAa,WAAW,KAAK;AAC/C,OAAI,WAAW;AACb,cAAU,UAAU,QAAQ,GAAG,GAAG,aAAa,aAAa;IAC5D,MAAM,UACJ,cAAc,KAAM,sBAAsB;AAC5C,cAAU,gBACN,aAAa,UAAU,YAAY,GACnC,aAAa,UAAU,cAAc,QAAQ;SAEjD,WAAU,gBACN,OAAO,UAAU,YAAY,GAC7B,OAAO,UAAU,cAAc,kBAAkB;QAGvD,WAAU,gBACN,OAAO,UAAU,YAAY,GAC7B,OAAO,UAAU,cAAc,kBAAkB;AAGvD,SAAO;GAAE;GAAS;GAAe;UAC1B,GAAG;AAEV,SAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workerEncoder.js","names":["timeoutId: number | null"],"sources":["../../../src/preview/encoding/workerEncoder.ts"],"sourcesContent":["/**\n * Worker-based canvas encoding.\n */\n\nconst WORKER_TASK_TIMEOUT_MS = 30000;\n\n/**\n * Encode a canvas using a worker.\n * @param worker - The worker to use for encoding\n * @param canvas - The canvas to encode\n * @param preserveAlpha - Whether to preserve alpha channel (PNG vs JPEG)\n * @returns Promise resolving to the encoded data URL\n */\nexport async function encodeCanvasInWorker(\n worker: Worker,\n canvas: HTMLCanvasElement,\n preserveAlpha: boolean,\n): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n const taskId = `task-${Date.now()}-${Math.random()}-${performance.now()}`;\n let timeoutId: number | null = null;\n\n const cleanup = () => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n worker.removeEventListener(\"message\", messageHandler);\n worker.removeEventListener(\"messageerror\", messageErrorHandler);\n };\n\n const messageHandler = (event: MessageEvent) => {\n const result = event.data as {
|
|
1
|
+
{"version":3,"file":"workerEncoder.js","names":["timeoutId: number | null"],"sources":["../../../src/preview/encoding/workerEncoder.ts"],"sourcesContent":["/**\n * Worker-based canvas encoding.\n */\n\nconst WORKER_TASK_TIMEOUT_MS = 30000;\n\n/**\n * Encode a canvas using a worker.\n * @param worker - The worker to use for encoding\n * @param canvas - The canvas to encode\n * @param preserveAlpha - Whether to preserve alpha channel (PNG vs JPEG)\n * @returns Promise resolving to the encoded data URL\n */\nexport async function encodeCanvasInWorker(\n worker: Worker,\n canvas: HTMLCanvasElement,\n preserveAlpha: boolean,\n): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n const taskId = `task-${Date.now()}-${Math.random()}-${performance.now()}`;\n let timeoutId: number | null = null;\n\n const cleanup = () => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n worker.removeEventListener(\"message\", messageHandler);\n worker.removeEventListener(\"messageerror\", messageErrorHandler);\n };\n\n const messageHandler = (event: MessageEvent) => {\n const result = event.data as {\n taskId: string;\n dataUrl: string;\n error?: string;\n };\n if (result.taskId === taskId) {\n cleanup();\n if (result.error) {\n reject(new Error(`Worker encoding failed: ${result.error}`));\n } else {\n resolve(result.dataUrl);\n }\n }\n };\n\n const messageErrorHandler = () => {\n cleanup();\n reject(new Error(\"Worker message error\"));\n };\n\n worker.addEventListener(\"message\", messageHandler);\n worker.addEventListener(\"messageerror\", messageErrorHandler);\n\n // Set timeout to detect if worker never responds\n timeoutId = window.setTimeout(() => {\n cleanup();\n reject(new Error(\"Worker task timed out\"));\n }, WORKER_TASK_TIMEOUT_MS);\n\n // Create ImageBitmap from canvas\n createImageBitmap(canvas)\n .then((bitmap) => {\n // Transfer bitmap to worker (zero-copy)\n worker.postMessage(\n {\n taskId,\n bitmap,\n preserveAlpha,\n },\n [bitmap],\n );\n })\n .catch((error) => {\n cleanup();\n reject(error instanceof Error ? error : new Error(String(error)));\n });\n });\n}\n"],"mappings":";;;;AAIA,MAAM,yBAAyB;;;;;;;;AAS/B,eAAsB,qBACpB,QACA,QACA,eACiB;AACjB,QAAO,IAAI,SAAiB,SAAS,WAAW;EAC9C,MAAM,SAAS,QAAQ,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,GAAG,YAAY,KAAK;EACvE,IAAIA,YAA2B;EAE/B,MAAM,gBAAgB;AACpB,OAAI,cAAc,MAAM;AACtB,iBAAa,UAAU;AACvB,gBAAY;;AAEd,UAAO,oBAAoB,WAAW,eAAe;AACrD,UAAO,oBAAoB,gBAAgB,oBAAoB;;EAGjE,MAAM,kBAAkB,UAAwB;GAC9C,MAAM,SAAS,MAAM;AAKrB,OAAI,OAAO,WAAW,QAAQ;AAC5B,aAAS;AACT,QAAI,OAAO,MACT,wBAAO,IAAI,MAAM,2BAA2B,OAAO,QAAQ,CAAC;QAE5D,SAAQ,OAAO,QAAQ;;;EAK7B,MAAM,4BAA4B;AAChC,YAAS;AACT,0BAAO,IAAI,MAAM,uBAAuB,CAAC;;AAG3C,SAAO,iBAAiB,WAAW,eAAe;AAClD,SAAO,iBAAiB,gBAAgB,oBAAoB;AAG5D,cAAY,OAAO,iBAAiB;AAClC,YAAS;AACT,0BAAO,IAAI,MAAM,wBAAwB,CAAC;KACzC,uBAAuB;AAG1B,oBAAkB,OAAO,CACtB,MAAM,WAAW;AAEhB,UAAO,YACL;IACE;IACA;IACA;IACD,EACD,CAAC,OAAO,CACT;IACD,CACD,OAAO,UAAU;AAChB,YAAS;AACT,UAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC,CAAC;IACjE;GACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","names":["LOG_LEVELS: Record<LogLevel, number>"],"sources":["../../src/preview/logger.ts"],"sourcesContent":["/**\n * Centralized logging utility for the preview module.\n
|
|
1
|
+
{"version":3,"file":"logger.js","names":["LOG_LEVELS: Record<LogLevel, number>"],"sources":["../../src/preview/logger.ts"],"sourcesContent":["/**\n * Centralized logging utility for the preview module.\n *\n * Logging is disabled by default. To enable logging, set:\n * - Environment variable: EF_LOG_LEVEL=debug (or info, warn, error)\n * - Global flag: globalThis.EF_LOG_LEVEL = 'debug'\n *\n * Log levels (in order of verbosity):\n * - debug: All logs including performance metrics\n * - info: Informational messages\n * - warn: Warnings\n * - error: Errors only\n * - silent: No logging (default)\n */\n\ntype LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\" | \"silent\";\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n silent: 4,\n};\n\nfunction getLogLevel(): LogLevel {\n // Check global flag first\n if (typeof globalThis !== \"undefined\" && \"EF_LOG_LEVEL\" in globalThis) {\n const level = (globalThis as { EF_LOG_LEVEL?: string }).EF_LOG_LEVEL;\n if (level && level in LOG_LEVELS) {\n return level as LogLevel;\n }\n }\n\n // Check environment variable (works in Node.js and some bundlers)\n if (typeof process !== \"undefined\" && process.env?.EF_LOG_LEVEL) {\n const level = process.env.EF_LOG_LEVEL;\n if (level in LOG_LEVELS) {\n return level as LogLevel;\n }\n }\n\n // Default to silent\n return \"silent\";\n}\n\nfunction shouldLog(level: LogLevel): boolean {\n const currentLevel = getLogLevel();\n return LOG_LEVELS[level] >= LOG_LEVELS[currentLevel];\n}\n\nexport const logger = {\n debug: (...args: unknown[]): void => {\n if (shouldLog(\"debug\")) {\n console.log(...args);\n }\n },\n\n info: (...args: unknown[]): void => {\n if (shouldLog(\"info\")) {\n console.log(...args);\n }\n },\n\n warn: (...args: unknown[]): void => {\n if (shouldLog(\"warn\")) {\n console.warn(...args);\n }\n },\n\n error: (...args: unknown[]): void => {\n if (shouldLog(\"error\")) {\n console.error(...args);\n }\n },\n};\n"],"mappings":";AAiBA,MAAMA,aAAuC;CAC3C,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACP,QAAQ;CACT;AAED,SAAS,cAAwB;AAE/B,KAAI,OAAO,eAAe,eAAe,kBAAkB,YAAY;EACrE,MAAM,QAAS,WAAyC;AACxD,MAAI,SAAS,SAAS,WACpB,QAAO;;AAKX,KAAI,OAAO,YAAY,eAAe,QAAQ,KAAK,cAAc;EAC/D,MAAM,QAAQ,QAAQ,IAAI;AAC1B,MAAI,SAAS,WACX,QAAO;;AAKX,QAAO;;AAGT,SAAS,UAAU,OAA0B;CAC3C,MAAM,eAAe,aAAa;AAClC,QAAO,WAAW,UAAU,WAAW;;AAGzC,MAAa,SAAS;CACpB,QAAQ,GAAG,SAA0B;AACnC,MAAI,UAAU,QAAQ,CACpB,SAAQ,IAAI,GAAG,KAAK;;CAIxB,OAAO,GAAG,SAA0B;AAClC,MAAI,UAAU,OAAO,CACnB,SAAQ,IAAI,GAAG,KAAK;;CAIxB,OAAO,GAAG,SAA0B;AAClC,MAAI,UAAU,OAAO,CACnB,SAAQ,KAAK,GAAG,KAAK;;CAIzB,QAAQ,GAAG,SAA0B;AACnC,MAAI,UAAU,QAAQ,CACpB,SAAQ,MAAM,GAAG,KAAK;;CAG3B"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/preview/previewSettings.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Preview settings module with localStorage persistence.
|
|
4
|
+
* Manages configuration for the preview rendering system.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Render mode for HTML-to-canvas capture operations.
|
|
8
|
+
* - "foreignObject": SVG foreignObject serialization (fallback, works everywhere)
|
|
9
|
+
* - "native": Chrome's experimental drawElementImage API (fastest when available)
|
|
10
|
+
*/
|
|
11
|
+
type RenderMode = "foreignObject" | "native";
|
|
12
|
+
/**
|
|
13
|
+
* Preview resolution scale factor.
|
|
14
|
+
* Controls how much to reduce the preview render resolution for better performance.
|
|
15
|
+
* - 1: Full resolution (default)
|
|
16
|
+
* - 0.75: 3/4 resolution
|
|
17
|
+
* - 0.5: Half resolution
|
|
18
|
+
* - 0.25: Quarter resolution
|
|
19
|
+
* - "auto": Adaptive resolution that scales down during motion to prevent dropped frames,
|
|
20
|
+
* and renders at full resolution when at rest
|
|
21
|
+
*/
|
|
22
|
+
type PreviewResolutionScale = 1 | 0.75 | 0.5 | 0.25 | "auto";
|
|
23
|
+
/**
|
|
24
|
+
* Preview presentation mode determines how content is rendered in the workbench.
|
|
25
|
+
* - "clone": Show a clone with computed styles applied (alias for "computed")
|
|
26
|
+
* - "dom": Show the original DOM content directly (alias for "original")
|
|
27
|
+
* - "original": Show the original DOM content directly
|
|
28
|
+
* - "computed": Show a clone with computed styles applied
|
|
29
|
+
* - "canvas": Render to canvas using the active rendering path
|
|
30
|
+
*/
|
|
31
|
+
type PreviewPresentationMode = "dom" | "canvas";
|
|
32
|
+
//#endregion
|
|
33
|
+
export { PreviewPresentationMode, PreviewResolutionScale, RenderMode };
|
|
34
|
+
//# sourceMappingURL=previewSettings.d.ts.map
|
|
@@ -3,6 +3,7 @@ const STORAGE_KEY_PRESENTATION_MODE = "ef-preview-presentation-mode";
|
|
|
3
3
|
const STORAGE_KEY_RENDER_MODE = "ef-preview-render-mode";
|
|
4
4
|
const STORAGE_KEY_RESOLUTION_SCALE = "ef-preview-resolution-scale";
|
|
5
5
|
const STORAGE_KEY_SHOW_STATS = "ef-preview-show-stats";
|
|
6
|
+
const STORAGE_KEY_SHOW_THUMBNAIL_TIMESTAMPS = "ef-preview-show-thumbnail-timestamps";
|
|
6
7
|
/**
|
|
7
8
|
* Cached detection result for native HTML-in-Canvas API availability.
|
|
8
9
|
* This is separate from the user preference - it detects browser capability.
|
|
@@ -11,7 +12,7 @@ let _nativeApiAvailable = null;
|
|
|
11
12
|
/**
|
|
12
13
|
* Detect if the native HTML-in-Canvas API (drawElementImage) is available in this browser.
|
|
13
14
|
* This checks browser capability, not user preference.
|
|
14
|
-
*
|
|
15
|
+
*
|
|
15
16
|
* The API is available in Chrome Canary with chrome://flags/#canvas-draw-element
|
|
16
17
|
* @see https://github.com/WICG/html-in-canvas
|
|
17
18
|
*/
|
|
@@ -24,15 +25,15 @@ function isNativeCanvasApiAvailable() {
|
|
|
24
25
|
}
|
|
25
26
|
/**
|
|
26
27
|
* Get the current preview presentation mode.
|
|
27
|
-
* Defaults to "
|
|
28
|
+
* Defaults to "dom" if not set.
|
|
28
29
|
*/
|
|
29
30
|
function getPreviewPresentationMode() {
|
|
30
31
|
try {
|
|
31
32
|
const stored = localStorage.getItem(STORAGE_KEY_PRESENTATION_MODE);
|
|
32
|
-
if (stored === "
|
|
33
|
-
return "
|
|
33
|
+
if (stored === "dom" || stored === "canvas") return stored;
|
|
34
|
+
return "dom";
|
|
34
35
|
} catch {
|
|
35
|
-
return "
|
|
36
|
+
return "dom";
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
/**
|
|
@@ -48,7 +49,7 @@ function setPreviewPresentationMode(mode) {
|
|
|
48
49
|
/**
|
|
49
50
|
* Get the current render mode for HTML-to-canvas capture.
|
|
50
51
|
* Defaults to "native" if available, otherwise "foreignObject".
|
|
51
|
-
*
|
|
52
|
+
*
|
|
52
53
|
* Checks EF_NATIVE_RENDER URL parameter to force native mode when set.
|
|
53
54
|
*/
|
|
54
55
|
function getRenderMode() {
|
|
@@ -100,16 +101,6 @@ function getPreviewResolutionScale() {
|
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
/**
|
|
103
|
-
* Set the preview resolution scale.
|
|
104
|
-
* Persists to localStorage and dispatches a change event.
|
|
105
|
-
*/
|
|
106
|
-
function setPreviewResolutionScale(scale) {
|
|
107
|
-
try {
|
|
108
|
-
localStorage.setItem(STORAGE_KEY_RESOLUTION_SCALE, String(scale));
|
|
109
|
-
} catch {}
|
|
110
|
-
window.dispatchEvent(new CustomEvent("ef-preview-settings-changed", { detail: { resolutionScale: scale } }));
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
104
|
* Get whether performance stats should be shown.
|
|
114
105
|
* Defaults to false (stats hidden by default).
|
|
115
106
|
*/
|
|
@@ -130,7 +121,28 @@ function setShowStats(enabled) {
|
|
|
130
121
|
} catch {}
|
|
131
122
|
window.dispatchEvent(new CustomEvent("ef-preview-settings-changed", { detail: { showStats: enabled } }));
|
|
132
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Get whether thumbnail timestamps should be shown.
|
|
126
|
+
* Defaults to false (timestamps hidden by default).
|
|
127
|
+
*/
|
|
128
|
+
function getShowThumbnailTimestamps() {
|
|
129
|
+
try {
|
|
130
|
+
return localStorage.getItem(STORAGE_KEY_SHOW_THUMBNAIL_TIMESTAMPS) === "true";
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Set whether thumbnail timestamps should be shown.
|
|
137
|
+
* Persists to localStorage and dispatches a change event.
|
|
138
|
+
*/
|
|
139
|
+
function setShowThumbnailTimestamps(enabled) {
|
|
140
|
+
try {
|
|
141
|
+
localStorage.setItem(STORAGE_KEY_SHOW_THUMBNAIL_TIMESTAMPS, String(enabled));
|
|
142
|
+
} catch {}
|
|
143
|
+
window.dispatchEvent(new CustomEvent("ef-preview-settings-changed", { detail: { showThumbnailTimestamps: enabled } }));
|
|
144
|
+
}
|
|
133
145
|
|
|
134
146
|
//#endregion
|
|
135
|
-
export { getPreviewPresentationMode, getPreviewResolutionScale, getRenderMode, getShowStats, isNativeCanvasApiAvailable, setPreviewPresentationMode,
|
|
147
|
+
export { getPreviewPresentationMode, getPreviewResolutionScale, getRenderMode, getShowStats, getShowThumbnailTimestamps, isNativeCanvasApiAvailable, setPreviewPresentationMode, setRenderMode, setShowStats, setShowThumbnailTimestamps };
|
|
136
148
|
//# sourceMappingURL=previewSettings.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"previewSettings.js","names":["_nativeApiAvailable: boolean | null","VALID_NUMERIC_SCALES: number[]"],"sources":["../../src/preview/previewSettings.ts"],"sourcesContent":["/**\n * Preview settings module with localStorage persistence.\n * Manages configuration for the preview rendering system.\n */\n\nconst STORAGE_KEY_NATIVE_CANVAS_API = \"ef-preview-native-canvas-api-enabled\";\nconst STORAGE_KEY_PRESENTATION_MODE = \"ef-preview-presentation-mode\";\nconst STORAGE_KEY_RENDER_MODE = \"ef-preview-render-mode\";\nconst STORAGE_KEY_RESOLUTION_SCALE = \"ef-preview-resolution-scale\";\nconst STORAGE_KEY_SHOW_STATS = \"ef-preview-show-stats\";\n\n/**\n * Render mode for HTML-to-canvas capture operations.\n * - \"foreignObject\": SVG foreignObject serialization (fallback, works everywhere)\n * - \"native\": Chrome's experimental drawElementImage API (fastest when available)\n */\nexport type RenderMode = \"foreignObject\" | \"native\";\n\n/**\n * Preview resolution scale factor.\n * Controls how much to reduce the preview render resolution for better performance.\n * - 1: Full resolution (default)\n * - 0.75: 3/4 resolution\n * - 0.5: Half resolution\n * - 0.25: Quarter resolution\n * - \"auto\": Adaptive resolution that scales down during motion to prevent dropped frames,\n * and renders at full resolution when at rest\n */\nexport type PreviewResolutionScale = 1 | 0.75 | 0.5 | 0.25 | \"auto\";\n\n/**\n * Preview presentation mode determines how content is rendered in the workbench.\n * - \"clone\": Show a clone with computed styles applied (alias for \"computed\")\n * - \"dom\": Show the original DOM content directly (alias for \"original\")\n * - \"original\": Show the original DOM content directly\n * - \"computed\": Show a clone with computed styles applied\n * - \"canvas\": Render to canvas using the active rendering path\n */\nexport type PreviewPresentationMode = \"original\" | \"computed\" | \"canvas\" | \"clone\" | \"dom\";\n\n/**\n * Cached detection result for native HTML-in-Canvas API availability.\n * This is separate from the user preference - it detects browser capability.\n */\nlet _nativeApiAvailable: boolean | null = null;\n\n/**\n * Detect if the native HTML-in-Canvas API (drawElementImage) is available in this browser.\n * This checks browser capability, not user preference.\n * \n * The API is available in Chrome Canary with chrome://flags/#canvas-draw-element\n * @see https://github.com/WICG/html-in-canvas\n */\nexport function isNativeCanvasApiAvailable(): boolean {\n if (_nativeApiAvailable === null) {\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\");\n _nativeApiAvailable = ctx !== null && \"drawElementImage\" in ctx;\n }\n return _nativeApiAvailable;\n}\n\n/**\n * Check if the native Canvas API is enabled by the user.\n * Returns true only if:\n * 1. The API is available in the browser\n * 2. The user has not explicitly disabled it\n * \n * Default is enabled when available (opt-out model).\n */\nexport function isNativeCanvasApiEnabled(): boolean {\n if (!isNativeCanvasApiAvailable()) {\n return false;\n }\n \n try {\n const stored = localStorage.getItem(STORAGE_KEY_NATIVE_CANVAS_API);\n // Default to true (enabled) when available, unless explicitly disabled\n if (stored === null) {\n return true;\n }\n return stored === \"true\";\n } catch {\n // localStorage not available (e.g., private browsing)\n return true;\n }\n}\n\n/**\n * Set whether the native Canvas API should be used (when available).\n * Persists to localStorage and dispatches a change event.\n */\nexport function setNativeCanvasApiEnabled(enabled: boolean): void {\n try {\n localStorage.setItem(STORAGE_KEY_NATIVE_CANVAS_API, String(enabled));\n } catch {\n // localStorage not available\n }\n \n // Dispatch event so components can react to the change\n window.dispatchEvent(new CustomEvent(\"ef-preview-settings-changed\", {\n detail: { nativeCanvasApiEnabled: enabled }\n }));\n}\n\n/**\n * Get the current raw user preference (ignoring availability).\n * Returns null if no preference is set.\n */\nexport function getNativeCanvasApiPreference(): boolean | null {\n try {\n const stored = localStorage.getItem(STORAGE_KEY_NATIVE_CANVAS_API);\n if (stored === null) {\n return null;\n }\n return stored === \"true\";\n } catch {\n return null;\n }\n}\n\n/**\n * Subscribe to preview settings changes.\n * @returns Unsubscribe function\n */\nexport function onPreviewSettingsChanged(\n callback: (detail: PreviewSettingsChangedDetail) => void\n): () => void {\n const handler = (event: Event) => {\n callback((event as CustomEvent).detail);\n };\n window.addEventListener(\"ef-preview-settings-changed\", handler);\n return () => window.removeEventListener(\"ef-preview-settings-changed\", handler);\n}\n\n/**\n * Detail object for preview settings change events.\n */\nexport interface PreviewSettingsChangedDetail {\n nativeCanvasApiEnabled?: boolean;\n presentationMode?: PreviewPresentationMode;\n renderMode?: RenderMode;\n resolutionScale?: PreviewResolutionScale;\n showStats?: boolean;\n}\n\n/**\n * Get the current preview presentation mode.\n * Defaults to \"original\" if not set.\n */\nexport function getPreviewPresentationMode(): PreviewPresentationMode {\n try {\n const stored = localStorage.getItem(STORAGE_KEY_PRESENTATION_MODE);\n if (stored === \"original\" || stored === \"computed\" || stored === \"canvas\") {\n return stored;\n }\n return \"original\";\n } catch {\n return \"original\";\n }\n}\n\n/**\n * Set the preview presentation mode.\n * Persists to localStorage and dispatches a change event.\n */\nexport function setPreviewPresentationMode(mode: PreviewPresentationMode): void {\n try {\n localStorage.setItem(STORAGE_KEY_PRESENTATION_MODE, mode);\n } catch {\n // localStorage not available\n }\n \n // Dispatch event so components can react to the change\n window.dispatchEvent(new CustomEvent(\"ef-preview-settings-changed\", {\n detail: { presentationMode: mode }\n }));\n}\n\n/**\n * Get the current render mode for HTML-to-canvas capture.\n * Defaults to \"native\" if available, otherwise \"foreignObject\".\n * \n * Checks EF_NATIVE_RENDER URL parameter to force native mode when set.\n */\nexport function getRenderMode(): RenderMode {\n // Check URL parameter first (CLI flag override)\n try {\n const urlParams = new URLSearchParams(window.location.search);\n if (urlParams.get(\"EF_NATIVE_RENDER\") === \"1\") {\n // Force native mode if available, otherwise fall back to foreignObject\n return isNativeCanvasApiAvailable() ? \"native\" : \"foreignObject\";\n }\n } catch {\n // URL parsing failed, continue with normal logic\n }\n\n try {\n const stored = localStorage.getItem(STORAGE_KEY_RENDER_MODE);\n if (stored === \"foreignObject\" || stored === \"native\") {\n return stored;\n }\n // Default: prefer native if available, otherwise foreignObject\n return isNativeCanvasApiAvailable() ? \"native\" : \"foreignObject\";\n } catch {\n return isNativeCanvasApiAvailable() ? \"native\" : \"foreignObject\";\n }\n}\n\n/**\n * Set the render mode for HTML-to-canvas capture.\n * Persists to localStorage and dispatches a change event.\n */\nexport function setRenderMode(mode: RenderMode): void {\n try {\n localStorage.setItem(STORAGE_KEY_RENDER_MODE, mode);\n } catch {\n // localStorage not available\n }\n \n // Dispatch event so components can react to the change\n window.dispatchEvent(new CustomEvent(\"ef-preview-settings-changed\", {\n detail: { renderMode: mode }\n }));\n}\n\n/**\n * Valid numeric resolution scale values.\n */\nconst VALID_NUMERIC_SCALES: number[] = [1, 0.75, 0.5, 0.25];\n\n/**\n * Get the current preview resolution scale.\n * Defaults to 1 (full resolution) if not set.\n */\nexport function getPreviewResolutionScale(): PreviewResolutionScale {\n try {\n const stored = localStorage.getItem(STORAGE_KEY_RESOLUTION_SCALE);\n if (stored !== null) {\n // Check for \"auto\" string first\n if (stored === \"auto\") {\n return \"auto\";\n }\n // Then check numeric values\n const parsed = parseFloat(stored);\n if (VALID_NUMERIC_SCALES.includes(parsed)) {\n return parsed as PreviewResolutionScale;\n }\n }\n return 1;\n } catch {\n return 1;\n }\n}\n\n/**\n * Set the preview resolution scale.\n * Persists to localStorage and dispatches a change event.\n */\nexport function setPreviewResolutionScale(scale: PreviewResolutionScale): void {\n try {\n localStorage.setItem(STORAGE_KEY_RESOLUTION_SCALE, String(scale));\n } catch {\n // localStorage not available\n }\n \n // Dispatch event so components can react to the change\n window.dispatchEvent(new CustomEvent(\"ef-preview-settings-changed\", {\n detail: { resolutionScale: scale }\n }));\n}\n\n/**\n * Get whether performance stats should be shown.\n * Defaults to false (stats hidden by default).\n */\nexport function getShowStats(): boolean {\n try {\n const stored = localStorage.getItem(STORAGE_KEY_SHOW_STATS);\n return stored === \"true\";\n } catch {\n return false;\n }\n}\n\n/**\n * Set whether performance stats should be shown.\n * Persists to localStorage and dispatches a change event.\n */\nexport function setShowStats(enabled: boolean): void {\n try {\n localStorage.setItem(STORAGE_KEY_SHOW_STATS, String(enabled));\n } catch {\n // localStorage not available\n }\n \n // Dispatch event so components can react to the change\n window.dispatchEvent(new CustomEvent(\"ef-preview-settings-changed\", {\n detail: { showStats: enabled }\n }));\n}\n\n"],"mappings":";AAMA,MAAM,gCAAgC;AACtC,MAAM,0BAA0B;AAChC,MAAM,+BAA+B;AACrC,MAAM,yBAAyB;;;;;AAmC/B,IAAIA,sBAAsC;;;;;;;;AAS1C,SAAgB,6BAAsC;AACpD,KAAI,wBAAwB,MAAM;EAEhC,MAAM,MADS,SAAS,cAAc,SAAS,CAC5B,WAAW,KAAK;AACnC,wBAAsB,QAAQ,QAAQ,sBAAsB;;AAE9D,QAAO;;;;;;AA2FT,SAAgB,6BAAsD;AACpE,KAAI;EACF,MAAM,SAAS,aAAa,QAAQ,8BAA8B;AAClE,MAAI,WAAW,cAAc,WAAW,cAAc,WAAW,SAC/D,QAAO;AAET,SAAO;SACD;AACN,SAAO;;;;;;;AAQX,SAAgB,2BAA2B,MAAqC;AAC9E,KAAI;AACF,eAAa,QAAQ,+BAA+B,KAAK;SACnD;AAKR,QAAO,cAAc,IAAI,YAAY,+BAA+B,EAClE,QAAQ,EAAE,kBAAkB,MAAM,EACnC,CAAC,CAAC;;;;;;;;AASL,SAAgB,gBAA4B;AAE1C,KAAI;AAEF,MADkB,IAAI,gBAAgB,OAAO,SAAS,OAAO,CAC/C,IAAI,mBAAmB,KAAK,IAExC,QAAO,4BAA4B,GAAG,WAAW;SAE7C;AAIR,KAAI;EACF,MAAM,SAAS,aAAa,QAAQ,wBAAwB;AAC5D,MAAI,WAAW,mBAAmB,WAAW,SAC3C,QAAO;AAGT,SAAO,4BAA4B,GAAG,WAAW;SAC3C;AACN,SAAO,4BAA4B,GAAG,WAAW;;;;;;;AAQrD,SAAgB,cAAc,MAAwB;AACpD,KAAI;AACF,eAAa,QAAQ,yBAAyB,KAAK;SAC7C;AAKR,QAAO,cAAc,IAAI,YAAY,+BAA+B,EAClE,QAAQ,EAAE,YAAY,MAAM,EAC7B,CAAC,CAAC;;;;;AAML,MAAMC,uBAAiC;CAAC;CAAG;CAAM;CAAK;CAAK;;;;;AAM3D,SAAgB,4BAAoD;AAClE,KAAI;EACF,MAAM,SAAS,aAAa,QAAQ,6BAA6B;AACjE,MAAI,WAAW,MAAM;AAEnB,OAAI,WAAW,OACb,QAAO;GAGT,MAAM,SAAS,WAAW,OAAO;AACjC,OAAI,qBAAqB,SAAS,OAAO,CACvC,QAAO;;AAGX,SAAO;SACD;AACN,SAAO;;;;;;;AAQX,SAAgB,0BAA0B,OAAqC;AAC7E,KAAI;AACF,eAAa,QAAQ,8BAA8B,OAAO,MAAM,CAAC;SAC3D;AAKR,QAAO,cAAc,IAAI,YAAY,+BAA+B,EAClE,QAAQ,EAAE,iBAAiB,OAAO,EACnC,CAAC,CAAC;;;;;;AAOL,SAAgB,eAAwB;AACtC,KAAI;AAEF,SADe,aAAa,QAAQ,uBAAuB,KACzC;SACZ;AACN,SAAO;;;;;;;AAQX,SAAgB,aAAa,SAAwB;AACnD,KAAI;AACF,eAAa,QAAQ,wBAAwB,OAAO,QAAQ,CAAC;SACvD;AAKR,QAAO,cAAc,IAAI,YAAY,+BAA+B,EAClE,QAAQ,EAAE,WAAW,SAAS,EAC/B,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"previewSettings.js","names":["_nativeApiAvailable: boolean | null","VALID_NUMERIC_SCALES: number[]"],"sources":["../../src/preview/previewSettings.ts"],"sourcesContent":["/**\n * Preview settings module with localStorage persistence.\n * Manages configuration for the preview rendering system.\n */\n\nconst STORAGE_KEY_NATIVE_CANVAS_API = \"ef-preview-native-canvas-api-enabled\";\nconst STORAGE_KEY_PRESENTATION_MODE = \"ef-preview-presentation-mode\";\nconst STORAGE_KEY_RENDER_MODE = \"ef-preview-render-mode\";\nconst STORAGE_KEY_RESOLUTION_SCALE = \"ef-preview-resolution-scale\";\nconst STORAGE_KEY_SHOW_STATS = \"ef-preview-show-stats\";\nconst STORAGE_KEY_SHOW_THUMBNAIL_TIMESTAMPS =\n \"ef-preview-show-thumbnail-timestamps\";\n\n/**\n * Render mode for HTML-to-canvas capture operations.\n * - \"foreignObject\": SVG foreignObject serialization (fallback, works everywhere)\n * - \"native\": Chrome's experimental drawElementImage API (fastest when available)\n */\nexport type RenderMode = \"foreignObject\" | \"native\";\n\n/**\n * Preview resolution scale factor.\n * Controls how much to reduce the preview render resolution for better performance.\n * - 1: Full resolution (default)\n * - 0.75: 3/4 resolution\n * - 0.5: Half resolution\n * - 0.25: Quarter resolution\n * - \"auto\": Adaptive resolution that scales down during motion to prevent dropped frames,\n * and renders at full resolution when at rest\n */\nexport type PreviewResolutionScale = 1 | 0.75 | 0.5 | 0.25 | \"auto\";\n\n/**\n * Preview presentation mode determines how content is rendered in the workbench.\n * - \"clone\": Show a clone with computed styles applied (alias for \"computed\")\n * - \"dom\": Show the original DOM content directly (alias for \"original\")\n * - \"original\": Show the original DOM content directly\n * - \"computed\": Show a clone with computed styles applied\n * - \"canvas\": Render to canvas using the active rendering path\n */\nexport type PreviewPresentationMode = \"dom\" | \"canvas\";\n\n/**\n * Cached detection result for native HTML-in-Canvas API availability.\n * This is separate from the user preference - it detects browser capability.\n */\nlet _nativeApiAvailable: boolean | null = null;\n\n/**\n * Detect if the native HTML-in-Canvas API (drawElementImage) is available in this browser.\n * This checks browser capability, not user preference.\n *\n * The API is available in Chrome Canary with chrome://flags/#canvas-draw-element\n * @see https://github.com/WICG/html-in-canvas\n */\nexport function isNativeCanvasApiAvailable(): boolean {\n if (_nativeApiAvailable === null) {\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\");\n _nativeApiAvailable = ctx !== null && \"drawElementImage\" in ctx;\n }\n return _nativeApiAvailable;\n}\n\n/**\n * Check if the native Canvas API is enabled by the user.\n * Returns true only if:\n * 1. The API is available in the browser\n * 2. The user has not explicitly disabled it\n *\n * Default is enabled when available (opt-out model).\n */\nexport function isNativeCanvasApiEnabled(): boolean {\n if (!isNativeCanvasApiAvailable()) {\n return false;\n }\n\n try {\n const stored = localStorage.getItem(STORAGE_KEY_NATIVE_CANVAS_API);\n // Default to true (enabled) when available, unless explicitly disabled\n if (stored === null) {\n return true;\n }\n return stored === \"true\";\n } catch {\n // localStorage not available (e.g., private browsing)\n return true;\n }\n}\n\n/**\n * Set whether the native Canvas API should be used (when available).\n * Persists to localStorage and dispatches a change event.\n */\nexport function setNativeCanvasApiEnabled(enabled: boolean): void {\n try {\n localStorage.setItem(STORAGE_KEY_NATIVE_CANVAS_API, String(enabled));\n } catch {\n // localStorage not available\n }\n\n // Dispatch event so components can react to the change\n window.dispatchEvent(\n new CustomEvent(\"ef-preview-settings-changed\", {\n detail: { nativeCanvasApiEnabled: enabled },\n }),\n );\n}\n\n/**\n * Get the current raw user preference (ignoring availability).\n * Returns null if no preference is set.\n */\nexport function getNativeCanvasApiPreference(): boolean | null {\n try {\n const stored = localStorage.getItem(STORAGE_KEY_NATIVE_CANVAS_API);\n if (stored === null) {\n return null;\n }\n return stored === \"true\";\n } catch {\n return null;\n }\n}\n\n/**\n * Subscribe to preview settings changes.\n * @returns Unsubscribe function\n */\nexport function onPreviewSettingsChanged(\n callback: (detail: PreviewSettingsChangedDetail) => void,\n): () => void {\n const handler = (event: Event) => {\n callback((event as CustomEvent).detail);\n };\n window.addEventListener(\"ef-preview-settings-changed\", handler);\n return () =>\n window.removeEventListener(\"ef-preview-settings-changed\", handler);\n}\n\n/**\n * Detail object for preview settings change events.\n */\nexport interface PreviewSettingsChangedDetail {\n nativeCanvasApiEnabled?: boolean;\n presentationMode?: PreviewPresentationMode;\n renderMode?: RenderMode;\n resolutionScale?: PreviewResolutionScale;\n showStats?: boolean;\n showThumbnailTimestamps?: boolean;\n}\n\n/**\n * Get the current preview presentation mode.\n * Defaults to \"dom\" if not set.\n */\nexport function getPreviewPresentationMode(): PreviewPresentationMode {\n try {\n const stored = localStorage.getItem(STORAGE_KEY_PRESENTATION_MODE);\n if (stored === \"dom\" || stored === \"canvas\") {\n return stored;\n }\n return \"dom\";\n } catch {\n return \"dom\";\n }\n}\n\n/**\n * Set the preview presentation mode.\n * Persists to localStorage and dispatches a change event.\n */\nexport function setPreviewPresentationMode(\n mode: PreviewPresentationMode,\n): void {\n try {\n localStorage.setItem(STORAGE_KEY_PRESENTATION_MODE, mode);\n } catch {\n // localStorage not available\n }\n\n // Dispatch event so components can react to the change\n window.dispatchEvent(\n new CustomEvent(\"ef-preview-settings-changed\", {\n detail: { presentationMode: mode },\n }),\n );\n}\n\n/**\n * Get the current render mode for HTML-to-canvas capture.\n * Defaults to \"native\" if available, otherwise \"foreignObject\".\n *\n * Checks EF_NATIVE_RENDER URL parameter to force native mode when set.\n */\nexport function getRenderMode(): RenderMode {\n // Check URL parameter first (CLI flag override)\n try {\n const urlParams = new URLSearchParams(window.location.search);\n if (urlParams.get(\"EF_NATIVE_RENDER\") === \"1\") {\n // Force native mode if available, otherwise fall back to foreignObject\n return isNativeCanvasApiAvailable() ? \"native\" : \"foreignObject\";\n }\n } catch {\n // URL parsing failed, continue with normal logic\n }\n\n try {\n const stored = localStorage.getItem(STORAGE_KEY_RENDER_MODE);\n if (stored === \"foreignObject\" || stored === \"native\") {\n return stored;\n }\n // Default: prefer native if available, otherwise foreignObject\n return isNativeCanvasApiAvailable() ? \"native\" : \"foreignObject\";\n } catch {\n return isNativeCanvasApiAvailable() ? \"native\" : \"foreignObject\";\n }\n}\n\n/**\n * Set the render mode for HTML-to-canvas capture.\n * Persists to localStorage and dispatches a change event.\n */\nexport function setRenderMode(mode: RenderMode): void {\n try {\n localStorage.setItem(STORAGE_KEY_RENDER_MODE, mode);\n } catch {\n // localStorage not available\n }\n\n // Dispatch event so components can react to the change\n window.dispatchEvent(\n new CustomEvent(\"ef-preview-settings-changed\", {\n detail: { renderMode: mode },\n }),\n );\n}\n\n/**\n * Valid numeric resolution scale values.\n */\nconst VALID_NUMERIC_SCALES: number[] = [1, 0.75, 0.5, 0.25];\n\n/**\n * Get the current preview resolution scale.\n * Defaults to 1 (full resolution) if not set.\n */\nexport function getPreviewResolutionScale(): PreviewResolutionScale {\n try {\n const stored = localStorage.getItem(STORAGE_KEY_RESOLUTION_SCALE);\n if (stored !== null) {\n // Check for \"auto\" string first\n if (stored === \"auto\") {\n return \"auto\";\n }\n // Then check numeric values\n const parsed = parseFloat(stored);\n if (VALID_NUMERIC_SCALES.includes(parsed)) {\n return parsed as PreviewResolutionScale;\n }\n }\n return 1;\n } catch {\n return 1;\n }\n}\n\n/**\n * Set the preview resolution scale.\n * Persists to localStorage and dispatches a change event.\n */\nexport function setPreviewResolutionScale(scale: PreviewResolutionScale): void {\n try {\n localStorage.setItem(STORAGE_KEY_RESOLUTION_SCALE, String(scale));\n } catch {\n // localStorage not available\n }\n\n // Dispatch event so components can react to the change\n window.dispatchEvent(\n new CustomEvent(\"ef-preview-settings-changed\", {\n detail: { resolutionScale: scale },\n }),\n );\n}\n\n/**\n * Get whether performance stats should be shown.\n * Defaults to false (stats hidden by default).\n */\nexport function getShowStats(): boolean {\n try {\n const stored = localStorage.getItem(STORAGE_KEY_SHOW_STATS);\n return stored === \"true\";\n } catch {\n return false;\n }\n}\n\n/**\n * Set whether performance stats should be shown.\n * Persists to localStorage and dispatches a change event.\n */\nexport function setShowStats(enabled: boolean): void {\n try {\n localStorage.setItem(STORAGE_KEY_SHOW_STATS, String(enabled));\n } catch {\n // localStorage not available\n }\n\n // Dispatch event so components can react to the change\n window.dispatchEvent(\n new CustomEvent(\"ef-preview-settings-changed\", {\n detail: { showStats: enabled },\n }),\n );\n}\n\n/**\n * Get whether thumbnail timestamps should be shown.\n * Defaults to false (timestamps hidden by default).\n */\nexport function getShowThumbnailTimestamps(): boolean {\n try {\n const stored = localStorage.getItem(STORAGE_KEY_SHOW_THUMBNAIL_TIMESTAMPS);\n return stored === \"true\";\n } catch {\n return false;\n }\n}\n\n/**\n * Set whether thumbnail timestamps should be shown.\n * Persists to localStorage and dispatches a change event.\n */\nexport function setShowThumbnailTimestamps(enabled: boolean): void {\n try {\n localStorage.setItem(\n STORAGE_KEY_SHOW_THUMBNAIL_TIMESTAMPS,\n String(enabled),\n );\n } catch {\n // localStorage not available\n }\n\n // Dispatch event so components can react to the change\n window.dispatchEvent(\n new CustomEvent(\"ef-preview-settings-changed\", {\n detail: { showThumbnailTimestamps: enabled },\n }),\n );\n}\n"],"mappings":";AAMA,MAAM,gCAAgC;AACtC,MAAM,0BAA0B;AAChC,MAAM,+BAA+B;AACrC,MAAM,yBAAyB;AAC/B,MAAM,wCACJ;;;;;AAmCF,IAAIA,sBAAsC;;;;;;;;AAS1C,SAAgB,6BAAsC;AACpD,KAAI,wBAAwB,MAAM;EAEhC,MAAM,MADS,SAAS,cAAc,SAAS,CAC5B,WAAW,KAAK;AACnC,wBAAsB,QAAQ,QAAQ,sBAAsB;;AAE9D,QAAO;;;;;;AA+FT,SAAgB,6BAAsD;AACpE,KAAI;EACF,MAAM,SAAS,aAAa,QAAQ,8BAA8B;AAClE,MAAI,WAAW,SAAS,WAAW,SACjC,QAAO;AAET,SAAO;SACD;AACN,SAAO;;;;;;;AAQX,SAAgB,2BACd,MACM;AACN,KAAI;AACF,eAAa,QAAQ,+BAA+B,KAAK;SACnD;AAKR,QAAO,cACL,IAAI,YAAY,+BAA+B,EAC7C,QAAQ,EAAE,kBAAkB,MAAM,EACnC,CAAC,CACH;;;;;;;;AASH,SAAgB,gBAA4B;AAE1C,KAAI;AAEF,MADkB,IAAI,gBAAgB,OAAO,SAAS,OAAO,CAC/C,IAAI,mBAAmB,KAAK,IAExC,QAAO,4BAA4B,GAAG,WAAW;SAE7C;AAIR,KAAI;EACF,MAAM,SAAS,aAAa,QAAQ,wBAAwB;AAC5D,MAAI,WAAW,mBAAmB,WAAW,SAC3C,QAAO;AAGT,SAAO,4BAA4B,GAAG,WAAW;SAC3C;AACN,SAAO,4BAA4B,GAAG,WAAW;;;;;;;AAQrD,SAAgB,cAAc,MAAwB;AACpD,KAAI;AACF,eAAa,QAAQ,yBAAyB,KAAK;SAC7C;AAKR,QAAO,cACL,IAAI,YAAY,+BAA+B,EAC7C,QAAQ,EAAE,YAAY,MAAM,EAC7B,CAAC,CACH;;;;;AAMH,MAAMC,uBAAiC;CAAC;CAAG;CAAM;CAAK;CAAK;;;;;AAM3D,SAAgB,4BAAoD;AAClE,KAAI;EACF,MAAM,SAAS,aAAa,QAAQ,6BAA6B;AACjE,MAAI,WAAW,MAAM;AAEnB,OAAI,WAAW,OACb,QAAO;GAGT,MAAM,SAAS,WAAW,OAAO;AACjC,OAAI,qBAAqB,SAAS,OAAO,CACvC,QAAO;;AAGX,SAAO;SACD;AACN,SAAO;;;;;;;AA2BX,SAAgB,eAAwB;AACtC,KAAI;AAEF,SADe,aAAa,QAAQ,uBAAuB,KACzC;SACZ;AACN,SAAO;;;;;;;AAQX,SAAgB,aAAa,SAAwB;AACnD,KAAI;AACF,eAAa,QAAQ,wBAAwB,OAAO,QAAQ,CAAC;SACvD;AAKR,QAAO,cACL,IAAI,YAAY,+BAA+B,EAC7C,QAAQ,EAAE,WAAW,SAAS,EAC/B,CAAC,CACH;;;;;;AAOH,SAAgB,6BAAsC;AACpD,KAAI;AAEF,SADe,aAAa,QAAQ,sCAAsC,KACxD;SACZ;AACN,SAAO;;;;;;;AAQX,SAAgB,2BAA2B,SAAwB;AACjE,KAAI;AACF,eAAa,QACX,uCACA,OAAO,QAAQ,CAChB;SACK;AAKR,QAAO,cACL,IAAI,YAAY,+BAA+B,EAC7C,QAAQ,EAAE,yBAAyB,SAAS,EAC7C,CAAC,CACH"}
|
|
@@ -8,7 +8,7 @@ function isTemporal(el) {
|
|
|
8
8
|
/**
|
|
9
9
|
* Get temporal bounds for an element, treating invalid ranges as unbounded.
|
|
10
10
|
* Invalid range (end <= start) means element hasn't computed its duration yet.
|
|
11
|
-
*
|
|
11
|
+
*
|
|
12
12
|
* NOTE: No caching - bounds are computed dynamically by timegroups based on
|
|
13
13
|
* composition mode (sequence/contain/fit) and may change between frames.
|
|
14
14
|
*/
|
|
@@ -39,8 +39,8 @@ function isVisibleAtTime(element, timeMs) {
|
|
|
39
39
|
/** Default timegroup dimensions when not measurable */
|
|
40
40
|
const DEFAULT_WIDTH = 1920;
|
|
41
41
|
const DEFAULT_HEIGHT = 1080;
|
|
42
|
-
/** Default scale for
|
|
43
|
-
const
|
|
42
|
+
/** Default scale for capture operations */
|
|
43
|
+
const DEFAULT_CAPTURE_SCALE = .25;
|
|
44
44
|
/** Default timeout for blocking content readiness mode (ms) */
|
|
45
45
|
const DEFAULT_BLOCKING_TIMEOUT_MS = 5e3;
|
|
46
46
|
/**
|
|
@@ -61,5 +61,5 @@ function createPreviewContainer(options) {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
//#endregion
|
|
64
|
-
export { DEFAULT_BLOCKING_TIMEOUT_MS,
|
|
64
|
+
export { DEFAULT_BLOCKING_TIMEOUT_MS, DEFAULT_CAPTURE_SCALE, DEFAULT_HEIGHT, DEFAULT_WIDTH, createPreviewContainer, isTemporal, isVisibleAtTime };
|
|
65
65
|
//# sourceMappingURL=previewTypes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"previewTypes.js","names":[],"sources":["../../src/preview/previewTypes.ts"],"sourcesContent":["/**\n * Shared types and constants for preview rendering.\n
|
|
1
|
+
{"version":3,"file":"previewTypes.js","names":[],"sources":["../../src/preview/previewTypes.ts"],"sourcesContent":["/**\n * Shared types and constants for preview rendering.\n *\n * Consolidates duplicate definitions from renderTimegroupToCanvas.ts and\n * renderTimegroupPreview.ts into a single source of truth.\n */\n\n// ============================================================================\n// Temporal Types\n// ============================================================================\n\n/**\n * Element with temporal properties (startTimeMs, endTimeMs).\n * Used for temporal visibility checks during preview rendering.\n */\nexport interface TemporalElement extends Element {\n startTimeMs?: number;\n endTimeMs?: number;\n src?: string;\n}\n\n/**\n * Type guard to check if an element has temporal properties.\n */\nexport function isTemporal(el: Element): el is TemporalElement {\n return \"startTimeMs\" in el && \"endTimeMs\" in el;\n}\n\n/**\n * Get temporal bounds for an element, treating invalid ranges as unbounded.\n * Invalid range (end <= start) means element hasn't computed its duration yet.\n *\n * NOTE: No caching - bounds are computed dynamically by timegroups based on\n * composition mode (sequence/contain/fit) and may change between frames.\n */\nexport function getTemporalBounds(el: Element): {\n startMs: number;\n endMs: number;\n} {\n // Non-temporal elements are always visible\n if (!isTemporal(el)) {\n return { startMs: -Infinity, endMs: Infinity };\n }\n\n // Compute bounds fresh each time\n const temporal = el as TemporalElement;\n let startMs = temporal.startTimeMs ?? -Infinity;\n let endMs = temporal.endTimeMs ?? Infinity;\n\n // If end <= start, treat as always visible (element hasn't computed duration yet)\n if (endMs <= startMs) {\n startMs = -Infinity;\n endMs = Infinity;\n }\n\n return { startMs, endMs };\n}\n\n/**\n * Check if an element is temporally visible at the given time.\n */\nexport function isVisibleAtTime(element: Element, timeMs: number): boolean {\n const { startMs, endMs } = getTemporalBounds(element);\n return timeMs >= startMs && timeMs <= endMs;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Default timegroup dimensions when not measurable */\nexport const DEFAULT_WIDTH = 1920;\nexport const DEFAULT_HEIGHT = 1080;\n\n/** Default scale for capture operations */\nexport const DEFAULT_CAPTURE_SCALE = 0.25;\n\n/** Default timeout for blocking content readiness mode (ms) */\nexport const DEFAULT_BLOCKING_TIMEOUT_MS = 5000;\n\n// ============================================================================\n// Container Creation\n// ============================================================================\n\n/**\n * Options for creating a preview container.\n */\nexport interface PreviewContainerOptions {\n width: number;\n height: number;\n background?: string;\n position?: \"relative\" | \"absolute\" | \"fixed\";\n}\n\n/**\n * Create a preview container with standard styling.\n * Consolidates the repeated container creation pattern across preview functions.\n */\nexport function createPreviewContainer(\n options: PreviewContainerOptions,\n): HTMLDivElement {\n const { width, height, background = \"#000\", position = \"relative\" } = options;\n\n const container = document.createElement(\"div\");\n container.style.cssText = `\n width: ${width}px;\n height: ${height}px;\n position: ${position};\n overflow: hidden;\n background: ${background};\n `;\n return container;\n}\n\n// ============================================================================\n// Style Injection\n// ============================================================================\n\n/**\n * Inject document styles into a container for foreignObject rendering.\n * SVG foreignObject needs all CSS rules inlined since it can't access\n * the document's stylesheets.\n */\nexport function injectDocumentStyles(\n container: HTMLElement,\n collectStyles: () => string,\n): HTMLStyleElement {\n const styleEl = document.createElement(\"style\");\n styleEl.textContent = collectStyles();\n container.appendChild(styleEl);\n return styleEl;\n}\n"],"mappings":";;;;AAwBA,SAAgB,WAAW,IAAoC;AAC7D,QAAO,iBAAiB,MAAM,eAAe;;;;;;;;;AAU/C,SAAgB,kBAAkB,IAGhC;AAEA,KAAI,CAAC,WAAW,GAAG,CACjB,QAAO;EAAE,SAAS;EAAW,OAAO;EAAU;CAIhD,MAAM,WAAW;CACjB,IAAI,UAAU,SAAS,eAAe;CACtC,IAAI,QAAQ,SAAS,aAAa;AAGlC,KAAI,SAAS,SAAS;AACpB,YAAU;AACV,UAAQ;;AAGV,QAAO;EAAE;EAAS;EAAO;;;;;AAM3B,SAAgB,gBAAgB,SAAkB,QAAyB;CACzE,MAAM,EAAE,SAAS,UAAU,kBAAkB,QAAQ;AACrD,QAAO,UAAU,WAAW,UAAU;;;AAQxC,MAAa,gBAAgB;AAC7B,MAAa,iBAAiB;;AAG9B,MAAa,wBAAwB;;AAGrC,MAAa,8BAA8B;;;;;AAoB3C,SAAgB,uBACd,SACgB;CAChB,MAAM,EAAE,OAAO,QAAQ,aAAa,QAAQ,WAAW,eAAe;CAEtE,MAAM,YAAY,SAAS,cAAc,MAAM;AAC/C,WAAU,MAAM,UAAU;aACf,MAAM;cACL,OAAO;gBACL,SAAS;;kBAEP,WAAW;;AAE3B,QAAO"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { RenderMode } from "./previewSettings.js";
|
|
2
|
+
import { RenderContext } from "./RenderContext.js";
|
|
3
|
+
|
|
4
|
+
//#region src/preview/renderElementToCanvas.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options for rendering an element to canvas.
|
|
8
|
+
*/
|
|
9
|
+
interface RenderElementOptions {
|
|
10
|
+
/** Time to render at in milliseconds (used for serialization metadata) */
|
|
11
|
+
timeMs: number;
|
|
12
|
+
/** Scale factor for canvas encoding (default: 1.0) */
|
|
13
|
+
scale?: number;
|
|
14
|
+
/** Output width in pixels (defaults to element's computed width or 1920) */
|
|
15
|
+
width?: number;
|
|
16
|
+
/** Output height in pixels (defaults to element's computed height or 1080) */
|
|
17
|
+
height?: number;
|
|
18
|
+
/** Render context for canvas pixel caching */
|
|
19
|
+
renderContext?: RenderContext;
|
|
20
|
+
/** Override render mode (native or foreignObject) */
|
|
21
|
+
renderMode?: RenderMode;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Render any element to canvas or image.
|
|
25
|
+
*
|
|
26
|
+
* This is a low-level rendering function that renders the element as-is.
|
|
27
|
+
* The caller is responsible for:
|
|
28
|
+
* - Creating clones if needed
|
|
29
|
+
* - Seeking to the correct time
|
|
30
|
+
* - Finding the correct element to render
|
|
31
|
+
*
|
|
32
|
+
* Use cases:
|
|
33
|
+
* - Preview: Pass prime timeline element (already at correct time)
|
|
34
|
+
* - Video/thumbnails: Pass element from reused clone (already seeked)
|
|
35
|
+
* - One-off capture: Create clone, seek, pass element, clean up
|
|
36
|
+
*
|
|
37
|
+
* @param element - Element to render (timegroup, temporal element, or plain DOM)
|
|
38
|
+
* @param options - Render options
|
|
39
|
+
* @returns Canvas or Image (both are CanvasImageSource)
|
|
40
|
+
*/
|
|
41
|
+
declare function renderElementToCanvas(element: Element, options: RenderElementOptions): Promise<CanvasImageSource>;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { RenderElementOptions, renderElementToCanvas };
|
|
44
|
+
//# sourceMappingURL=renderElementToCanvas.d.ts.map
|