@editframe/elements 0.30.1-beta.0 → 0.31.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/EF_FRAMEGEN.d.ts +5 -0
- package/dist/EF_FRAMEGEN.js +20 -4
- package/dist/EF_FRAMEGEN.js.map +1 -1
- package/dist/EF_INTERACTIVE.js.map +1 -1
- package/dist/_virtual/rolldown_runtime.js +27 -0
- package/dist/canvas/EFCanvas.d.ts +311 -0
- package/dist/canvas/EFCanvas.js +1089 -0
- package/dist/canvas/EFCanvas.js.map +1 -0
- package/dist/canvas/EFCanvasItem.d.ts +55 -0
- package/dist/canvas/EFCanvasItem.js +72 -0
- package/dist/canvas/EFCanvasItem.js.map +1 -0
- package/dist/canvas/api/CanvasAPI.d.ts +115 -0
- package/dist/canvas/api/CanvasAPI.js +182 -0
- package/dist/canvas/api/CanvasAPI.js.map +1 -0
- package/dist/canvas/api/types.d.ts +42 -0
- package/dist/canvas/coordinateTransform.js +90 -0
- package/dist/canvas/coordinateTransform.js.map +1 -0
- package/dist/canvas/getElementBounds.js +40 -0
- package/dist/canvas/getElementBounds.js.map +1 -0
- package/dist/canvas/overlays/SelectionOverlay.js +265 -0
- package/dist/canvas/overlays/SelectionOverlay.js.map +1 -0
- package/dist/canvas/overlays/overlayState.js +153 -0
- package/dist/canvas/overlays/overlayState.js.map +1 -0
- package/dist/canvas/selection/SelectionController.js +105 -0
- package/dist/canvas/selection/SelectionController.js.map +1 -0
- package/dist/canvas/selection/SelectionModel.d.ts +98 -0
- package/dist/canvas/selection/SelectionModel.js +229 -0
- package/dist/canvas/selection/SelectionModel.js.map +1 -0
- package/dist/canvas/selection/selectionContext.d.ts +31 -0
- package/dist/canvas/selection/selectionContext.js +12 -0
- package/dist/canvas/selection/selectionContext.js.map +1 -0
- package/dist/elements/ContainerInfo.d.ts +29 -0
- package/dist/elements/ContainerInfo.js +30 -0
- package/dist/elements/ContainerInfo.js.map +1 -0
- package/dist/elements/EFAudio.d.ts +13 -3
- package/dist/elements/EFAudio.js +64 -10
- package/dist/elements/EFAudio.js.map +1 -1
- package/dist/elements/EFCaptions.d.ts +18 -16
- package/dist/elements/EFCaptions.js +110 -19
- package/dist/elements/EFCaptions.js.map +1 -1
- package/dist/elements/EFImage.d.ts +16 -6
- package/dist/elements/EFImage.js +79 -9
- package/dist/elements/EFImage.js.map +1 -1
- package/dist/elements/EFMedia/AssetIdMediaEngine.js +51 -4
- package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/AssetMediaEngine.js +125 -52
- package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/BaseMediaEngine.js +24 -6
- package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/JitMediaEngine.js +12 -8
- package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +46 -7
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +98 -73
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +28 -5
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +18 -6
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +8 -2
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +31 -6
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +28 -5
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +97 -72
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js.map +1 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/BufferUtils.js +1 -1
- package/dist/elements/EFMedia/shared/BufferUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +25 -14
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +47 -16
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +37 -19
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +65 -21
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +8 -3
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js +32 -9
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +33 -10
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +23 -8
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js +34 -10
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +31 -8
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +31 -114
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +44 -8
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js.map +1 -1
- package/dist/elements/EFMedia.d.ts +18 -7
- package/dist/elements/EFMedia.js +23 -3
- package/dist/elements/EFMedia.js.map +1 -1
- package/dist/elements/EFPanZoom.d.ts +96 -0
- package/dist/elements/EFPanZoom.js +290 -0
- package/dist/elements/EFPanZoom.js.map +1 -0
- package/dist/elements/EFSourceMixin.js +7 -6
- package/dist/elements/EFSourceMixin.js.map +1 -1
- package/dist/elements/EFSurface.d.ts +6 -6
- package/dist/elements/EFSurface.js +7 -2
- package/dist/elements/EFSurface.js.map +1 -1
- package/dist/elements/EFTemporal.d.ts +2 -1
- package/dist/elements/EFTemporal.js +192 -71
- package/dist/elements/EFTemporal.js.map +1 -1
- package/dist/elements/EFText.d.ts +5 -4
- package/dist/elements/EFText.js +102 -13
- package/dist/elements/EFText.js.map +1 -1
- package/dist/elements/EFTextSegment.d.ts +32 -6
- package/dist/elements/EFTextSegment.js +53 -15
- package/dist/elements/EFTextSegment.js.map +1 -1
- package/dist/elements/EFThumbnailStrip.d.ts +118 -56
- package/dist/elements/EFThumbnailStrip.js +522 -358
- package/dist/elements/EFThumbnailStrip.js.map +1 -1
- package/dist/elements/EFTimegroup.d.ts +223 -27
- package/dist/elements/EFTimegroup.js +851 -148
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/dist/elements/EFVideo.d.ts +42 -5
- package/dist/elements/EFVideo.js +165 -11
- package/dist/elements/EFVideo.js.map +1 -1
- package/dist/elements/EFWaveform.d.ts +6 -6
- package/dist/elements/EFWaveform.js +2 -1
- package/dist/elements/EFWaveform.js.map +1 -1
- package/dist/elements/ElementPositionInfo.d.ts +35 -0
- package/dist/elements/ElementPositionInfo.js +49 -0
- package/dist/elements/ElementPositionInfo.js.map +1 -0
- package/dist/elements/FetchMixin.js +16 -1
- package/dist/elements/FetchMixin.js.map +1 -1
- package/dist/elements/SessionThumbnailCache.js +152 -0
- package/dist/elements/SessionThumbnailCache.js.map +1 -0
- package/dist/elements/TargetController.js +3 -1
- package/dist/elements/TargetController.js.map +1 -1
- package/dist/elements/TimegroupController.js +9 -3
- package/dist/elements/TimegroupController.js.map +1 -1
- package/dist/elements/findRootTemporal.js +30 -0
- package/dist/elements/findRootTemporal.js.map +1 -0
- package/dist/elements/renderTemporalAudio.js +18 -5
- package/dist/elements/renderTemporalAudio.js.map +1 -1
- package/dist/elements/updateAnimations.js +492 -109
- package/dist/elements/updateAnimations.js.map +1 -1
- package/dist/getRenderInfo.d.ts +2 -2
- package/dist/gui/ContextMixin.js +4 -2
- package/dist/gui/ContextMixin.js.map +1 -1
- package/dist/gui/Controllable.js +74 -1
- package/dist/gui/Controllable.js.map +1 -1
- package/dist/gui/EFActiveRootTemporal.d.ts +50 -0
- package/dist/gui/EFActiveRootTemporal.js +94 -0
- package/dist/gui/EFActiveRootTemporal.js.map +1 -0
- package/dist/gui/EFConfiguration.d.ts +11 -5
- package/dist/gui/EFConfiguration.js.map +1 -1
- package/dist/gui/EFControls.d.ts +2 -2
- package/dist/gui/EFControls.js +109 -13
- package/dist/gui/EFControls.js.map +1 -1
- package/dist/gui/EFDial.d.ts +4 -4
- package/dist/gui/EFFilmstrip.d.ts +11 -214
- package/dist/gui/EFFilmstrip.js +53 -1152
- package/dist/gui/EFFilmstrip.js.map +1 -1
- package/dist/gui/EFFitScale.d.ts +3 -3
- package/dist/gui/EFFitScale.js +39 -12
- package/dist/gui/EFFitScale.js.map +1 -1
- package/dist/gui/EFFocusOverlay.d.ts +4 -4
- package/dist/gui/EFOverlayItem.d.ts +48 -0
- package/dist/gui/EFOverlayItem.js +97 -0
- package/dist/gui/EFOverlayItem.js.map +1 -0
- package/dist/gui/EFOverlayLayer.d.ts +70 -0
- package/dist/gui/EFOverlayLayer.js +104 -0
- package/dist/gui/EFOverlayLayer.js.map +1 -0
- package/dist/gui/EFPause.d.ts +4 -4
- package/dist/gui/EFPlay.d.ts +4 -4
- package/dist/gui/EFPreview.d.ts +4 -4
- package/dist/gui/EFResizableBox.d.ts +12 -16
- package/dist/gui/EFResizableBox.js +109 -451
- package/dist/gui/EFResizableBox.js.map +1 -1
- package/dist/gui/EFScrubber.d.ts +30 -5
- package/dist/gui/EFScrubber.js +224 -31
- package/dist/gui/EFScrubber.js.map +1 -1
- package/dist/gui/EFTimeDisplay.d.ts +4 -4
- package/dist/gui/EFTimeDisplay.js +4 -1
- package/dist/gui/EFTimeDisplay.js.map +1 -1
- package/dist/gui/EFTimelineRuler.d.ts +71 -0
- package/dist/gui/EFTimelineRuler.js +320 -0
- package/dist/gui/EFTimelineRuler.js.map +1 -0
- package/dist/gui/EFToggleLoop.d.ts +4 -4
- package/dist/gui/EFTogglePlay.d.ts +4 -4
- package/dist/gui/EFTransformHandles.d.ts +91 -0
- package/dist/gui/EFTransformHandles.js +393 -0
- package/dist/gui/EFTransformHandles.js.map +1 -0
- package/dist/gui/EFWorkbench.d.ts +182 -4
- package/dist/gui/EFWorkbench.js +2067 -22
- package/dist/gui/EFWorkbench.js.map +1 -1
- package/dist/gui/FitScaleHelpers.d.ts +31 -0
- package/dist/gui/FitScaleHelpers.js +41 -0
- package/dist/gui/FitScaleHelpers.js.map +1 -0
- package/dist/gui/PlaybackController.d.ts +2 -1
- package/dist/gui/PlaybackController.js +46 -15
- package/dist/gui/PlaybackController.js.map +1 -1
- package/dist/gui/TWMixin.js +1 -1
- package/dist/gui/TWMixin.js.map +1 -1
- package/dist/gui/hierarchy/EFHierarchy.d.ts +65 -0
- package/dist/gui/hierarchy/EFHierarchy.js +338 -0
- package/dist/gui/hierarchy/EFHierarchy.js.map +1 -0
- package/dist/gui/hierarchy/EFHierarchyItem.d.ts +118 -0
- package/dist/gui/hierarchy/EFHierarchyItem.js +551 -0
- package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -0
- package/dist/gui/hierarchy/hierarchyContext.d.ts +38 -0
- package/dist/gui/hierarchy/hierarchyContext.js +8 -0
- package/dist/gui/hierarchy/hierarchyContext.js.map +1 -0
- package/dist/gui/icons.js +34 -0
- package/dist/gui/icons.js.map +1 -0
- package/dist/gui/panZoomTransformContext.js +12 -0
- package/dist/gui/panZoomTransformContext.js.map +1 -0
- package/dist/gui/previewSettingsContext.js +12 -0
- package/dist/gui/previewSettingsContext.js.map +1 -0
- package/dist/gui/timeline/EFTimeline.d.ts +270 -0
- package/dist/gui/timeline/EFTimeline.js +1369 -0
- package/dist/gui/timeline/EFTimeline.js.map +1 -0
- package/dist/gui/timeline/EFTimelineRow.js +374 -0
- package/dist/gui/timeline/EFTimelineRow.js.map +1 -0
- package/dist/gui/timeline/TrimHandles.d.ts +36 -0
- package/dist/gui/timeline/TrimHandles.js +204 -0
- package/dist/gui/timeline/TrimHandles.js.map +1 -0
- package/dist/gui/timeline/flattenHierarchy.js +31 -0
- package/dist/gui/timeline/flattenHierarchy.js.map +1 -0
- package/dist/gui/timeline/timelineStateContext.d.ts +26 -0
- package/dist/gui/timeline/timelineStateContext.js +42 -0
- package/dist/gui/timeline/timelineStateContext.js.map +1 -0
- package/dist/gui/timeline/tracks/AudioTrack.js +264 -0
- package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/CaptionsTrack.js +595 -0
- package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/HTMLTrack.js +19 -0
- package/dist/gui/timeline/tracks/HTMLTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/ImageTrack.js +53 -0
- package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TextTrack.js +250 -0
- package/dist/gui/timeline/tracks/TextTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TimegroupTrack.js +143 -0
- package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TrackItem.js +269 -0
- package/dist/gui/timeline/tracks/TrackItem.js.map +1 -0
- package/dist/gui/timeline/tracks/VideoTrack.js +265 -0
- package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/WaveformTrack.js +19 -0
- package/dist/gui/timeline/tracks/WaveformTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/ensureTrackItemInit.js +1 -0
- package/dist/gui/timeline/tracks/preloadTracks.js +9 -0
- package/dist/gui/timeline/tracks/renderTrackChildren.js +119 -0
- package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -0
- package/dist/gui/timeline/tracks/waveformUtils.js +80 -0
- package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -0
- package/dist/gui/transformCalculations.js +217 -0
- package/dist/gui/transformCalculations.js.map +1 -0
- package/dist/gui/transformUtils.d.ts +37 -0
- package/dist/gui/transformUtils.js +77 -0
- package/dist/gui/transformUtils.js.map +1 -0
- package/dist/gui/tree/EFTree.d.ts +59 -0
- package/dist/gui/tree/EFTree.js +174 -0
- package/dist/gui/tree/EFTree.js.map +1 -0
- package/dist/gui/tree/EFTreeItem.d.ts +38 -0
- package/dist/gui/tree/EFTreeItem.js +146 -0
- package/dist/gui/tree/EFTreeItem.js.map +1 -0
- package/dist/gui/tree/treeContext.d.ts +60 -0
- package/dist/gui/tree/treeContext.js +23 -0
- package/dist/gui/tree/treeContext.js.map +1 -0
- package/dist/index.d.ts +32 -8
- package/dist/index.js +30 -6
- package/dist/index.js.map +1 -1
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +688 -0
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +1 -0
- package/dist/node_modules/react/cjs/react.development.js +1521 -0
- package/dist/node_modules/react/cjs/react.development.js.map +1 -0
- package/dist/node_modules/react/index.js +13 -0
- package/dist/node_modules/react/index.js.map +1 -0
- package/dist/node_modules/react/jsx-runtime.js +13 -0
- package/dist/node_modules/react/jsx-runtime.js.map +1 -0
- package/dist/preview/AdaptiveResolutionTracker.js +228 -0
- package/dist/preview/AdaptiveResolutionTracker.js.map +1 -0
- package/dist/preview/RenderProfiler.js +135 -0
- package/dist/preview/RenderProfiler.js.map +1 -0
- package/dist/preview/previewSettings.js +131 -0
- package/dist/preview/previewSettings.js.map +1 -0
- package/dist/preview/previewTypes.js +64 -0
- package/dist/preview/previewTypes.js.map +1 -0
- package/dist/preview/renderTimegroupPreview.js +656 -0
- package/dist/preview/renderTimegroupPreview.js.map +1 -0
- package/dist/preview/renderTimegroupToCanvas.d.ts +37 -0
- package/dist/preview/renderTimegroupToCanvas.js +840 -0
- package/dist/preview/renderTimegroupToCanvas.js.map +1 -0
- package/dist/preview/renderTimegroupToVideo.d.ts +39 -0
- package/dist/preview/renderTimegroupToVideo.js +274 -0
- package/dist/preview/renderTimegroupToVideo.js.map +1 -0
- package/dist/preview/renderers.js +16 -0
- package/dist/preview/renderers.js.map +1 -0
- package/dist/preview/statsTrackingStrategy.js +201 -0
- package/dist/preview/statsTrackingStrategy.js.map +1 -0
- package/dist/preview/thumbnailCacheSettings.js +52 -0
- package/dist/preview/thumbnailCacheSettings.js.map +1 -0
- package/dist/preview/workers/WorkerPool.js +178 -0
- package/dist/preview/workers/WorkerPool.js.map +1 -0
- package/dist/sandbox/PlaybackControls.js +10 -0
- package/dist/sandbox/PlaybackControls.js.map +1 -0
- package/dist/sandbox/ScenarioRunner.js +1 -0
- package/dist/sandbox/index.js +2 -0
- package/dist/style.css +66 -69
- package/dist/transcoding/types/index.d.ts +2 -1
- package/dist/transcoding/utils/UrlGenerator.d.ts +6 -1
- package/dist/transcoding/utils/UrlGenerator.js +12 -3
- package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
- package/dist/utils/LRUCache.js +1 -375
- package/dist/utils/LRUCache.js.map +1 -1
- package/dist/utils/frameTime.js +14 -0
- package/dist/utils/frameTime.js.map +1 -0
- package/package.json +3 -3
- package/test/profilingPlugin.ts +223 -0
- package/test/recordReplayProxyPlugin.js +22 -27
- package/test/thumbnail-performance-test.html +116 -0
- package/test/visualRegressionUtils.ts +286 -0
- package/types.json +1 -1
- package/dist/elements/TimegroupController.d.ts +0 -18
- package/dist/msToTimeCode.js +0 -17
- package/dist/msToTimeCode.js.map +0 -1
package/dist/EF_FRAMEGEN.d.ts
CHANGED
|
@@ -47,6 +47,11 @@ declare class EFFramegen {
|
|
|
47
47
|
private initializeVerificationCanvas;
|
|
48
48
|
private drawVerificationStrip;
|
|
49
49
|
constructor();
|
|
50
|
+
/**
|
|
51
|
+
* Helper method to get the workbench and set its rendering state.
|
|
52
|
+
* This ensures consistent state management across the framegen lifecycle.
|
|
53
|
+
*/
|
|
54
|
+
private setWorkbenchRendering;
|
|
50
55
|
connectToBridge(): void;
|
|
51
56
|
get showFrameBox(): boolean;
|
|
52
57
|
initialize(renderOptions: VideoRenderOptions): Promise<void>;
|
package/dist/EF_FRAMEGEN.js
CHANGED
|
@@ -92,6 +92,14 @@ var EFFramegen = class {
|
|
|
92
92
|
this.BRIDGE = window.FRAMEGEN_BRIDGE;
|
|
93
93
|
if (this.BRIDGE) this.connectToBridge();
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Helper method to get the workbench and set its rendering state.
|
|
97
|
+
* This ensures consistent state management across the framegen lifecycle.
|
|
98
|
+
*/
|
|
99
|
+
setWorkbenchRendering(isRendering) {
|
|
100
|
+
const workbench = document.querySelector("ef-workbench");
|
|
101
|
+
if (workbench) workbench.rendering = isRendering;
|
|
102
|
+
}
|
|
95
103
|
connectToBridge() {
|
|
96
104
|
const BRIDGE = this.BRIDGE;
|
|
97
105
|
if (!BRIDGE) throw new Error("No BRIDGE when attempting to connect to bridge");
|
|
@@ -112,10 +120,13 @@ var EFFramegen = class {
|
|
|
112
120
|
fps: renderOptions.encoderOptions.video.framerate,
|
|
113
121
|
durationMs: renderOptions.encoderOptions.toMs - renderOptions.encoderOptions.fromMs
|
|
114
122
|
}, parentContext, async () => {
|
|
115
|
-
|
|
123
|
+
try {
|
|
124
|
+
await this.initialize(renderOptions);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
this.setWorkbenchRendering(false);
|
|
116
127
|
console.error("[EF_FRAMEGEN.connectToBridge] error initializing", error);
|
|
117
128
|
throw error;
|
|
118
|
-
}
|
|
129
|
+
}
|
|
119
130
|
});
|
|
120
131
|
BRIDGE.initialized();
|
|
121
132
|
});
|
|
@@ -128,11 +139,15 @@ var EFFramegen = class {
|
|
|
128
139
|
setCurrentFrameSpan(span);
|
|
129
140
|
try {
|
|
130
141
|
await this.beginFrame(frameNumber, isLast);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
this.setWorkbenchRendering(false);
|
|
144
|
+
throw error;
|
|
131
145
|
} finally {
|
|
132
146
|
clearCurrentFrameSpan();
|
|
133
147
|
}
|
|
134
148
|
}).catch((error) => {
|
|
135
149
|
console.error("[EF_FRAMEGEN.beginFrame] error:", error);
|
|
150
|
+
this.setWorkbenchRendering(false);
|
|
136
151
|
clearCurrentFrameSpan();
|
|
137
152
|
throw error;
|
|
138
153
|
});
|
|
@@ -152,7 +167,7 @@ var EFFramegen = class {
|
|
|
152
167
|
this.renderOptions = renderOptions;
|
|
153
168
|
const workbench = document.querySelector("ef-workbench");
|
|
154
169
|
if (!workbench) throw new Error("No workbench found");
|
|
155
|
-
|
|
170
|
+
this.setWorkbenchRendering(true);
|
|
156
171
|
workbench.playing = false;
|
|
157
172
|
const firstGroup = shallowGetTimegroups(workbench)[0];
|
|
158
173
|
if (!firstGroup) throw new Error("No temporal elements found");
|
|
@@ -181,7 +196,7 @@ var EFFramegen = class {
|
|
|
181
196
|
if (this.renderOptions === void 0) throw new Error("No renderOptions");
|
|
182
197
|
const workbench = document.querySelector("ef-workbench");
|
|
183
198
|
if (!workbench) throw new Error("No workbench found");
|
|
184
|
-
|
|
199
|
+
this.setWorkbenchRendering(true);
|
|
185
200
|
const firstGroup = shallowGetTimegroups(workbench)[0];
|
|
186
201
|
if (!firstGroup) throw new Error("No temporal elements found");
|
|
187
202
|
const frameTime = this.renderOptions.encoderOptions.fromMs + frameNumber * this.frameDurationMs;
|
|
@@ -209,6 +224,7 @@ var EFFramegen = class {
|
|
|
209
224
|
});
|
|
210
225
|
return fileReader.result;
|
|
211
226
|
}
|
|
227
|
+
this.setWorkbenchRendering(false);
|
|
212
228
|
} else if (this.BRIDGE) this.BRIDGE.frameReady(frameNumber, /* @__PURE__ */ new ArrayBuffer(0));
|
|
213
229
|
else {
|
|
214
230
|
const fileReader = new FileReader();
|
package/dist/EF_FRAMEGEN.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EF_FRAMEGEN.js","names":[],"sources":["../src/EF_FRAMEGEN.ts"],"sourcesContent":["import type { VideoRenderOptions } from \"@editframe/assets\";\n\nimport { shallowGetTimegroups } from \"./elements/EFTimegroup.js\";\nimport { setupBrowserTracing } from \"./otel/setupBrowserTracing.js\";\nimport {\n clearCurrentFrameSpan,\n enableTracing,\n extractParentContext,\n setCurrentFrameSpan,\n type TraceContext,\n withSpan,\n withSpanAndContext,\n} from \"./otel/tracingHelpers.js\";\n\ninterface Bridge {\n onInitialize: (\n callback: (\n renderOptions: VideoRenderOptions,\n traceContext?: TraceContext,\n otelEndpoint?: string,\n ) => void,\n ) => void;\n\n initialized(): void;\n\n onBeginFrame(\n callback: (\n frameNumber: number,\n isLast: boolean,\n traceContext?: TraceContext,\n ) => void,\n ): void;\n\n onTriggerCanvas(callback: (traceContext?: TraceContext) => void): void;\n\n frameReady(frameNumber: number, audioSamples: ArrayBuffer): void;\n\n error(error: Error): void;\n\n syncLog(sequence: number, message: string, callback: () => void): void;\n\n exportSpans?: (endpoint: string, payload: string) => void;\n}\n\ndeclare global {\n interface Window {\n EF_FRAMEGEN?: EFFramegen;\n FRAMEGEN_BRIDGE?: Bridge;\n FRAMEGEN_BINDING?: any;\n FRAMEGEN_BINDING_error?: (error: Error) => void;\n EF_RENDERING?: () => boolean;\n }\n}\n\nclass TriggerCanvas {\n private canvas: HTMLCanvasElement;\n private ctx: CanvasRenderingContext2D;\n\n private canvasInitialized = false;\n\n constructor() {\n this.canvas = document.createElement(\"canvas\");\n const ctx = this.canvas.getContext(\"2d\", { willReadFrequently: true });\n if (!ctx) throw new Error(\"Canvas 2d context not ready\");\n this.ctx = ctx;\n this.ctx.fillStyle = \"transparent\";\n }\n\n initialize() {\n if (this.canvasInitialized) return;\n this.canvasInitialized = true;\n this.canvas.width = 1;\n this.canvas.height = 1;\n Object.assign(this.canvas.style, {\n position: \"fixed\",\n top: \"0px\",\n left: \"0px\",\n width: \"100%\",\n height: \"100%\",\n zIndex: \"100000\",\n });\n document.body.appendChild(this.canvas);\n }\n\n trigger() {\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n }\n}\n\nexport class EFFramegen {\n time = 0;\n frameDurationMs = 0;\n audioBufferPromise?: Promise<AudioBuffer>;\n renderOptions?: VideoRenderOptions;\n frameBox = document.createElement(\"div\");\n BRIDGE: typeof window.FRAMEGEN_BRIDGE;\n triggerCanvas = new TriggerCanvas();\n verificationCanvas?: HTMLCanvasElement;\n verificationCtx?: CanvasRenderingContext2D;\n private logSequence = 0;\n\n // Frame sequence coordination\n public frameTasksInProgress = false;\n public currentFrameNumber = 0;\n\n trace(...args: any[]) {\n console.trace(\"[EF_FRAMEGEN]\", ...args);\n }\n\n async syncLog(...args: any[]): Promise<void> {\n if (!this.BRIDGE) {\n // Fallback to regular console.log if no bridge\n console.log(\"[EF_FRAMEGEN]\", ...args);\n return;\n }\n\n const sequence = ++this.logSequence;\n const message = args\n .map((arg) =>\n typeof arg === \"object\" ? JSON.stringify(arg) : String(arg),\n )\n .join(\" \");\n\n return new Promise<void>((resolve) => {\n // biome-ignore lint/style/noNonNullAssertion: We know BRIDGE is set due to the guard above\n this.BRIDGE!.syncLog(sequence, message, () => {\n resolve();\n });\n });\n }\n\n private initializeVerificationCanvas() {\n if (this.verificationCanvas) {\n return;\n }\n\n this.verificationCanvas = document.createElement(\"canvas\");\n const ctx = this.verificationCanvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"Verification canvas 2d context not ready\");\n this.verificationCtx = ctx;\n\n // Size to match the workbench width, 1 pixel height for verification strip\n const workbench = document.querySelector(\"ef-workbench\") as HTMLElement;\n if (workbench) {\n this.verificationCanvas.width = workbench.clientWidth;\n this.verificationCanvas.height = 1;\n\n // Position at the bottom of the workbench (beyond content height)\n Object.assign(this.verificationCanvas.style, {\n position: \"fixed\",\n left: \"0px\",\n bottom: \"0px\",\n width: `${workbench.clientWidth}px`,\n height: \"1px\",\n zIndex: \"99999\", // Above trigger canvas\n });\n\n document.body.appendChild(this.verificationCanvas);\n }\n }\n\n private drawVerificationStrip(frameNumber: number) {\n this.initializeVerificationCanvas();\n\n if (!this.verificationCanvas || !this.verificationCtx) {\n return;\n }\n\n const width = this.verificationCanvas.width;\n const height = this.verificationCanvas.height;\n\n // Clear the strip\n this.verificationCtx.clearRect(0, 0, width, height);\n\n // Encode frame number into RGB (24-bit)\n // R=high byte, G=middle byte, B=low byte\n const red = Math.floor(frameNumber / (256 * 256)) % 256;\n const green = Math.floor(frameNumber / 256) % 256;\n const blue = frameNumber % 256;\n\n // Fill the entire strip with the encoded frame number\n this.verificationCtx.fillStyle = `rgb(${red}, ${green}, ${blue})`;\n this.verificationCtx.fillRect(0, 0, width, height);\n }\n\n constructor() {\n this.BRIDGE = window.FRAMEGEN_BRIDGE;\n if (this.BRIDGE) {\n this.connectToBridge();\n }\n }\n\n connectToBridge() {\n const BRIDGE = this.BRIDGE;\n if (!BRIDGE) {\n throw new Error(\"No BRIDGE when attempting to connect to bridge\");\n }\n\n BRIDGE.onInitialize(async (renderOptions, traceContext, otelEndpoint) => {\n // Only enable tracing if explicitly requested in renderOptions\n if (renderOptions.enableTracing && otelEndpoint) {\n enableTracing();\n await setupBrowserTracing({\n otelEndpoint,\n serviceName: \"telecine-browser\",\n bridge: BRIDGE,\n useBatching: true, // Batch spans to reduce overhead during rendering\n });\n }\n\n const parentContext = extractParentContext(traceContext);\n\n await withSpan(\n \"browser.initialize\",\n {\n width: renderOptions.encoderOptions.video.width,\n height: renderOptions.encoderOptions.video.height,\n fps: renderOptions.encoderOptions.video.framerate,\n durationMs:\n renderOptions.encoderOptions.toMs -\n renderOptions.encoderOptions.fromMs,\n },\n parentContext,\n async () => {\n await this.initialize(renderOptions).catch((error) => {\n console.error(\n \"[EF_FRAMEGEN.connectToBridge] error initializing\",\n error,\n );\n throw error;\n });\n },\n );\n\n BRIDGE.initialized();\n });\n\n BRIDGE.onBeginFrame((frameNumber, isLast, traceContext) => {\n const parentContext = extractParentContext(traceContext);\n withSpanAndContext(\n \"browser.frame.render\",\n {\n frameNumber,\n isLast,\n },\n parentContext,\n async (span, _spanContext) => {\n // Store the span itself for child operations\n // This allows spans created in Lit Tasks to use it as their parent\n setCurrentFrameSpan(span);\n\n try {\n await this.beginFrame(frameNumber, isLast);\n } finally {\n clearCurrentFrameSpan();\n }\n },\n ).catch((error) => {\n console.error(\"[EF_FRAMEGEN.beginFrame] error:\", error);\n clearCurrentFrameSpan();\n throw error;\n });\n });\n\n BRIDGE.onTriggerCanvas((traceContext) => {\n const parentContext = extractParentContext(traceContext);\n\n withSpan(\"browser.canvas.trigger\", {}, parentContext, async () => {\n this.triggerCanvas.trigger();\n }).catch((error) => {\n console.error(\"[EF_FRAMEGEN.triggerCanvas] error:\", error);\n });\n });\n }\n\n get showFrameBox() {\n return this.renderOptions?.showFrameBox ?? false;\n }\n\n async initialize(renderOptions: VideoRenderOptions) {\n this.renderOptions = renderOptions;\n\n const workbench = document.querySelector(\"ef-workbench\");\n if (!workbench) {\n throw new Error(\"No workbench found\");\n }\n workbench.rendering = true;\n workbench.playing = false;\n const timegroups = shallowGetTimegroups(workbench);\n const firstGroup = timegroups[0];\n if (!firstGroup) {\n throw new Error(\"No temporal elements found\");\n }\n const startingTimeMs = renderOptions.encoderOptions.fromMs;\n await firstGroup.waitForMediaDurations();\n await firstGroup.waitForFrameTasks();\n\n this.frameDurationMs = 1000 / renderOptions.encoderOptions.video.framerate;\n\n this.time = startingTimeMs;\n if (this.showFrameBox) {\n Object.assign(this.frameBox.style, {\n width: \"200px\",\n height: \"100px\",\n font: \"10px Arial\",\n backgroundColor: \"white\",\n position: \"absolute\",\n top: \"0px\",\n right: \"0px\",\n zIndex: \"100000\",\n });\n document.body.prepend(this.frameBox);\n }\n\n this.triggerCanvas.initialize();\n\n // These times are aligned to the audio frame boundaries\n // And they include padding if any.\n this.audioBufferPromise = firstGroup.renderAudio(\n renderOptions.encoderOptions.alignedFromUs / 1000,\n renderOptions.encoderOptions.alignedToUs / 1000,\n );\n }\n\n async beginFrame(frameNumber: number, isLast: boolean) {\n if (this.renderOptions === undefined) {\n throw new Error(\"No renderOptions\");\n }\n const workbench = document.querySelector(\"ef-workbench\");\n if (!workbench) {\n throw new Error(\"No workbench found\");\n }\n workbench.rendering = true;\n const timegroups = shallowGetTimegroups(workbench);\n const firstGroup = timegroups[0];\n if (!firstGroup) {\n throw new Error(\"No temporal elements found\");\n }\n\n // Calculate base frame time using normal progression\n const frameTime =\n this.renderOptions.encoderOptions.fromMs +\n frameNumber * this.frameDurationMs;\n firstGroup.currentTimeMs = Number(Number(frameTime).toFixed(5));\n await firstGroup.waitForFrameTasks();\n if (this.showFrameBox) {\n this.frameBox.innerHTML = `\n <div>🖼️ Frame: ${frameNumber}</div>\n <div>🕛 Segment: ${this.time.toFixed(4)}</div>\n <div>🕛 Frame: ${firstGroup.currentTimeMs.toFixed(4)}</div>\n <div> from-to: ${this.renderOptions.encoderOptions.fromMs.toFixed(4)} - ${this.renderOptions.encoderOptions.toMs.toFixed(4)}</div>\n `;\n }\n\n // Draw verification pixel strip for frame verification\n this.drawVerificationStrip(frameNumber);\n\n if (isLast && this.audioBufferPromise) {\n // Currently we emit the audio in one belch at the end of the render.\n // This is not ideal, but it's the simplest thing that could possibly work.\n // We could either emit it slices, or in parallel with the video.\n // But in any case, it's fine for now.\n const renderedAudio = await this.audioBufferPromise;\n\n const channelCount = renderedAudio.numberOfChannels;\n\n const interleavedSamples = new Float32Array(\n channelCount * renderedAudio.length,\n );\n\n for (let i = 0; i < renderedAudio.length; i++) {\n for (let j = 0; j < channelCount; j++) {\n interleavedSamples.set(\n renderedAudio.getChannelData(j).slice(i, i + 1),\n i * channelCount + j,\n );\n }\n }\n\n if (this.BRIDGE) {\n this.BRIDGE.frameReady(frameNumber, interleavedSamples.buffer);\n } else {\n const fileReader = new FileReader();\n fileReader.readAsDataURL(new Blob([interleavedSamples.buffer]));\n await new Promise((resolve, reject) => {\n fileReader.onload = resolve;\n fileReader.onerror = reject;\n });\n return fileReader.result;\n }\n } else {\n if (this.BRIDGE) {\n this.BRIDGE.frameReady(frameNumber, new ArrayBuffer(0));\n } else {\n const fileReader = new FileReader();\n fileReader.readAsDataURL(new Blob([]));\n await new Promise((resolve, reject) => {\n fileReader.onload = resolve;\n fileReader.onerror = reject;\n });\n return fileReader.result;\n }\n }\n }\n}\n\nif (typeof window !== \"undefined\") {\n window.EF_FRAMEGEN = new EFFramegen();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EF_FRAMEGEN.js","names":[],"sources":["../src/EF_FRAMEGEN.ts"],"sourcesContent":["import type { VideoRenderOptions } from \"@editframe/assets\";\n\nimport { shallowGetTimegroups } from \"./elements/EFTimegroup.js\";\nimport { setupBrowserTracing } from \"./otel/setupBrowserTracing.js\";\nimport {\n clearCurrentFrameSpan,\n enableTracing,\n extractParentContext,\n setCurrentFrameSpan,\n type TraceContext,\n withSpan,\n withSpanAndContext,\n} from \"./otel/tracingHelpers.js\";\n\ninterface Bridge {\n onInitialize: (\n callback: (\n renderOptions: VideoRenderOptions,\n traceContext?: TraceContext,\n otelEndpoint?: string,\n ) => void,\n ) => void;\n\n initialized(): void;\n\n onBeginFrame(\n callback: (\n frameNumber: number,\n isLast: boolean,\n traceContext?: TraceContext,\n ) => void,\n ): void;\n\n onTriggerCanvas(callback: (traceContext?: TraceContext) => void): void;\n\n frameReady(frameNumber: number, audioSamples: ArrayBuffer): void;\n\n error(error: Error): void;\n\n syncLog(sequence: number, message: string, callback: () => void): void;\n\n exportSpans?: (endpoint: string, payload: string) => void;\n}\n\ndeclare global {\n interface Window {\n EF_FRAMEGEN?: EFFramegen;\n FRAMEGEN_BRIDGE?: Bridge;\n FRAMEGEN_BINDING?: any;\n FRAMEGEN_BINDING_error?: (error: Error) => void;\n EF_RENDERING?: () => boolean;\n }\n}\n\nclass TriggerCanvas {\n private canvas: HTMLCanvasElement;\n private ctx: CanvasRenderingContext2D;\n\n private canvasInitialized = false;\n\n constructor() {\n this.canvas = document.createElement(\"canvas\");\n const ctx = this.canvas.getContext(\"2d\", { willReadFrequently: true });\n if (!ctx) throw new Error(\"Canvas 2d context not ready\");\n this.ctx = ctx;\n this.ctx.fillStyle = \"transparent\";\n }\n\n initialize() {\n if (this.canvasInitialized) return;\n this.canvasInitialized = true;\n this.canvas.width = 1;\n this.canvas.height = 1;\n Object.assign(this.canvas.style, {\n position: \"fixed\",\n top: \"0px\",\n left: \"0px\",\n width: \"100%\",\n height: \"100%\",\n zIndex: \"100000\",\n });\n document.body.appendChild(this.canvas);\n }\n\n trigger() {\n this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);\n }\n}\n\nexport class EFFramegen {\n time = 0;\n frameDurationMs = 0;\n audioBufferPromise?: Promise<AudioBuffer>;\n renderOptions?: VideoRenderOptions;\n frameBox = document.createElement(\"div\");\n BRIDGE: typeof window.FRAMEGEN_BRIDGE;\n triggerCanvas = new TriggerCanvas();\n verificationCanvas?: HTMLCanvasElement;\n verificationCtx?: CanvasRenderingContext2D;\n private logSequence = 0;\n\n // Frame sequence coordination\n public frameTasksInProgress = false;\n public currentFrameNumber = 0;\n\n trace(...args: any[]) {\n console.trace(\"[EF_FRAMEGEN]\", ...args);\n }\n\n async syncLog(...args: any[]): Promise<void> {\n if (!this.BRIDGE) {\n // Fallback to regular console.log if no bridge\n console.log(\"[EF_FRAMEGEN]\", ...args);\n return;\n }\n\n const sequence = ++this.logSequence;\n const message = args\n .map((arg) =>\n typeof arg === \"object\" ? JSON.stringify(arg) : String(arg),\n )\n .join(\" \");\n\n return new Promise<void>((resolve) => {\n // biome-ignore lint/style/noNonNullAssertion: We know BRIDGE is set due to the guard above\n this.BRIDGE!.syncLog(sequence, message, () => {\n resolve();\n });\n });\n }\n\n private initializeVerificationCanvas() {\n if (this.verificationCanvas) {\n return;\n }\n\n this.verificationCanvas = document.createElement(\"canvas\");\n const ctx = this.verificationCanvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"Verification canvas 2d context not ready\");\n this.verificationCtx = ctx;\n\n // Size to match the workbench width, 1 pixel height for verification strip\n const workbench = document.querySelector(\"ef-workbench\") as HTMLElement;\n if (workbench) {\n this.verificationCanvas.width = workbench.clientWidth;\n this.verificationCanvas.height = 1;\n\n // Position at the bottom of the workbench (beyond content height)\n Object.assign(this.verificationCanvas.style, {\n position: \"fixed\",\n left: \"0px\",\n bottom: \"0px\",\n width: `${workbench.clientWidth}px`,\n height: \"1px\",\n zIndex: \"99999\", // Above trigger canvas\n });\n\n document.body.appendChild(this.verificationCanvas);\n }\n }\n\n private drawVerificationStrip(frameNumber: number) {\n this.initializeVerificationCanvas();\n\n if (!this.verificationCanvas || !this.verificationCtx) {\n return;\n }\n\n const width = this.verificationCanvas.width;\n const height = this.verificationCanvas.height;\n\n // Clear the strip\n this.verificationCtx.clearRect(0, 0, width, height);\n\n // Encode frame number into RGB (24-bit)\n // R=high byte, G=middle byte, B=low byte\n const red = Math.floor(frameNumber / (256 * 256)) % 256;\n const green = Math.floor(frameNumber / 256) % 256;\n const blue = frameNumber % 256;\n\n // Fill the entire strip with the encoded frame number\n this.verificationCtx.fillStyle = `rgb(${red}, ${green}, ${blue})`;\n this.verificationCtx.fillRect(0, 0, width, height);\n }\n\n constructor() {\n this.BRIDGE = window.FRAMEGEN_BRIDGE;\n if (this.BRIDGE) {\n this.connectToBridge();\n }\n }\n\n /**\n * Helper method to get the workbench and set its rendering state.\n * This ensures consistent state management across the framegen lifecycle.\n */\n private setWorkbenchRendering(isRendering: boolean) {\n const workbench = document.querySelector(\"ef-workbench\");\n if (workbench) {\n workbench.rendering = isRendering;\n }\n }\n\n connectToBridge() {\n const BRIDGE = this.BRIDGE;\n if (!BRIDGE) {\n throw new Error(\"No BRIDGE when attempting to connect to bridge\");\n }\n\n BRIDGE.onInitialize(async (renderOptions, traceContext, otelEndpoint) => {\n // Only enable tracing if explicitly requested in renderOptions\n if (renderOptions.enableTracing && otelEndpoint) {\n enableTracing();\n await setupBrowserTracing({\n otelEndpoint,\n serviceName: \"telecine-browser\",\n bridge: BRIDGE,\n useBatching: true, // Batch spans to reduce overhead during rendering\n });\n }\n\n const parentContext = extractParentContext(traceContext);\n\n await withSpan(\n \"browser.initialize\",\n {\n width: renderOptions.encoderOptions.video.width,\n height: renderOptions.encoderOptions.video.height,\n fps: renderOptions.encoderOptions.video.framerate,\n durationMs:\n renderOptions.encoderOptions.toMs -\n renderOptions.encoderOptions.fromMs,\n },\n parentContext,\n async () => {\n try {\n await this.initialize(renderOptions);\n } catch (error) {\n // If initialization fails, ensure rendering state is cleared\n this.setWorkbenchRendering(false);\n console.error(\n \"[EF_FRAMEGEN.connectToBridge] error initializing\",\n error,\n );\n throw error;\n }\n },\n );\n\n BRIDGE.initialized();\n });\n\n BRIDGE.onBeginFrame((frameNumber, isLast, traceContext) => {\n const parentContext = extractParentContext(traceContext);\n withSpanAndContext(\n \"browser.frame.render\",\n {\n frameNumber,\n isLast,\n },\n parentContext,\n async (span, _spanContext) => {\n // Store the span itself for child operations\n // This allows spans created in Lit Tasks to use it as their parent\n setCurrentFrameSpan(span);\n\n try {\n await this.beginFrame(frameNumber, isLast);\n } catch (error) {\n // If an error occurs during rendering, ensure rendering state is cleared\n this.setWorkbenchRendering(false);\n throw error;\n } finally {\n clearCurrentFrameSpan();\n }\n },\n ).catch((error) => {\n console.error(\"[EF_FRAMEGEN.beginFrame] error:\", error);\n // Ensure rendering state is cleared on error\n this.setWorkbenchRendering(false);\n clearCurrentFrameSpan();\n throw error;\n });\n });\n\n BRIDGE.onTriggerCanvas((traceContext) => {\n const parentContext = extractParentContext(traceContext);\n\n withSpan(\"browser.canvas.trigger\", {}, parentContext, async () => {\n this.triggerCanvas.trigger();\n }).catch((error) => {\n console.error(\"[EF_FRAMEGEN.triggerCanvas] error:\", error);\n });\n });\n }\n\n get showFrameBox() {\n return this.renderOptions?.showFrameBox ?? false;\n }\n\n async initialize(renderOptions: VideoRenderOptions) {\n this.renderOptions = renderOptions;\n\n const workbench = document.querySelector(\"ef-workbench\");\n if (!workbench) {\n throw new Error(\"No workbench found\");\n }\n this.setWorkbenchRendering(true);\n workbench.playing = false;\n const timegroups = shallowGetTimegroups(workbench);\n const firstGroup = timegroups[0];\n if (!firstGroup) {\n throw new Error(\"No temporal elements found\");\n }\n const startingTimeMs = renderOptions.encoderOptions.fromMs;\n await firstGroup.waitForMediaDurations();\n await firstGroup.waitForFrameTasks();\n\n this.frameDurationMs = 1000 / renderOptions.encoderOptions.video.framerate;\n\n this.time = startingTimeMs;\n if (this.showFrameBox) {\n Object.assign(this.frameBox.style, {\n width: \"200px\",\n height: \"100px\",\n font: \"10px Arial\",\n backgroundColor: \"white\",\n position: \"absolute\",\n top: \"0px\",\n right: \"0px\",\n zIndex: \"100000\",\n });\n document.body.prepend(this.frameBox);\n }\n\n this.triggerCanvas.initialize();\n\n // These times are aligned to the audio frame boundaries\n // And they include padding if any.\n this.audioBufferPromise = firstGroup.renderAudio(\n renderOptions.encoderOptions.alignedFromUs / 1000,\n renderOptions.encoderOptions.alignedToUs / 1000,\n );\n }\n\n async beginFrame(frameNumber: number, isLast: boolean) {\n if (this.renderOptions === undefined) {\n throw new Error(\"No renderOptions\");\n }\n const workbench = document.querySelector(\"ef-workbench\");\n if (!workbench) {\n throw new Error(\"No workbench found\");\n }\n this.setWorkbenchRendering(true);\n const timegroups = shallowGetTimegroups(workbench);\n const firstGroup = timegroups[0];\n if (!firstGroup) {\n throw new Error(\"No temporal elements found\");\n }\n\n // Calculate base frame time using normal progression\n const frameTime =\n this.renderOptions.encoderOptions.fromMs +\n frameNumber * this.frameDurationMs;\n firstGroup.currentTimeMs = Number(Number(frameTime).toFixed(5));\n await firstGroup.waitForFrameTasks();\n if (this.showFrameBox) {\n this.frameBox.innerHTML = `\n <div>🖼️ Frame: ${frameNumber}</div>\n <div>🕛 Segment: ${this.time.toFixed(4)}</div>\n <div>🕛 Frame: ${firstGroup.currentTimeMs.toFixed(4)}</div>\n <div> from-to: ${this.renderOptions.encoderOptions.fromMs.toFixed(4)} - ${this.renderOptions.encoderOptions.toMs.toFixed(4)}</div>\n `;\n }\n\n // Draw verification pixel strip for frame verification\n this.drawVerificationStrip(frameNumber);\n\n if (isLast && this.audioBufferPromise) {\n // Currently we emit the audio in one belch at the end of the render.\n // This is not ideal, but it's the simplest thing that could possibly work.\n // We could either emit it slices, or in parallel with the video.\n // But in any case, it's fine for now.\n const renderedAudio = await this.audioBufferPromise;\n\n const channelCount = renderedAudio.numberOfChannels;\n\n const interleavedSamples = new Float32Array(\n channelCount * renderedAudio.length,\n );\n\n for (let i = 0; i < renderedAudio.length; i++) {\n for (let j = 0; j < channelCount; j++) {\n interleavedSamples.set(\n renderedAudio.getChannelData(j).slice(i, i + 1),\n i * channelCount + j,\n );\n }\n }\n\n if (this.BRIDGE) {\n this.BRIDGE.frameReady(frameNumber, interleavedSamples.buffer);\n } else {\n const fileReader = new FileReader();\n fileReader.readAsDataURL(new Blob([interleavedSamples.buffer]));\n await new Promise((resolve, reject) => {\n fileReader.onload = resolve;\n fileReader.onerror = reject;\n });\n return fileReader.result;\n }\n \n // Rendering is complete after the last frame\n this.setWorkbenchRendering(false);\n } else {\n if (this.BRIDGE) {\n this.BRIDGE.frameReady(frameNumber, new ArrayBuffer(0));\n } else {\n const fileReader = new FileReader();\n fileReader.readAsDataURL(new Blob([]));\n await new Promise((resolve, reject) => {\n fileReader.onload = resolve;\n fileReader.onerror = reject;\n });\n return fileReader.result;\n }\n }\n }\n}\n\nif (typeof window !== \"undefined\") {\n window.EF_FRAMEGEN = new EFFramegen();\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EF_INTERACTIVE.js","names":[],"sources":["../src/EF_INTERACTIVE.ts"],"sourcesContent":["export let EF_INTERACTIVE = false;\n\nif (typeof window !== \"undefined\") {\n EF_INTERACTIVE = !window.location?.search.includes(\"EF_NONINTERACTIVE\");\n}\n"],"mappings":";AAAA,IAAW,iBAAiB;AAE5B,IAAI,OAAO,WAAW,YACpB,kBAAiB,CAAC,OAAO,UAAU,OAAO,SAAS,oBAAoB"}
|
|
1
|
+
{"version":3,"file":"EF_INTERACTIVE.js","names":[],"sources":["../src/EF_INTERACTIVE.ts"],"sourcesContent":["export let EF_INTERACTIVE = false;\n\nif (typeof window !== \"undefined\") {\n EF_INTERACTIVE = !window.location?.search.includes(\"EF_NONINTERACTIVE\");\n}\n\n/**\n * Set EF_INTERACTIVE value for testing purposes.\n * @internal\n */\nexport const setEFInteractive = (value: boolean) => {\n EF_INTERACTIVE = value;\n};\n"],"mappings":";AAAA,IAAW,iBAAiB;AAE5B,IAAI,OAAO,WAAW,YACpB,kBAAiB,CAAC,OAAO,UAAU,OAAO,SAAS,oBAAoB"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __commonJS = (cb, mod) => function() {
|
|
9
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
13
|
+
key = keys[i];
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
15
|
+
get: ((k) => from[k]).bind(null, key),
|
|
16
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
22
|
+
value: mod,
|
|
23
|
+
enumerable: true
|
|
24
|
+
}) : target, mod));
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
export { __commonJS, __toESM };
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { TemporalMixinInterface } from "../elements/EFTemporal.js";
|
|
2
|
+
import { CanvasElementData } from "./api/types.js";
|
|
3
|
+
import { SelectionContext } from "./selection/selectionContext.js";
|
|
4
|
+
import { PanZoomTransform } from "../elements/EFPanZoom.js";
|
|
5
|
+
import * as lit33 from "lit";
|
|
6
|
+
import { LitElement } from "lit";
|
|
7
|
+
import * as lit_html32 from "lit-html";
|
|
8
|
+
|
|
9
|
+
//#region src/canvas/EFCanvas.d.ts
|
|
10
|
+
declare const EFCanvas_base: typeof LitElement;
|
|
11
|
+
/**
|
|
12
|
+
* =============================================================================
|
|
13
|
+
* COORDINATE SYSTEM AND DIMENSION CALCULATION PRINCIPLES
|
|
14
|
+
* =============================================================================
|
|
15
|
+
*
|
|
16
|
+
* This canvas system uses a unified approach for calculating element positions
|
|
17
|
+
* and dimensions that works correctly regardless of CSS transforms (rotation,
|
|
18
|
+
* scale, etc.), zoom level, or nesting depth.
|
|
19
|
+
*
|
|
20
|
+
* TWO KEY DOM APIS WITH DIFFERENT BEHAVIORS:
|
|
21
|
+
*
|
|
22
|
+
* 1. offsetWidth / offsetHeight
|
|
23
|
+
* - Returns the element's LAYOUT dimensions (CSS box model)
|
|
24
|
+
* - These are the dimensions BEFORE any CSS transforms are applied
|
|
25
|
+
* - UNAFFECTED by: rotation, scale, skew, or any other transform
|
|
26
|
+
* - UNAFFECTED by: parent transforms (including zoom scale)
|
|
27
|
+
* - AFFECTED by: CSS width/height properties, padding, border
|
|
28
|
+
* - Units: CSS pixels in the element's own coordinate space
|
|
29
|
+
*
|
|
30
|
+
* Example: A 200x100px element rotated 45° still has offsetWidth=200, offsetHeight=100
|
|
31
|
+
* Example: A 200x100px element in a 2x zoomed canvas still has offsetWidth=200, offsetHeight=100
|
|
32
|
+
*
|
|
33
|
+
* USE FOR: Getting the actual dimensions of an element in canvas coordinates
|
|
34
|
+
*
|
|
35
|
+
* 2. getBoundingClientRect()
|
|
36
|
+
* - Returns the element's visual BOUNDING BOX on screen
|
|
37
|
+
* - This is the axis-aligned rectangle that fully contains the transformed element
|
|
38
|
+
* - AFFECTED by: rotation (bounding box grows), scale, all transforms
|
|
39
|
+
* - AFFECTED by: parent transforms (including zoom scale)
|
|
40
|
+
* - Units: Screen pixels (viewport coordinates)
|
|
41
|
+
*
|
|
42
|
+
* Example: A 200x100px element rotated 45° has bounding box ~212x212px
|
|
43
|
+
* Example: A 200x100px element in a 2x zoomed canvas has bounding rect 400x200px
|
|
44
|
+
*
|
|
45
|
+
* USE FOR: Getting the visual center position (center of bounding box = element center)
|
|
46
|
+
* NOT FOR: Getting actual dimensions (bounding box ≠ actual size when rotated)
|
|
47
|
+
*
|
|
48
|
+
* THE UNIFIED CALCULATION METHOD:
|
|
49
|
+
*
|
|
50
|
+
* For ANY element (rotated, scaled, nested, etc.):
|
|
51
|
+
*
|
|
52
|
+
* 1. DIMENSIONS: Use offsetWidth/offsetHeight
|
|
53
|
+
* - These give us the true dimensions in canvas coordinates
|
|
54
|
+
* - No division by scale needed - they're already in canvas space
|
|
55
|
+
*
|
|
56
|
+
* 2. CENTER POSITION: Use getBoundingClientRect() center
|
|
57
|
+
* - screenCenterX = rect.left + rect.width / 2
|
|
58
|
+
* - screenCenterY = rect.top + rect.height / 2
|
|
59
|
+
* - The center of the bounding box IS the element's center
|
|
60
|
+
* - This is true for ANY rotation (rotation around center keeps center fixed)
|
|
61
|
+
*
|
|
62
|
+
* 3. TOP-LEFT POSITION: Calculate from center and dimensions
|
|
63
|
+
* - canvasX = canvasCenter.x - width / 2
|
|
64
|
+
* - canvasY = canvasCenter.y - height / 2
|
|
65
|
+
*
|
|
66
|
+
* WHY THIS WORKS UNIVERSALLY:
|
|
67
|
+
*
|
|
68
|
+
* - offsetWidth/Height are defined by CSS spec to be unaffected by transforms
|
|
69
|
+
* - The center of any shape is preserved under rotation around that center
|
|
70
|
+
* - This mathematical relationship holds for ALL transform combinations
|
|
71
|
+
* - No special cases needed for rotation, scale, or nesting
|
|
72
|
+
*
|
|
73
|
+
* COMMON MISTAKES TO AVOID:
|
|
74
|
+
*
|
|
75
|
+
* ❌ Using getBoundingClientRect().width for dimensions (wrong when rotated)
|
|
76
|
+
* ❌ Dividing offsetWidth by scale (offsetWidth is already in canvas coords)
|
|
77
|
+
* ❌ Using getBoundingClientRect().left/top for position (wrong when rotated)
|
|
78
|
+
* ❌ Having different code paths for rotated vs non-rotated elements
|
|
79
|
+
*
|
|
80
|
+
* =============================================================================
|
|
81
|
+
*/
|
|
82
|
+
/**
|
|
83
|
+
* Main canvas container element.
|
|
84
|
+
* Manages existing elements (EF* elements, divs, etc.) and provides selection functionality.
|
|
85
|
+
*/
|
|
86
|
+
declare class EFCanvas extends EFCanvas_base {
|
|
87
|
+
static styles: lit33.CSSResult[];
|
|
88
|
+
panZoomTransform?: PanZoomTransform;
|
|
89
|
+
elementIdAttribute: string;
|
|
90
|
+
enableTransformHandles: boolean;
|
|
91
|
+
private elementRegistry;
|
|
92
|
+
private elementMetadata;
|
|
93
|
+
private selectionController;
|
|
94
|
+
private overlayLayer;
|
|
95
|
+
private selectionOverlay;
|
|
96
|
+
private transformHandlesMap;
|
|
97
|
+
private overlayRafId;
|
|
98
|
+
private isDragging;
|
|
99
|
+
private dragStarted;
|
|
100
|
+
private dragStartPos;
|
|
101
|
+
private dragStartCanvasPos;
|
|
102
|
+
private dragStartElementPositions;
|
|
103
|
+
private draggedElementId;
|
|
104
|
+
private capturedPointerId;
|
|
105
|
+
private readonly DRAG_THRESHOLD;
|
|
106
|
+
private isBoxSelecting;
|
|
107
|
+
private boxSelectStart;
|
|
108
|
+
private boxSelectModifierKeys;
|
|
109
|
+
private emptySpaceClickPos;
|
|
110
|
+
private selectionChangeHandler?;
|
|
111
|
+
private elementHoverHandlers;
|
|
112
|
+
private _activeRootTemporal;
|
|
113
|
+
selectionContext: SelectionContext;
|
|
114
|
+
/**
|
|
115
|
+
* The currently highlighted (hovered) element.
|
|
116
|
+
* This is the source of truth for highlight state across all panels
|
|
117
|
+
* (canvas, hierarchy, timeline).
|
|
118
|
+
*/
|
|
119
|
+
highlightedElement: HTMLElement | null;
|
|
120
|
+
constructor();
|
|
121
|
+
connectedCallback(): void;
|
|
122
|
+
disconnectedCallback(): void;
|
|
123
|
+
/**
|
|
124
|
+
* Setup mutation observer to track element additions/removals.
|
|
125
|
+
*/
|
|
126
|
+
private setupElementTracking;
|
|
127
|
+
/**
|
|
128
|
+
* Try to register an element, auto-generating an ID if needed.
|
|
129
|
+
* Public method for external use (e.g., hierarchy selection).
|
|
130
|
+
*/
|
|
131
|
+
tryRegisterElement(element: HTMLElement): void;
|
|
132
|
+
/**
|
|
133
|
+
* Register an element for canvas management.
|
|
134
|
+
* @throws Error if element does not have an ID
|
|
135
|
+
*/
|
|
136
|
+
registerElement(element: HTMLElement, id?: string): string;
|
|
137
|
+
/**
|
|
138
|
+
* Unregister an element.
|
|
139
|
+
*/
|
|
140
|
+
unregisterElement(element: HTMLElement | string): void;
|
|
141
|
+
/**
|
|
142
|
+
* Setup hover event listeners for an element to update highlightedElement.
|
|
143
|
+
* The canvas is the source of truth for highlight state.
|
|
144
|
+
*/
|
|
145
|
+
private setupElementHoverListeners;
|
|
146
|
+
/**
|
|
147
|
+
* Set the highlighted element. Called by canvas hover handlers
|
|
148
|
+
* and by external panels (hierarchy, timeline) when user hovers items.
|
|
149
|
+
*/
|
|
150
|
+
setHighlightedElement(element: HTMLElement | null): void;
|
|
151
|
+
/**
|
|
152
|
+
* Remove hover event listeners for an element.
|
|
153
|
+
*/
|
|
154
|
+
private removeElementHoverListeners;
|
|
155
|
+
/**
|
|
156
|
+
* Update element metadata from DOM.
|
|
157
|
+
*
|
|
158
|
+
* UNIFIED APPROACH - works for ALL elements regardless of rotation, scale, or nesting:
|
|
159
|
+
*
|
|
160
|
+
* 1. DIMENSIONS: Always use offsetWidth/offsetHeight
|
|
161
|
+
* - These are layout dimensions in the element's coordinate space
|
|
162
|
+
* - Unaffected by CSS transforms (rotation, scale, etc.)
|
|
163
|
+
* - Already in canvas coordinates (no scale division needed)
|
|
164
|
+
*
|
|
165
|
+
* 2. CENTER POSITION: Always use getBoundingClientRect() center
|
|
166
|
+
* - The center of the bounding box IS the element's center (transform-origin: center)
|
|
167
|
+
* - Works correctly for rotated elements (center is rotation-invariant)
|
|
168
|
+
* - Convert to canvas coordinates using screenToCanvas()
|
|
169
|
+
*
|
|
170
|
+
* 3. TOP-LEFT POSITION: Calculate from center and dimensions
|
|
171
|
+
* - x = centerX - width/2
|
|
172
|
+
* - y = centerY - height/2
|
|
173
|
+
*/
|
|
174
|
+
private updateElementMetadata;
|
|
175
|
+
/**
|
|
176
|
+
* Hit test - find elements intersecting with bounds.
|
|
177
|
+
* @param bounds - DOMRect in canvas coordinate space (from SelectionModel.boxSelectBounds)
|
|
178
|
+
*/
|
|
179
|
+
private hitTest;
|
|
180
|
+
/**
|
|
181
|
+
* Handle pointer down events.
|
|
182
|
+
* Only handles events when clicking on elements. Otherwise, lets panzoom handle it.
|
|
183
|
+
*/
|
|
184
|
+
private handlePointerDown;
|
|
185
|
+
/**
|
|
186
|
+
* Handle pointer move events.
|
|
187
|
+
*/
|
|
188
|
+
private handlePointerMove;
|
|
189
|
+
/**
|
|
190
|
+
* Handle pointer up events.
|
|
191
|
+
*/
|
|
192
|
+
private handlePointerUp;
|
|
193
|
+
/**
|
|
194
|
+
* Setup listener for selection changes to update data-selected attributes.
|
|
195
|
+
*/
|
|
196
|
+
private setupSelectionListener;
|
|
197
|
+
/**
|
|
198
|
+
* Remove selection change listener.
|
|
199
|
+
*/
|
|
200
|
+
private removeSelectionListener;
|
|
201
|
+
/**
|
|
202
|
+
* Get the root temporal element containing the current selection.
|
|
203
|
+
* Returns null if no element is selected or the selected element has no root temporal.
|
|
204
|
+
*/
|
|
205
|
+
get activeRootTemporal(): (TemporalMixinInterface & HTMLElement) | null;
|
|
206
|
+
/**
|
|
207
|
+
* Update data-selected attributes on elements based on current selection.
|
|
208
|
+
* This enables pure CSS styling of selected elements.
|
|
209
|
+
*/
|
|
210
|
+
private updateSelectionAttributes;
|
|
211
|
+
/**
|
|
212
|
+
* Update element position in canvas coordinates.
|
|
213
|
+
* Unified approach: Always calculate relative to parent (or .canvas-content for direct children).
|
|
214
|
+
* For direct children, parent position is (0, 0), so relative = absolute (no-op).
|
|
215
|
+
*
|
|
216
|
+
* For nested elements, we read parent's current position from DOM (not metadata) to ensure
|
|
217
|
+
* we're always calculating relative to the actual current position.
|
|
218
|
+
*/
|
|
219
|
+
updateElementPosition(elementId: string, x: number, y: number): void;
|
|
220
|
+
/**
|
|
221
|
+
* Get element metadata.
|
|
222
|
+
*/
|
|
223
|
+
getElementData(elementId: string): CanvasElementData | null;
|
|
224
|
+
/**
|
|
225
|
+
* Get all element data.
|
|
226
|
+
*/
|
|
227
|
+
getAllElementsData(): CanvasElementData[];
|
|
228
|
+
/**
|
|
229
|
+
* Convert screen coordinates to canvas coordinates (for API).
|
|
230
|
+
*/
|
|
231
|
+
screenToCanvasCoords(screenX: number, screenY: number): {
|
|
232
|
+
x: number;
|
|
233
|
+
y: number;
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* Convert canvas coordinates to screen coordinates (for API).
|
|
237
|
+
*/
|
|
238
|
+
canvasToScreenCoords(canvasX: number, canvasY: number): {
|
|
239
|
+
x: number;
|
|
240
|
+
y: number;
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Setup overlay layer as sibling of panzoom (outside panzoom, same level as panzoom).
|
|
244
|
+
*/
|
|
245
|
+
private setupOverlayLayer;
|
|
246
|
+
/**
|
|
247
|
+
* Cleanup overlay layer if we created it.
|
|
248
|
+
*/
|
|
249
|
+
private cleanupOverlayLayer;
|
|
250
|
+
/**
|
|
251
|
+
* Setup selection overlay as sibling of panzoom (outside panzoom, same level as panzoom).
|
|
252
|
+
* This ensures the overlay maintains 1:1 pixel ratio regardless of zoom level.
|
|
253
|
+
*/
|
|
254
|
+
private setupSelectionOverlay;
|
|
255
|
+
/**
|
|
256
|
+
* Cleanup selection overlay if we created it.
|
|
257
|
+
*/
|
|
258
|
+
private cleanupSelectionOverlay;
|
|
259
|
+
/**
|
|
260
|
+
* Start RAF loop for overlay layer sync and transform handles updates.
|
|
261
|
+
*/
|
|
262
|
+
private startOverlayRafLoop;
|
|
263
|
+
/**
|
|
264
|
+
* Stop RAF loop.
|
|
265
|
+
*/
|
|
266
|
+
private stopOverlayRafLoop;
|
|
267
|
+
/**
|
|
268
|
+
* Update transform handles for selected elements.
|
|
269
|
+
* For multiple selections, shows a single set of handles for the bounding box.
|
|
270
|
+
*/
|
|
271
|
+
private updateTransformHandles;
|
|
272
|
+
/**
|
|
273
|
+
* Update transform handles bounds for an element.
|
|
274
|
+
*/
|
|
275
|
+
private updateTransformHandlesBounds;
|
|
276
|
+
/**
|
|
277
|
+
* Handle transform handles bounds-change event for multi-selection.
|
|
278
|
+
* Updates all selected elements proportionally.
|
|
279
|
+
*/
|
|
280
|
+
private handleMultiSelectionTransformHandlesBoundsChange;
|
|
281
|
+
private lastMultiSelectionRotation;
|
|
282
|
+
private multiSelectionRotationCenter;
|
|
283
|
+
private multiSelectionResizeInitial;
|
|
284
|
+
/**
|
|
285
|
+
* Handle transform handles rotation-change event for multiple selected elements.
|
|
286
|
+
* Rotates all elements around the center of the bounding box.
|
|
287
|
+
*/
|
|
288
|
+
private handleMultiSelectionTransformHandlesRotationChange;
|
|
289
|
+
/**
|
|
290
|
+
* Handle transform handles bounds-change event for single element.
|
|
291
|
+
* Converts bounds from overlay-relative screen coordinates to canvas coordinates.
|
|
292
|
+
*/
|
|
293
|
+
private handleTransformHandlesBoundsChange;
|
|
294
|
+
/**
|
|
295
|
+
* Handle transform handles rotation-change event for single element.
|
|
296
|
+
*/
|
|
297
|
+
private handleTransformHandlesRotationChange;
|
|
298
|
+
/**
|
|
299
|
+
* Cleanup transform handles.
|
|
300
|
+
*/
|
|
301
|
+
private cleanupTransformHandles;
|
|
302
|
+
render(): lit_html32.TemplateResult<1>;
|
|
303
|
+
}
|
|
304
|
+
declare global {
|
|
305
|
+
interface HTMLElementTagNameMap {
|
|
306
|
+
"ef-canvas": EFCanvas;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
//#endregion
|
|
310
|
+
export { EFCanvas };
|
|
311
|
+
//# sourceMappingURL=EFCanvas.d.ts.map
|