@editframe/elements 0.37.3-beta → 0.38.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +2 -2
- 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 +5 -8
- 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 +2 -2
- 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 +2 -2
- 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 +2 -2
- package/dist/gui/EFFocusOverlay.js +3 -3
- package/dist/gui/EFFocusOverlay.js.map +1 -1
- package/dist/gui/EFOverlayItem.d.ts +2 -2
- package/dist/gui/EFOverlayLayer.d.ts +2 -2
- package/dist/gui/EFPause.d.ts +2 -2
- package/dist/gui/EFPause.js +1 -1
- package/dist/gui/EFPlay.d.ts +2 -2
- package/dist/gui/EFPlay.js +1 -1
- package/dist/gui/EFPreview.js +1 -1
- package/dist/gui/EFResizableBox.d.ts +2 -2
- package/dist/gui/EFResizableBox.js +5 -5
- package/dist/gui/EFResizableBox.js.map +1 -1
- package/dist/gui/EFScrubber.d.ts +2 -2
- package/dist/gui/EFScrubber.js +8 -13
- package/dist/gui/EFScrubber.js.map +1 -1
- package/dist/gui/EFTimeDisplay.d.ts +6 -2
- package/dist/gui/EFTimeDisplay.js +25 -7
- package/dist/gui/EFTimeDisplay.js.map +1 -1
- package/dist/gui/EFTimelineRuler.d.ts +2 -2
- package/dist/gui/EFTimelineRuler.js +3 -3
- package/dist/gui/EFTimelineRuler.js.map +1 -1
- package/dist/gui/EFToggleLoop.d.ts +2 -2
- package/dist/gui/EFToggleLoop.js +1 -1
- package/dist/gui/EFTogglePlay.d.ts +2 -2
- package/dist/gui/EFTogglePlay.js +1 -1
- package/dist/gui/EFTransformHandles.d.ts +2 -2
- 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 +5 -3
- 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 +2 -2
- package/dist/gui/tree/EFTree.js +8 -14
- package/dist/gui/tree/EFTree.js.map +1 -1
- package/dist/gui/tree/EFTreeItem.d.ts +2 -2
- 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.d.ts +134 -32
- package/dist/preview/renderTimegroupToCanvas.js +321 -146
- package/dist/preview/renderTimegroupToCanvas.js.map +1 -1
- package/dist/preview/renderTimegroupToCanvas.types.d.ts +51 -0
- package/dist/preview/renderTimegroupToVideo.d.ts +20 -35
- package/dist/preview/renderTimegroupToVideo.js +94 -106
- package/dist/preview/renderTimegroupToVideo.js.map +1 -1
- package/dist/preview/renderTimegroupToVideo.types.d.ts +42 -0
- package/dist/preview/renderVideoToVideo.js +286 -0
- package/dist/preview/renderVideoToVideo.js.map +1 -0
- package/dist/preview/renderers.d.ts +56 -0
- package/dist/preview/renderers.js +13 -1
- 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.d.ts +13 -0
- package/dist/preview/rendering/inlineImages.js +7 -44
- package/dist/preview/rendering/inlineImages.js.map +1 -1
- package/dist/preview/rendering/loadImage.d.ts +8 -0
- 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 +45 -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/tsdown.config.ts +6 -1
- 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/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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderers.js","names":[],"sources":["../../src/preview/renderers.ts"],"sourcesContent":["/**\n * Renderer strategy pattern for HTML-to-image rendering.\n * Provides a unified interface for native (drawElementImage) and foreignObject paths.\n */\n\nimport {
|
|
1
|
+
{"version":3,"file":"renderers.js","names":[],"sources":["../../src/preview/renderers.ts"],"sourcesContent":["/**\n * Renderer strategy pattern for HTML-to-image rendering.\n * Provides a unified interface for native (drawElementImage) and foreignObject paths.\n */\n\nimport {\n isNativeCanvasApiAvailable,\n getRenderMode,\n type RenderMode,\n} from \"./previewSettings.js\";\n\n/**\n * Options for rendering HTML to an image or canvas.\n */\nexport interface RenderOptions {\n /** Skip device pixel ratio scaling (render at logical pixels) */\n skipDprScaling?: boolean;\n /** Scale factor for encoding internal canvases (foreignObject only) */\n canvasScale?: number;\n /** Whether to reuse an existing canvas (native only) */\n reuseCanvas?: HTMLCanvasElement;\n}\n\n/**\n * Result of a render operation.\n * Native path returns a canvas, foreignObject path returns an image.\n */\nexport type RenderResult = HTMLCanvasElement | HTMLImageElement;\n\n/**\n * Renderer interface for HTML-to-image conversion.\n */\nexport interface Renderer {\n /** The render mode this renderer implements */\n readonly mode: RenderMode;\n\n /**\n * Render an HTML container to an image or canvas.\n * @param container - The HTML element to render\n * @param width - Target width in logical pixels\n * @param height - Target height in logical pixels\n * @param options - Rendering options\n * @returns Promise resolving to a canvas or image element\n */\n render(\n container: HTMLElement,\n width: number,\n height: number,\n options?: RenderOptions,\n ): Promise<RenderResult>;\n\n /**\n * Check if this renderer is available in the current environment.\n */\n isAvailable(): boolean;\n}\n\n/**\n * Get the effective render mode, validating that native is available when selected.\n * Falls back to foreignObject if native is selected but not available.\n */\nexport function getEffectiveRenderMode(): RenderMode {\n const mode = getRenderMode();\n\n if (mode === \"native\" && !isNativeCanvasApiAvailable()) {\n return \"foreignObject\";\n }\n\n return mode;\n}\n\n/**\n * Check if a render result is a canvas element.\n */\nexport function isCanvas(result: RenderResult): result is HTMLCanvasElement {\n return result instanceof HTMLCanvasElement;\n}\n\n/**\n * Check if a render result is an image element.\n */\nexport function isImage(result: RenderResult): result is HTMLImageElement {\n return result instanceof HTMLImageElement;\n}\n"],"mappings":";;;;;;;AA6DA,SAAgB,yBAAqC;CACnD,MAAM,OAAO,eAAe;AAE5B,KAAI,SAAS,YAAY,CAAC,4BAA4B,CACpD,QAAO;AAGT,QAAO;;;;;AAMT,SAAgB,SAAS,QAAmD;AAC1E,QAAO,kBAAkB;;;;;AAM3B,SAAgB,QAAQ,QAAkD;AACxE,QAAO,kBAAkB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//#region src/preview/rendering/ScaleConfig.ts
|
|
2
|
+
/**
|
|
3
|
+
* Immutable scaling configuration for a serialization operation.
|
|
4
|
+
*
|
|
5
|
+
* All scaling decisions are computed once at construction and cached.
|
|
6
|
+
* This ensures consistency across all stages of serialization.
|
|
7
|
+
*/
|
|
8
|
+
var ScaleConfig = class ScaleConfig {
|
|
9
|
+
constructor(width, height, exportScale) {
|
|
10
|
+
this.qualityMultiplier = 1.5;
|
|
11
|
+
this.inputWidth = width;
|
|
12
|
+
this.inputHeight = height;
|
|
13
|
+
this.exportScale = exportScale;
|
|
14
|
+
this.outputWidth = Math.floor(width * exportScale);
|
|
15
|
+
this.outputHeight = Math.floor(height * exportScale);
|
|
16
|
+
this.needsDOMScaling = exportScale < 1;
|
|
17
|
+
Object.freeze(this);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Compute optimal encoding scale for a canvas element.
|
|
21
|
+
*
|
|
22
|
+
* This is Stage 3 of the scaling architecture. Canvas pixels are scaled
|
|
23
|
+
* independently from DOM content because they have intrinsic resolution.
|
|
24
|
+
*
|
|
25
|
+
* Algorithm:
|
|
26
|
+
* 1. Calculate display scale (CSS size vs natural size)
|
|
27
|
+
* 2. Multiply by export scale
|
|
28
|
+
* 3. Multiply by quality multiplier (1.5x for sharpness)
|
|
29
|
+
* 4. Cap at 1.0 (never upscale beyond natural resolution)
|
|
30
|
+
*
|
|
31
|
+
* @param params - Canvas dimensions (natural and display)
|
|
32
|
+
* @returns Optimal scale for encoding (0.0 to 1.0)
|
|
33
|
+
*/
|
|
34
|
+
computeCanvasScale(params) {
|
|
35
|
+
const { naturalWidth, naturalHeight, displayWidth, displayHeight } = params;
|
|
36
|
+
const displayScaleX = displayWidth / naturalWidth;
|
|
37
|
+
const displayScaleY = displayHeight / naturalHeight;
|
|
38
|
+
const displayScale = Math.min(displayScaleX, displayScaleY);
|
|
39
|
+
return Math.min(1, displayScale * this.exportScale * this.qualityMultiplier);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get the CSS transform value for DOM scaling.
|
|
43
|
+
* Returns null if no scaling is needed.
|
|
44
|
+
*/
|
|
45
|
+
getDOMTransform() {
|
|
46
|
+
return this.needsDOMScaling ? `scale(${this.exportScale})` : null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get the wrapper dimensions for the CSS transform.
|
|
50
|
+
* When DOM is scaled, the wrapper must be larger to accommodate
|
|
51
|
+
* the scaled-down content.
|
|
52
|
+
*/
|
|
53
|
+
getDOMWrapperDimensions() {
|
|
54
|
+
if (!this.needsDOMScaling) return {
|
|
55
|
+
width: this.outputWidth,
|
|
56
|
+
height: this.outputHeight
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
width: Math.floor(this.outputWidth / this.exportScale),
|
|
60
|
+
height: Math.floor(this.outputHeight / this.exportScale)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Create a ScaleConfig from legacy options.
|
|
65
|
+
* Maintains backward compatibility with existing callsites.
|
|
66
|
+
*/
|
|
67
|
+
static fromOptions(width, height, canvasScale) {
|
|
68
|
+
return new ScaleConfig(width, height, canvasScale);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
//#endregion
|
|
73
|
+
export { ScaleConfig };
|
|
74
|
+
//# sourceMappingURL=ScaleConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScaleConfig.js","names":[],"sources":["../../../src/preview/rendering/ScaleConfig.ts"],"sourcesContent":["/**\n * ScaleConfig - Unified scaling configuration for timeline serialization.\n *\n * Consolidates the multi-stage scaling architecture into a single,\n * well-defined abstraction with clear contracts.\n *\n * Previously, scaling was applied in 4 separate stages with implicit contracts:\n * 1. captureTimelineToDataUri: scaled output dimensions\n * 2. captureElementParts: CSS transform wrapper for DOM content\n * 3. serializeCanvas: independent optimalScale calculation per canvas\n * 4. encodeCanvasesInParallel: received pre-scaled snapshots\n *\n * Now, ScaleConfig centralizes all scaling logic and makes the contracts explicit.\n */\n\nexport interface CanvasScaleParams {\n /** Natural canvas pixel dimensions */\n naturalWidth: number;\n naturalHeight: number;\n /** CSS display dimensions (how big it appears) */\n displayWidth: number;\n displayHeight: number;\n}\n\n/**\n * Immutable scaling configuration for a serialization operation.\n *\n * All scaling decisions are computed once at construction and cached.\n * This ensures consistency across all stages of serialization.\n */\nexport class ScaleConfig {\n /** User-specified export scale (e.g., 0.25 for thumbnails, 1.0 for full resolution) */\n readonly exportScale: number;\n\n /** Input dimensions (before scaling) */\n readonly inputWidth: number;\n readonly inputHeight: number;\n\n /** Output SVG dimensions (after scaling) */\n readonly outputWidth: number;\n readonly outputHeight: number;\n\n /** Whether DOM content needs CSS transform:scale() wrapper */\n readonly needsDOMScaling: boolean;\n\n /** Quality multiplier for canvas encoding (1.5x for sharpness) */\n readonly qualityMultiplier: number = 1.5;\n\n constructor(width: number, height: number, exportScale: number) {\n this.inputWidth = width;\n this.inputHeight = height;\n this.exportScale = exportScale;\n\n // Compute output dimensions (Stage 1)\n this.outputWidth = Math.floor(width * exportScale);\n this.outputHeight = Math.floor(height * exportScale);\n\n // Determine if DOM needs CSS scaling (Stage 2)\n this.needsDOMScaling = exportScale < 1;\n\n // Freeze to ensure immutability\n Object.freeze(this);\n }\n\n /**\n * Compute optimal encoding scale for a canvas element.\n *\n * This is Stage 3 of the scaling architecture. Canvas pixels are scaled\n * independently from DOM content because they have intrinsic resolution.\n *\n * Algorithm:\n * 1. Calculate display scale (CSS size vs natural size)\n * 2. Multiply by export scale\n * 3. Multiply by quality multiplier (1.5x for sharpness)\n * 4. Cap at 1.0 (never upscale beyond natural resolution)\n *\n * @param params - Canvas dimensions (natural and display)\n * @returns Optimal scale for encoding (0.0 to 1.0)\n */\n computeCanvasScale(params: CanvasScaleParams): number {\n const { naturalWidth, naturalHeight, displayWidth, displayHeight } = params;\n\n // Calculate how much smaller the display is vs natural size\n const displayScaleX = displayWidth / naturalWidth;\n const displayScaleY = displayHeight / naturalHeight;\n const displayScale = Math.min(displayScaleX, displayScaleY);\n\n // Combine display scale, export scale, and quality multiplier\n // Cap at 1.0 to never upscale beyond natural resolution\n const optimalScale = Math.min(\n 1.0,\n displayScale * this.exportScale * this.qualityMultiplier,\n );\n\n return optimalScale;\n }\n\n /**\n * Get the CSS transform value for DOM scaling.\n * Returns null if no scaling is needed.\n */\n getDOMTransform(): string | null {\n return this.needsDOMScaling ? `scale(${this.exportScale})` : null;\n }\n\n /**\n * Get the wrapper dimensions for the CSS transform.\n * When DOM is scaled, the wrapper must be larger to accommodate\n * the scaled-down content.\n */\n getDOMWrapperDimensions(): { width: number; height: number } {\n if (!this.needsDOMScaling) {\n return { width: this.outputWidth, height: this.outputHeight };\n }\n\n return {\n width: Math.floor(this.outputWidth / this.exportScale),\n height: Math.floor(this.outputHeight / this.exportScale),\n };\n }\n\n /**\n * Create a ScaleConfig from legacy options.\n * Maintains backward compatibility with existing callsites.\n */\n static fromOptions(\n width: number,\n height: number,\n canvasScale: number,\n ): ScaleConfig {\n return new ScaleConfig(width, height, canvasScale);\n }\n}\n"],"mappings":";;;;;;;AA8BA,IAAa,cAAb,MAAa,YAAY;CAkBvB,YAAY,OAAe,QAAgB,aAAqB;2BAF3B;AAGnC,OAAK,aAAa;AAClB,OAAK,cAAc;AACnB,OAAK,cAAc;AAGnB,OAAK,cAAc,KAAK,MAAM,QAAQ,YAAY;AAClD,OAAK,eAAe,KAAK,MAAM,SAAS,YAAY;AAGpD,OAAK,kBAAkB,cAAc;AAGrC,SAAO,OAAO,KAAK;;;;;;;;;;;;;;;;;CAkBrB,mBAAmB,QAAmC;EACpD,MAAM,EAAE,cAAc,eAAe,cAAc,kBAAkB;EAGrE,MAAM,gBAAgB,eAAe;EACrC,MAAM,gBAAgB,gBAAgB;EACtC,MAAM,eAAe,KAAK,IAAI,eAAe,cAAc;AAS3D,SALqB,KAAK,IACxB,GACA,eAAe,KAAK,cAAc,KAAK,kBACxC;;;;;;CASH,kBAAiC;AAC/B,SAAO,KAAK,kBAAkB,SAAS,KAAK,YAAY,KAAK;;;;;;;CAQ/D,0BAA6D;AAC3D,MAAI,CAAC,KAAK,gBACR,QAAO;GAAE,OAAO,KAAK;GAAa,QAAQ,KAAK;GAAc;AAG/D,SAAO;GACL,OAAO,KAAK,MAAM,KAAK,cAAc,KAAK,YAAY;GACtD,QAAQ,KAAK,MAAM,KAAK,eAAe,KAAK,YAAY;GACzD;;;;;;CAOH,OAAO,YACL,OACA,QACA,aACa;AACb,SAAO,IAAI,YAAY,OAAO,QAAQ,YAAY"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/preview/rendering/inlineImages.d.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Clear the inline image cache. Useful for memory management in long-running sessions.
|
|
5
|
+
*/
|
|
6
|
+
declare function clearInlineImageCache(): void;
|
|
7
|
+
/**
|
|
8
|
+
* Get current inline image cache size for diagnostics.
|
|
9
|
+
*/
|
|
10
|
+
declare function getInlineImageCacheSize(): number;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { clearInlineImageCache, getInlineImageCacheSize };
|
|
13
|
+
//# sourceMappingURL=inlineImages.d.ts.map
|
|
@@ -1,56 +1,19 @@
|
|
|
1
|
-
import { logger } from "../logger.js";
|
|
2
|
-
|
|
3
1
|
//#region src/preview/rendering/inlineImages.ts
|
|
4
|
-
/** Maximum number of cached inline images before eviction */
|
|
5
|
-
const MAX_INLINE_IMAGE_CACHE_SIZE = 100;
|
|
6
2
|
/** Image cache for inlining external images as data URIs (foreignObject path) */
|
|
7
3
|
const _inlineImageCache = /* @__PURE__ */ new Map();
|
|
8
4
|
/**
|
|
9
|
-
* Convert a Blob to a data URL.
|
|
10
|
-
*/
|
|
11
|
-
function blobToDataURL(blob) {
|
|
12
|
-
return new Promise((resolve, reject) => {
|
|
13
|
-
const reader = new FileReader();
|
|
14
|
-
reader.onload = () => resolve(reader.result);
|
|
15
|
-
reader.onerror = reject;
|
|
16
|
-
reader.readAsDataURL(blob);
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Inline all images in a container as base64 data URIs.
|
|
21
|
-
* SVG foreignObject can't load external images due to security restrictions.
|
|
22
|
-
* Uses an LRU-style cache with size limits to prevent memory leaks.
|
|
23
|
-
*/
|
|
24
|
-
async function inlineImages(container) {
|
|
25
|
-
const images = container.querySelectorAll("img");
|
|
26
|
-
for (const image of images) {
|
|
27
|
-
const src = image.getAttribute("src");
|
|
28
|
-
if (!src || src.startsWith("data:")) continue;
|
|
29
|
-
const cached = _inlineImageCache.get(src);
|
|
30
|
-
if (cached) {
|
|
31
|
-
image.setAttribute("src", cached);
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
try {
|
|
35
|
-
const dataUrl = await blobToDataURL(await (await fetch(src)).blob());
|
|
36
|
-
image.setAttribute("src", dataUrl);
|
|
37
|
-
if (_inlineImageCache.size >= MAX_INLINE_IMAGE_CACHE_SIZE) {
|
|
38
|
-
const firstKey = _inlineImageCache.keys().next().value;
|
|
39
|
-
if (firstKey) _inlineImageCache.delete(firstKey);
|
|
40
|
-
}
|
|
41
|
-
_inlineImageCache.set(src, dataUrl);
|
|
42
|
-
} catch (e) {
|
|
43
|
-
logger.warn("Failed to inline image:", src, e);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
5
|
* Clear the inline image cache. Useful for memory management in long-running sessions.
|
|
49
6
|
*/
|
|
50
7
|
function clearInlineImageCache() {
|
|
51
8
|
_inlineImageCache.clear();
|
|
52
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Get current inline image cache size for diagnostics.
|
|
12
|
+
*/
|
|
13
|
+
function getInlineImageCacheSize() {
|
|
14
|
+
return _inlineImageCache.size;
|
|
15
|
+
}
|
|
53
16
|
|
|
54
17
|
//#endregion
|
|
55
|
-
export { clearInlineImageCache,
|
|
18
|
+
export { clearInlineImageCache, getInlineImageCacheSize };
|
|
56
19
|
//# sourceMappingURL=inlineImages.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inlineImages.js","names":[],"sources":["../../../src/preview/rendering/inlineImages.ts"],"sourcesContent":["/**\n * Image inlining utilities for SVG foreignObject rendering.\n * SVG foreignObject can't load external images due to security restrictions,\n * so we convert them to base64 data URIs.\n */\n\nimport { logger } from \"../logger.js\";\n\n/** Maximum number of cached inline images before eviction */\nconst MAX_INLINE_IMAGE_CACHE_SIZE = 100;\n\n/** Image cache for inlining external images as data URIs (foreignObject path) */\nconst _inlineImageCache = new Map<string, string>();\n\n/**\n * Convert a Blob to a data URL.\n */\nfunction blobToDataURL(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = reject;\n reader.readAsDataURL(blob);\n });\n}\n\n/**\n * Inline all images in a container as base64 data URIs.\n * SVG foreignObject can't load external images due to security restrictions.\n * Uses an LRU-style cache with size limits to prevent memory leaks.\n */\nexport async function inlineImages(container: HTMLElement): Promise<void> {\n const images = container.querySelectorAll(\"img\");\n for (const image of images) {\n const src = image.getAttribute(\"src\");\n if (!src || src.startsWith(\"data:\")) continue;\n\n const cached = _inlineImageCache.get(src);\n if (cached) {\n image.setAttribute(\"src\", cached);\n continue;\n }\n\n try {\n const response = await fetch(src);\n const blob = await response.blob();\n const dataUrl = await blobToDataURL(blob);\n image.setAttribute(\"src\", dataUrl);\n
|
|
1
|
+
{"version":3,"file":"inlineImages.js","names":[],"sources":["../../../src/preview/rendering/inlineImages.ts"],"sourcesContent":["/**\n * Image inlining utilities for SVG foreignObject rendering.\n * SVG foreignObject can't load external images due to security restrictions,\n * so we convert them to base64 data URIs.\n */\n\nimport { logger } from \"../logger.js\";\n\n/** Maximum number of cached inline images before eviction */\nconst MAX_INLINE_IMAGE_CACHE_SIZE = 100;\n\n/** Image cache for inlining external images as data URIs (foreignObject path) */\nconst _inlineImageCache = new Map<string, string>();\n\n/**\n * Convert a Blob to a data URL.\n */\nfunction blobToDataURL(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as string);\n reader.onerror = reject;\n reader.readAsDataURL(blob);\n });\n}\n\n/**\n * Inline all images in a container as base64 data URIs.\n * SVG foreignObject can't load external images due to security restrictions.\n * Uses an LRU-style cache with size limits to prevent memory leaks.\n */\nexport async function inlineImages(container: HTMLElement): Promise<void> {\n const images = container.querySelectorAll(\"img\");\n for (const image of images) {\n const src = image.getAttribute(\"src\");\n if (!src || src.startsWith(\"data:\")) continue;\n\n const cached = _inlineImageCache.get(src);\n if (cached) {\n image.setAttribute(\"src\", cached);\n continue;\n }\n\n try {\n const response = await fetch(src);\n const blob = await response.blob();\n const dataUrl = await blobToDataURL(blob);\n image.setAttribute(\"src\", dataUrl);\n\n // Evict oldest entries if cache is full (simple FIFO eviction)\n if (_inlineImageCache.size >= MAX_INLINE_IMAGE_CACHE_SIZE) {\n const firstKey = _inlineImageCache.keys().next().value;\n if (firstKey) _inlineImageCache.delete(firstKey);\n }\n _inlineImageCache.set(src, dataUrl);\n } catch (e) {\n logger.warn(\"Failed to inline image:\", src, e);\n }\n }\n}\n\n/**\n * Clear the inline image cache. Useful for memory management in long-running sessions.\n */\nexport function clearInlineImageCache(): void {\n _inlineImageCache.clear();\n}\n\n/**\n * Get current inline image cache size for diagnostics.\n */\nexport function getInlineImageCacheSize(): number {\n return _inlineImageCache.size;\n}\n"],"mappings":";;AAYA,MAAM,oCAAoB,IAAI,KAAqB;;;;AAoDnD,SAAgB,wBAA8B;AAC5C,mBAAkB,OAAO;;;;;AAM3B,SAAgB,0BAAkC;AAChD,QAAO,kBAAkB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/preview/rendering/loadImage.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Load an image from a data URI. Returns a Promise that resolves when loaded.
|
|
4
|
+
*/
|
|
5
|
+
declare function loadImageFromDataUri(dataUri: string): Promise<HTMLImageElement>;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { loadImageFromDataUri };
|
|
8
|
+
//# sourceMappingURL=loadImage.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defaultProfiler } from "../RenderProfiler.js";
|
|
2
|
+
|
|
3
|
+
//#region src/preview/rendering/loadImage.ts
|
|
4
|
+
/**
|
|
5
|
+
* Load an image from a data URI. Returns a Promise that resolves when loaded.
|
|
6
|
+
*/
|
|
7
|
+
function loadImageFromDataUri(dataUri) {
|
|
8
|
+
const img = new Image();
|
|
9
|
+
const imageLoadStart = performance.now();
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
img.onload = () => {
|
|
12
|
+
defaultProfiler.addTime("imageLoad", performance.now() - imageLoadStart);
|
|
13
|
+
resolve(img);
|
|
14
|
+
};
|
|
15
|
+
img.onerror = reject;
|
|
16
|
+
img.src = dataUri;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
export { loadImageFromDataUri };
|
|
22
|
+
//# sourceMappingURL=loadImage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadImage.js","names":[],"sources":["../../../src/preview/rendering/loadImage.ts"],"sourcesContent":["import { defaultProfiler } from \"../RenderProfiler.js\";\n\n/**\n * Load an image from a data URI. Returns a Promise that resolves when loaded.\n */\nexport function loadImageFromDataUri(\n dataUri: string,\n): Promise<HTMLImageElement> {\n const img = new Image();\n const imageLoadStart = performance.now();\n\n return new Promise<HTMLImageElement>((resolve, reject) => {\n img.onload = () => {\n defaultProfiler.addTime(\"imageLoad\", performance.now() - imageLoadStart);\n resolve(img);\n };\n img.onerror = reject;\n img.src = dataUri;\n });\n}\n"],"mappings":";;;;;;AAKA,SAAgB,qBACd,SAC2B;CAC3B,MAAM,MAAM,IAAI,OAAO;CACvB,MAAM,iBAAiB,YAAY,KAAK;AAExC,QAAO,IAAI,SAA2B,SAAS,WAAW;AACxD,MAAI,eAAe;AACjB,mBAAgB,QAAQ,aAAa,YAAY,KAAK,GAAG,eAAe;AACxE,WAAQ,IAAI;;AAEd,MAAI,UAAU;AACd,MAAI,MAAM;GACV"}
|
|
@@ -27,15 +27,15 @@ function createDprCanvas(options) {
|
|
|
27
27
|
/**
|
|
28
28
|
* Render HTML content to canvas using native HTML-in-Canvas API (drawElementImage).
|
|
29
29
|
* This is much faster than the foreignObject approach and avoids canvas tainting.
|
|
30
|
-
*
|
|
30
|
+
*
|
|
31
31
|
* Note: The native API renders at device pixel ratio, so we capture at DPR scale
|
|
32
32
|
* and then downsample to logical pixels to match the foreignObject path's output.
|
|
33
|
-
*
|
|
33
|
+
*
|
|
34
34
|
* @param container - The HTML element to render
|
|
35
35
|
* @param width - Target width in logical pixels
|
|
36
36
|
* @param height - Target height in logical pixels
|
|
37
37
|
* @param options - Rendering options (skipWait for batch mode)
|
|
38
|
-
*
|
|
38
|
+
*
|
|
39
39
|
* @see https://github.com/WICG/html-in-canvas
|
|
40
40
|
*/
|
|
41
41
|
async function renderToImageNative(container, width, height, options = {}) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderToImageNative.js","names":["captureCanvas: HTMLCanvasElement","dpr"],"sources":["../../../src/preview/rendering/renderToImageNative.ts"],"sourcesContent":["/**\n * Native HTML-in-Canvas rendering using drawElementImage API.\n */\n\nimport type { HtmlInCanvasContext, HtmlInCanvasElement, NativeRenderOptions } from \"./types.js\";\nimport { defaultProfiler } from \"../RenderProfiler.js\";\n\n/** Track canvases that have been initialized for layoutsubtree (only need to wait once) */\nconst _layoutInitializedCanvases = new WeakSet<HTMLCanvasElement>();\n\n/**\n * Wait for next animation frame (allows browser to complete layout)\n */\nfunction waitForFrame(): Promise<void> {\n return new Promise(resolve => requestAnimationFrame(() => resolve()));\n}\n\n/**\n * Create a canvas element with proper DPR handling.\n * Buffer size is based on renderWidth/renderHeight (internal resolution).\n * CSS size is based on fullWidth/fullHeight (logical display size).\n */\nexport function createDprCanvas(options: {\n renderWidth: number;\n renderHeight: number;\n scale: number;\n dpr?: number;\n fullWidth: number;\n fullHeight: number;\n}): HTMLCanvasElement {\n const { renderWidth, renderHeight, scale, fullWidth, fullHeight } = options;\n const dpr = options.dpr ?? window.devicePixelRatio ?? 1;\n \n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.floor(renderWidth * scale * dpr);\n canvas.height = Math.floor(renderHeight * scale * dpr);\n canvas.style.width = `${Math.floor(fullWidth * scale)}px`;\n canvas.style.height = `${Math.floor(fullHeight * scale)}px`;\n \n return canvas;\n}\n\n/**\n * Render HTML content to canvas using native HTML-in-Canvas API (drawElementImage).\n * This is much faster than the foreignObject approach and avoids canvas tainting.\n * \n * Note: The native API renders at device pixel ratio, so we capture at DPR scale\n * and then downsample to logical pixels to match the foreignObject path's output.\n * \n * @param container - The HTML element to render\n * @param width - Target width in logical pixels\n * @param height - Target height in logical pixels\n * @param options - Rendering options (skipWait for batch mode)\n * \n * @see https://github.com/WICG/html-in-canvas\n */\nexport async function renderToImageNative(\n container: HTMLElement,\n width: number,\n height: number,\n options: NativeRenderOptions = {},\n): Promise<HTMLCanvasElement> {\n const t0 = performance.now();\n const { reuseCanvas, skipDprScaling = false } = options;\n // Use 1x DPR when skipDprScaling is true (for video export) - 4x fewer pixels!\n const dpr = skipDprScaling ? 1 : (window.devicePixelRatio || 1);\n \n // Use provided canvas or create new one\n let captureCanvas: HTMLCanvasElement;\n let shouldCleanup = false;\n \n if (reuseCanvas) {\n captureCanvas = reuseCanvas;\n \n // Ensure canvas dimensions match (both attribute and CSS)\n const dpr = skipDprScaling ? 1 : (window.devicePixelRatio || 1);\n const targetWidth = Math.floor(width * dpr);\n const targetHeight = Math.floor(height * dpr);\n \n // Set attribute dimensions (pixel buffer size)\n if (captureCanvas.width !== targetWidth) {\n captureCanvas.width = targetWidth;\n }\n if (captureCanvas.height !== targetHeight) {\n captureCanvas.height = targetHeight;\n }\n \n // Ensure CSS dimensions and positioning (same as non-reuse path)\n // This ensures consistent behavior and avoids layout issues\n captureCanvas.style.cssText = `\n position: fixed;\n left: 0;\n top: 0;\n width: ${width}px;\n height: ${height}px;\n opacity: 0;\n pointer-events: none;\n z-index: -9999;\n `;\n \n // Ensure layoutsubtree is set (required for drawElementImage)\n if (!captureCanvas.hasAttribute(\"layoutsubtree\")) {\n captureCanvas.setAttribute(\"layoutsubtree\", \"\");\n (captureCanvas as HtmlInCanvasElement).layoutSubtree = true;\n }\n \n // Ensure canvas is in DOM (required for drawElementImage layout)\n if (!captureCanvas.parentNode) {\n document.body.appendChild(captureCanvas);\n }\n \n // Ensure container is child of canvas\n if (container.parentElement !== captureCanvas) {\n captureCanvas.appendChild(container);\n }\n \n // Ensure container is visible (not display: none) for layout\n // drawElementImage requires the element to be laid out\n const containerStyle = getComputedStyle(container);\n if (containerStyle.display === 'none') {\n container.style.display = 'block';\n }\n \n // Force synchronous layout ONLY on first use with this canvas\n // For batch rendering (video export), repeated layout forces are expensive\n // We only need to force layout once to ensure everything is ready\n if (!_layoutInitializedCanvases.has(captureCanvas)) {\n void captureCanvas.offsetHeight;\n void container.offsetHeight;\n getComputedStyle(captureCanvas).opacity;\n getComputedStyle(container).opacity;\n _layoutInitializedCanvases.add(captureCanvas);\n }\n } else {\n captureCanvas = document.createElement(\"canvas\");\n captureCanvas.width = Math.floor(width * dpr);\n captureCanvas.height = Math.floor(height * dpr);\n \n // Enable HTML-in-Canvas mode via layoutsubtree attribute/property\n captureCanvas.setAttribute(\"layoutsubtree\", \"\");\n (captureCanvas as HtmlInCanvasElement).layoutSubtree = true;\n \n captureCanvas.appendChild(container);\n \n captureCanvas.style.cssText = `\n position: fixed;\n left: 0;\n top: 0;\n width: ${width}px;\n height: ${height}px;\n opacity: 0;\n pointer-events: none;\n z-index: -9999;\n `;\n document.body.appendChild(captureCanvas);\n shouldCleanup = true;\n }\n \n const t1 = performance.now();\n defaultProfiler.addTime(\"setup\", t1 - t0);\n \n try {\n // Force style calculation to ensure CSS is computed before capture\n // This ensures both canvas and container are laid out (required for drawElementImage)\n getComputedStyle(container).opacity;\n \n // When reusing canvas with layoutsubtree, wait for initial layout (first use only)\n // Use a WeakSet to track canvases that have been initialized\n if (reuseCanvas && (captureCanvas as any).layoutSubtree && !_layoutInitializedCanvases.has(captureCanvas)) {\n await waitForFrame();\n _layoutInitializedCanvases.add(captureCanvas);\n \n // Canvas may have been detached during async wait (e.g., test cleanup)\n if (!captureCanvas.parentNode) {\n return captureCanvas;\n }\n }\n \n const ctx = captureCanvas.getContext(\"2d\") as HtmlInCanvasContext;\n ctx.drawElementImage(container, 0, 0);\n } finally {\n // Only clean up if we created the canvas\n if (shouldCleanup && captureCanvas.parentNode) {\n captureCanvas.parentNode.removeChild(captureCanvas);\n }\n }\n \n const t2 = performance.now();\n defaultProfiler.addTime(\"draw\", t2 - t1);\n \n // If DPR is 1, no downsampling needed - return as-is\n if (dpr === 1) {\n defaultProfiler.incrementRenderCount();\n return captureCanvas;\n }\n \n // Downsample to logical pixel dimensions to match foreignObject path output\n // This ensures consistent behavior regardless of which rendering path is used\n const outputCanvas = document.createElement(\"canvas\");\n outputCanvas.width = width;\n outputCanvas.height = height;\n \n const outputCtx = outputCanvas.getContext(\"2d\")!;\n // Draw the DPR-scaled capture onto the 1x output canvas\n outputCtx.drawImage(\n captureCanvas,\n 0, 0, captureCanvas.width, captureCanvas.height, // source (full DPR capture)\n 0, 0, width, height // destination (logical pixels)\n );\n \n const t3 = performance.now();\n defaultProfiler.addTime(\"downsample\", t3 - t2);\n defaultProfiler.incrementRenderCount();\n \n return outputCanvas;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"renderToImageNative.js","names":["captureCanvas: HTMLCanvasElement","dpr"],"sources":["../../../src/preview/rendering/renderToImageNative.ts"],"sourcesContent":["/**\n * Native HTML-in-Canvas rendering using drawElementImage API.\n */\n\nimport type {\n HtmlInCanvasContext,\n HtmlInCanvasElement,\n NativeRenderOptions,\n} from \"./types.js\";\nimport { defaultProfiler } from \"../RenderProfiler.js\";\n\n/** Track canvases that have been initialized for layoutsubtree (only need to wait once) */\nconst _layoutInitializedCanvases = new WeakSet<HTMLCanvasElement>();\n\n/**\n * Wait for next animation frame (allows browser to complete layout)\n */\nfunction waitForFrame(): Promise<void> {\n return new Promise((resolve) => requestAnimationFrame(() => resolve()));\n}\n\n/**\n * Create a canvas element with proper DPR handling.\n * Buffer size is based on renderWidth/renderHeight (internal resolution).\n * CSS size is based on fullWidth/fullHeight (logical display size).\n */\nexport function createDprCanvas(options: {\n renderWidth: number;\n renderHeight: number;\n scale: number;\n dpr?: number;\n fullWidth: number;\n fullHeight: number;\n}): HTMLCanvasElement {\n const { renderWidth, renderHeight, scale, fullWidth, fullHeight } = options;\n const dpr = options.dpr ?? window.devicePixelRatio ?? 1;\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.floor(renderWidth * scale * dpr);\n canvas.height = Math.floor(renderHeight * scale * dpr);\n canvas.style.width = `${Math.floor(fullWidth * scale)}px`;\n canvas.style.height = `${Math.floor(fullHeight * scale)}px`;\n\n return canvas;\n}\n\n/**\n * Render HTML content to canvas using native HTML-in-Canvas API (drawElementImage).\n * This is much faster than the foreignObject approach and avoids canvas tainting.\n *\n * Note: The native API renders at device pixel ratio, so we capture at DPR scale\n * and then downsample to logical pixels to match the foreignObject path's output.\n *\n * @param container - The HTML element to render\n * @param width - Target width in logical pixels\n * @param height - Target height in logical pixels\n * @param options - Rendering options (skipWait for batch mode)\n *\n * @see https://github.com/WICG/html-in-canvas\n */\nexport async function renderToImageNative(\n container: HTMLElement,\n width: number,\n height: number,\n options: NativeRenderOptions = {},\n): Promise<HTMLCanvasElement> {\n const t0 = performance.now();\n const { reuseCanvas, skipDprScaling = false } = options;\n // Use 1x DPR when skipDprScaling is true (for video export) - 4x fewer pixels!\n const dpr = skipDprScaling ? 1 : window.devicePixelRatio || 1;\n\n // Use provided canvas or create new one\n let captureCanvas: HTMLCanvasElement;\n let shouldCleanup = false;\n\n if (reuseCanvas) {\n captureCanvas = reuseCanvas;\n\n // Ensure canvas dimensions match (both attribute and CSS)\n const dpr = skipDprScaling ? 1 : window.devicePixelRatio || 1;\n const targetWidth = Math.floor(width * dpr);\n const targetHeight = Math.floor(height * dpr);\n\n // Set attribute dimensions (pixel buffer size)\n if (captureCanvas.width !== targetWidth) {\n captureCanvas.width = targetWidth;\n }\n if (captureCanvas.height !== targetHeight) {\n captureCanvas.height = targetHeight;\n }\n\n // Ensure CSS dimensions and positioning (same as non-reuse path)\n // This ensures consistent behavior and avoids layout issues\n captureCanvas.style.cssText = `\n position: fixed;\n left: 0;\n top: 0;\n width: ${width}px;\n height: ${height}px;\n opacity: 0;\n pointer-events: none;\n z-index: -9999;\n `;\n\n // Ensure layoutsubtree is set (required for drawElementImage)\n if (!captureCanvas.hasAttribute(\"layoutsubtree\")) {\n captureCanvas.setAttribute(\"layoutsubtree\", \"\");\n (captureCanvas as HtmlInCanvasElement).layoutSubtree = true;\n }\n\n // Ensure canvas is in DOM (required for drawElementImage layout)\n if (!captureCanvas.parentNode) {\n document.body.appendChild(captureCanvas);\n }\n\n // Ensure container is child of canvas\n if (container.parentElement !== captureCanvas) {\n captureCanvas.appendChild(container);\n }\n\n // Ensure container is visible (not display: none) for layout\n // drawElementImage requires the element to be laid out\n const containerStyle = getComputedStyle(container);\n if (containerStyle.display === \"none\") {\n container.style.display = \"block\";\n }\n\n // Force synchronous layout ONLY on first use with this canvas\n // For batch rendering (video export), repeated layout forces are expensive\n // We only need to force layout once to ensure everything is ready\n if (!_layoutInitializedCanvases.has(captureCanvas)) {\n void captureCanvas.offsetHeight;\n void container.offsetHeight;\n getComputedStyle(captureCanvas).opacity;\n getComputedStyle(container).opacity;\n _layoutInitializedCanvases.add(captureCanvas);\n }\n } else {\n captureCanvas = document.createElement(\"canvas\");\n captureCanvas.width = Math.floor(width * dpr);\n captureCanvas.height = Math.floor(height * dpr);\n\n // Enable HTML-in-Canvas mode via layoutsubtree attribute/property\n captureCanvas.setAttribute(\"layoutsubtree\", \"\");\n (captureCanvas as HtmlInCanvasElement).layoutSubtree = true;\n\n captureCanvas.appendChild(container);\n\n captureCanvas.style.cssText = `\n position: fixed;\n left: 0;\n top: 0;\n width: ${width}px;\n height: ${height}px;\n opacity: 0;\n pointer-events: none;\n z-index: -9999;\n `;\n document.body.appendChild(captureCanvas);\n shouldCleanup = true;\n }\n\n const t1 = performance.now();\n defaultProfiler.addTime(\"setup\", t1 - t0);\n\n try {\n // Force style calculation to ensure CSS is computed before capture\n // This ensures both canvas and container are laid out (required for drawElementImage)\n getComputedStyle(container).opacity;\n\n // When reusing canvas with layoutsubtree, wait for initial layout (first use only)\n // Use a WeakSet to track canvases that have been initialized\n if (\n reuseCanvas &&\n (captureCanvas as any).layoutSubtree &&\n !_layoutInitializedCanvases.has(captureCanvas)\n ) {\n await waitForFrame();\n _layoutInitializedCanvases.add(captureCanvas);\n\n // Canvas may have been detached during async wait (e.g., test cleanup)\n if (!captureCanvas.parentNode) {\n return captureCanvas;\n }\n }\n\n const ctx = captureCanvas.getContext(\"2d\") as HtmlInCanvasContext;\n ctx.drawElementImage(container, 0, 0);\n } finally {\n // Only clean up if we created the canvas\n if (shouldCleanup && captureCanvas.parentNode) {\n captureCanvas.parentNode.removeChild(captureCanvas);\n }\n }\n\n const t2 = performance.now();\n defaultProfiler.addTime(\"draw\", t2 - t1);\n\n // If DPR is 1, no downsampling needed - return as-is\n if (dpr === 1) {\n defaultProfiler.incrementRenderCount();\n return captureCanvas;\n }\n\n // Downsample to logical pixel dimensions to match foreignObject path output\n // This ensures consistent behavior regardless of which rendering path is used\n const outputCanvas = document.createElement(\"canvas\");\n outputCanvas.width = width;\n outputCanvas.height = height;\n\n const outputCtx = outputCanvas.getContext(\"2d\")!;\n // Draw the DPR-scaled capture onto the 1x output canvas\n outputCtx.drawImage(\n captureCanvas,\n 0,\n 0,\n captureCanvas.width,\n captureCanvas.height, // source (full DPR capture)\n 0,\n 0,\n width,\n height, // destination (logical pixels)\n );\n\n const t3 = performance.now();\n defaultProfiler.addTime(\"downsample\", t3 - t2);\n defaultProfiler.incrementRenderCount();\n\n return outputCanvas;\n}\n"],"mappings":"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"}
|