@editframe/elements 0.45.2 → 0.45.4
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/DelayedLoadingState.js.map +1 -1
- package/dist/EF_FRAMEGEN.js.map +1 -1
- package/dist/EF_RENDERING.js.map +1 -1
- package/dist/canvas/EFCanvas.js +3 -3
- package/dist/canvas/EFCanvas.js.map +1 -1
- package/dist/canvas/EFCanvasItem.js.map +1 -1
- package/dist/canvas/api/CanvasAPI.js.map +1 -1
- package/dist/canvas/getElementBounds.js.map +1 -1
- package/dist/canvas/overlays/SelectionOverlay.js.map +1 -1
- package/dist/canvas/overlays/overlayState.js.map +1 -1
- package/dist/canvas/selection/SelectionController.js +25 -23
- package/dist/canvas/selection/SelectionController.js.map +1 -1
- package/dist/canvas/selection/SelectionModel.js.map +1 -1
- package/dist/canvas/selection/selectionContext.js.map +1 -1
- package/dist/elements/ContainerInfo.js.map +1 -1
- package/dist/elements/CrossUpdateController.js.map +1 -1
- package/dist/elements/EFAudio.js.map +1 -1
- package/dist/elements/EFCaptions.js.map +1 -1
- package/dist/elements/EFImage.js +1 -1
- package/dist/elements/EFImage.js.map +1 -1
- package/dist/elements/EFMedia/BufferedSeekingInput.js.map +1 -1
- package/dist/elements/EFMedia/CachedFetcher.js.map +1 -1
- package/dist/elements/EFMedia/MediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/SegmentIndex.js.map +1 -1
- package/dist/elements/EFMedia/SegmentTransport.js.map +1 -1
- package/dist/elements/EFMedia/TimingModel.js.map +1 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/GlobalInputCache.js.map +1 -1
- package/dist/elements/EFMedia/shared/PrecisionUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
- package/dist/elements/EFMedia.js.map +1 -1
- package/dist/elements/EFPanZoom.js +9 -8
- package/dist/elements/EFPanZoom.js.map +1 -1
- package/dist/elements/EFSourceMixin.js +1 -1
- package/dist/elements/EFSourceMixin.js.map +1 -1
- package/dist/elements/EFSurface.js.map +1 -1
- package/dist/elements/EFTemporal.js.map +1 -1
- package/dist/elements/EFText.d.ts +4 -4
- package/dist/elements/EFText.js.map +1 -1
- package/dist/elements/EFTextSegment.d.ts +4 -4
- package/dist/elements/EFTimegroup.js +7 -8
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/dist/elements/EFVideo.d.ts +4 -4
- package/dist/elements/EFVideo.js.map +1 -1
- package/dist/elements/EFWaveform.d.ts +4 -4
- 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/SampleBuffer.js.map +1 -1
- package/dist/elements/TargetController.js.map +1 -1
- package/dist/elements/TimegroupController.js.map +1 -1
- package/dist/elements/cloneFactoryRegistry.js.map +1 -1
- package/dist/elements/durationConverter.js.map +1 -1
- package/dist/elements/easingUtils.js.map +1 -1
- package/dist/elements/renderTemporalAudio.js.map +1 -1
- package/dist/elements/setupTemporalHierarchy.js.map +1 -1
- package/dist/elements/updateAnimations.js +1 -1
- package/dist/elements/updateAnimations.js.map +1 -1
- package/dist/getRenderInfo.js.map +1 -1
- package/dist/gui/ContextMixin.js.map +1 -1
- package/dist/gui/Controllable.js.map +1 -1
- package/dist/gui/EFActiveRootTemporal.d.ts +4 -4
- package/dist/gui/EFActiveRootTemporal.js.map +1 -1
- package/dist/gui/EFConfiguration.d.ts +4 -4
- package/dist/gui/EFControls.js.map +1 -1
- package/dist/gui/EFDial.d.ts +4 -4
- package/dist/gui/EFFilmstrip.d.ts +4 -4
- package/dist/gui/EFFilmstrip.js.map +1 -1
- package/dist/gui/EFFitScale.js.map +1 -1
- package/dist/gui/EFOverlayItem.d.ts +4 -4
- package/dist/gui/EFOverlayItem.js.map +1 -1
- package/dist/gui/EFOverlayLayer.d.ts +4 -4
- package/dist/gui/EFOverlayLayer.js.map +1 -1
- 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/EFPreview.js.map +1 -1
- package/dist/gui/EFResizableBox.js.map +1 -1
- package/dist/gui/EFScrubber.d.ts +4 -4
- package/dist/gui/EFScrubber.js.map +1 -1
- package/dist/gui/EFTimeDisplay.d.ts +4 -4
- package/dist/gui/EFTimeDisplay.js.map +1 -1
- package/dist/gui/EFTimelineRuler.d.ts +4 -4
- package/dist/gui/EFTimelineRuler.js.map +1 -1
- package/dist/gui/EFToggleLoop.d.ts +4 -4
- package/dist/gui/EFTogglePlay.d.ts +4 -4
- package/dist/gui/EFTogglePlay.js.map +1 -1
- package/dist/gui/EFTransformHandles.js.map +1 -1
- package/dist/gui/EFWorkbench.d.ts +4 -4
- package/dist/gui/EFWorkbench.js.map +1 -1
- package/dist/gui/FitScaleHelpers.js.map +1 -1
- package/dist/gui/PlaybackController.js.map +1 -1
- package/dist/gui/TWMixin2.js.map +1 -1
- package/dist/gui/TargetOrContextMixin.js.map +1 -1
- package/dist/gui/currentTimeContext.js.map +1 -1
- package/dist/gui/efContext.js.map +1 -1
- package/dist/gui/fetchContext.js.map +1 -1
- package/dist/gui/hierarchy/EFHierarchy.d.ts +4 -4
- package/dist/gui/hierarchy/EFHierarchy.js.map +1 -1
- package/dist/gui/hierarchy/EFHierarchyItem.d.ts +2 -2
- package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -1
- package/dist/gui/hierarchy/hierarchyContext.js.map +1 -1
- package/dist/gui/panZoomTransformContext.js.map +1 -1
- package/dist/gui/previewSettingsContext.js.map +1 -1
- package/dist/gui/theme.js.map +1 -1
- package/dist/gui/timeline/EFTimeline.d.ts +2 -2
- package/dist/gui/timeline/EFTimeline.js +0 -1
- package/dist/gui/timeline/EFTimeline.js.map +1 -1
- package/dist/gui/timeline/EFTimelineRow.js.map +1 -1
- package/dist/gui/timeline/TrimHandles.d.ts +4 -4
- package/dist/gui/timeline/TrimHandles.js.map +1 -1
- package/dist/gui/timeline/flattenHierarchy.js.map +1 -1
- package/dist/gui/timeline/timelineStateContext.js.map +1 -1
- package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/EFThumbnailStrip.js.map +1 -1
- package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/TextTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/TrackItem.js.map +1 -1
- package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -1
- package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -1
- package/dist/gui/transformCalculations.js.map +1 -1
- package/dist/gui/transformUtils.js.map +1 -1
- package/dist/gui/tree/EFTree.d.ts +4 -4
- package/dist/gui/tree/EFTree.js.map +1 -1
- package/dist/gui/tree/EFTreeItem.d.ts +4 -4
- package/dist/gui/tree/EFTreeItem.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/otel/BridgeSpanExporter.js.map +1 -1
- package/dist/otel/setupBrowserTracing.js.map +1 -1
- package/dist/otel/tracingHelpers.js.map +1 -1
- package/dist/preview/AdaptiveResolutionTracker.js.map +1 -1
- package/dist/preview/FrameController.js.map +1 -1
- package/dist/preview/QualityUpgradeScheduler.js.map +1 -1
- package/dist/preview/RenderContext.js.map +1 -1
- package/dist/preview/RenderProfiler.js.map +1 -1
- package/dist/preview/RenderStats.js.map +1 -1
- package/dist/preview/encoding/canvasEncoder.js.map +1 -1
- package/dist/preview/encoding/mainThreadEncoder.js +1 -1
- package/dist/preview/encoding/mainThreadEncoder.js.map +1 -1
- package/dist/preview/previewSettings.js.map +1 -1
- package/dist/preview/previewTypes.js.map +1 -1
- package/dist/preview/renderElementToCanvas.js.map +1 -1
- package/dist/preview/renderTimegroupToCanvas.js +2 -44
- package/dist/preview/renderTimegroupToCanvas.js.map +1 -1
- package/dist/preview/renderTimegroupToVideo.js +2 -2
- package/dist/preview/renderTimegroupToVideo.js.map +1 -1
- package/dist/preview/renderVideoToVideo.js +2 -2
- package/dist/preview/renderVideoToVideo.js.map +1 -1
- package/dist/preview/renderers.js.map +1 -1
- package/dist/preview/rendering/ScaleConfig.js.map +1 -1
- package/dist/preview/rendering/loadImage.js.map +1 -1
- package/dist/preview/rendering/renderToImageNative.js.map +1 -1
- package/dist/preview/rendering/serializeTimelineDirect.js +1 -1
- package/dist/preview/rendering/serializeTimelineDirect.js.map +1 -1
- package/dist/preview/statsTrackingStrategy.js.map +1 -1
- package/dist/preview/workers/WorkerPool.js.map +1 -1
- package/dist/render/EFRenderAPI.js.map +1 -1
- package/dist/transcoding/cache/RequestDeduplicator.js.map +1 -1
- package/dist/utils/LRUCache.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EFTransformHandles.js","names":["EFTransformHandles","overlayStyles: Record<string, string>"],"sources":["../../src/gui/EFTransformHandles.ts"],"sourcesContent":["import { consume } from \"@lit/context\";\nimport { css, html, LitElement } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { styleMap } from \"lit/directives/style-map.js\";\nimport { panZoomTransformContext } from \"./panZoomTransformContext.js\";\nimport type { PanZoomTransform } from \"../elements/EFPanZoom.js\";\nimport {\n type ResizeHandle,\n calculateDragBounds,\n calculateResizeBounds,\n getResizeHandleCursor,\n} from \"./transformCalculations.js\";\nimport { getCornerPoint, getOppositeCorner } from \"./transformUtils.js\";\n\nconst DEFAULT_MIN_SIZE = 10;\n\nexport interface TransformBounds {\n x: number;\n y: number;\n width: number;\n height: number;\n rotation?: number;\n}\n\n/**\n * Interaction mode enumeration.\n * Only one mode can be active at a time (invariant).\n */\ntype InteractionMode = \"idle\" | \"dragging\" | \"resizing\" | \"rotating\";\n\n@customElement(\"ef-transform-handles\")\nexport class EFTransformHandles extends LitElement {\n @property({ type: Object })\n bounds: TransformBounds = { x: 0, y: 0, width: 100, height: 100 };\n\n @property({ type: Number })\n minSize = DEFAULT_MIN_SIZE;\n\n @property({ type: String })\n target?: string;\n\n @consume({ context: panZoomTransformContext, subscribe: true })\n panZoomTransformFromContext?: PanZoomTransform;\n\n @property({ type: Number, attribute: \"canvas-scale\" })\n canvasScale = 1;\n\n @property({ type: Boolean, attribute: \"enable-rotation\" })\n enableRotation = false;\n\n @property({ type: Boolean, attribute: \"enable-resize\" })\n enableResize = true;\n\n @property({ type: Boolean, attribute: \"corners-only\" })\n cornersOnly = false;\n\n @property({ type: Boolean, attribute: \"lock-aspect-ratio\" })\n lockAspectRatio = false;\n\n @property({ type: Boolean, attribute: \"enable-drag\" })\n enableDrag = true;\n\n @property({ type: Number, attribute: \"rotation-step\" })\n rotationStep?: number;\n\n /**\n * Current interaction mode.\n * Invariant: Only one mode active at a time.\n */\n @state()\n interactionMode: InteractionMode = \"idle\";\n\n /**\n * Active resize handle when in \"resizing\" mode.\n * Only valid when interactionMode === \"resizing\".\n */\n private activeResizeHandle: ResizeHandle | null = null;\n\n /**\n * Mouse start position for calculating deltas.\n * Only valid during active interaction.\n */\n private mouseStart: { x: number; y: number } | null = null;\n\n /**\n * Initial bounds at interaction start - NEVER mutated during interaction.\n * All calculations derive from this + mouse deltas.\n * Note: Not a @state() property to avoid re-renders during interaction.\n */\n private initialBounds: TransformBounds | null = null;\n\n static styles = css`\n :host {\n display: block;\n position: absolute;\n pointer-events: none;\n }\n .overlay {\n position: absolute;\n border: 2px solid var(--ef-transform-handles-border-color, var(--ef-color-primary));\n pointer-events: none;\n }\n .overlay.dragging {\n border-color: var(--ef-transform-handles-dragging-border-color, var(--ef-color-primary));\n }\n .drag-area {\n position: absolute;\n inset: 0;\n cursor: move;\n pointer-events: none;\n }\n /* Only enable pointer events when actively dragging */\n .drag-area:active {\n pointer-events: auto;\n }\n .handle {\n position: absolute;\n width: 8px;\n height: 8px;\n background: var(--ef-transform-handles-handle-color, var(--ef-color-bg-elevated));\n border: 1px solid var(--ef-transform-handles-handle-border-color, var(--ef-color-primary));\n pointer-events: auto;\n /* Only capture pointer events, allow wheel events to pass through */\n touch-action: none;\n }\n .handle.nw { top: -4px; left: -4px; }\n .handle.n { top: -4px; left: 50%; transform: translateX(-50%); }\n .handle.ne { top: -4px; right: -4px; }\n .handle.e { top: 50%; right: -4px; transform: translateY(-50%); }\n .handle.se { bottom: -4px; right: -4px; }\n .handle.s { bottom: -4px; left: 50%; transform: translateX(-50%); }\n .handle.sw { bottom: -4px; left: -4px; }\n .handle.w { top: 50%; left: -4px; transform: translateY(-50%); }\n .rotate-handle {\n position: absolute;\n top: -30px;\n left: 50%;\n transform: translateX(-50%);\n cursor: grab;\n pointer-events: auto;\n /* Only capture pointer events, allow wheel events to pass through */\n touch-action: none;\n }\n .rotate-handle-circle {\n width: 24px;\n height: 24px;\n background: var(--ef-transform-handles-rotate-handle-color, var(--ef-color-success));\n border: 2px solid var(--ef-color-bg-elevated);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .rotate-handle-circle span {\n font-size: 12px;\n color: white;\n }\n `;\n\n private resizeObserver?: ResizeObserver;\n\n /**\n * Single source of truth for zoom scale.\n * Priority: context > prop > 1.0\n */\n private getZoomScale(): number {\n return this.panZoomTransformFromContext?.scale ?? this.canvasScale ?? 1;\n }\n\n /**\n * Convert screen pixel bounds to canvas coordinates.\n */\n private screenToCanvas(bounds: TransformBounds): TransformBounds {\n const scale = this.getZoomScale();\n return {\n x: bounds.x / scale,\n y: bounds.y / scale,\n width: bounds.width / scale,\n height: bounds.height / scale,\n rotation: bounds.rotation,\n };\n }\n\n connectedCallback() {\n super.connectedCallback();\n this.resizeObserver = new ResizeObserver(() => {\n // Dimensions are read directly when needed\n });\n if (this.offsetParent) {\n this.resizeObserver.observe(this.offsetParent);\n }\n // Forward wheel events to parent panzoom so zoom works even when pointer is over handles\n // Wheel events should pass through, but we'll forward them to ensure panzoom receives them\n this.addEventListener(\n \"wheel\",\n (e: WheelEvent) => {\n // Only forward if not actively interacting with handles\n if (this.interactionMode === \"idle\") {\n // Find parent panzoom and forward the event\n const panZoom = this.closest(\"ef-pan-zoom\");\n if (panZoom) {\n // Create a new wheel event and dispatch it on panzoom\n const wheelEvent = new WheelEvent(\"wheel\", {\n bubbles: true,\n cancelable: true,\n clientX: e.clientX,\n clientY: e.clientY,\n deltaX: e.deltaX,\n deltaY: e.deltaY,\n deltaZ: e.deltaZ,\n deltaMode: e.deltaMode,\n ctrlKey: e.ctrlKey,\n metaKey: e.metaKey,\n shiftKey: e.shiftKey,\n altKey: e.altKey,\n });\n panZoom.dispatchEvent(wheelEvent);\n }\n }\n },\n { passive: true },\n );\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.resizeObserver?.disconnect();\n this.cleanup();\n }\n\n /**\n * Transition interaction mode state machine.\n * Ensures only one mode is active at a time (invariant).\n */\n private transitionInteractionMode(\n event:\n | \"mousedown-drag\"\n | \"mousedown-resize\"\n | \"mousedown-rotate\"\n | \"mouseup\",\n ): InteractionMode {\n if (event === \"mouseup\") {\n return \"idle\";\n }\n // Only allow transition from idle\n if (this.interactionMode !== \"idle\") {\n return this.interactionMode;\n }\n switch (event) {\n case \"mousedown-drag\":\n return \"dragging\";\n case \"mousedown-resize\":\n return \"resizing\";\n case \"mousedown-rotate\":\n return \"rotating\";\n default:\n return \"idle\";\n }\n }\n\n private handleMouseDown = (e: MouseEvent) => {\n if (!this.enableDrag) return;\n e.stopPropagation();\n this.interactionMode = this.transitionInteractionMode(\"mousedown-drag\");\n this.mouseStart = { x: e.clientX, y: e.clientY };\n this.initialBounds = { ...this.bounds };\n document.addEventListener(\"mousemove\", this.handleMouseMove);\n document.addEventListener(\"mouseup\", this.handleMouseUp);\n };\n\n private handleResizeMouseDown = (e: MouseEvent, handle: ResizeHandle) => {\n if (!this.enableResize) return;\n e.stopPropagation();\n e.preventDefault();\n this.interactionMode = this.transitionInteractionMode(\"mousedown-resize\");\n this.activeResizeHandle = handle;\n this.mouseStart = { x: e.clientX, y: e.clientY };\n // Store initial bounds as-is (in screen pixels) - we'll convert to canvas when calculating\n this.initialBounds = { ...this.bounds };\n document.addEventListener(\"mousemove\", this.handleMouseMove);\n document.addEventListener(\"mouseup\", this.handleMouseUp);\n };\n\n private handleRotateMouseDown = (e: MouseEvent) => {\n if (!this.enableRotation) return;\n e.stopPropagation();\n this.interactionMode = this.transitionInteractionMode(\"mousedown-rotate\");\n this.mouseStart = { x: e.clientX, y: e.clientY };\n this.initialBounds = { ...this.bounds };\n document.addEventListener(\"mousemove\", this.handleMouseMove);\n document.addEventListener(\"mouseup\", this.handleMouseUp);\n };\n\n /**\n * Dispatch bounds change event (one-way data flow).\n * Parent updates element, then reads element and updates handle bounds prop.\n * We don't modify this.bounds directly - always render from prop.\n */\n private dispatchBoundsChange(bounds: TransformBounds): void {\n this.dispatchEvent(\n new CustomEvent(\"bounds-change\", {\n detail: { bounds },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private handleMouseMove = (e: MouseEvent) => {\n if (!this.mouseStart || !this.initialBounds) return;\n\n // Calculate mouse deltas in viewport coordinates\n const screenDeltaX = e.clientX - this.mouseStart.x;\n const screenDeltaY = e.clientY - this.mouseStart.y;\n\n switch (this.interactionMode) {\n case \"dragging\": {\n const zoomScale = this.getZoomScale();\n const initialCanvas = this.screenToCanvas(this.initialBounds);\n\n const newPosition = calculateDragBounds(\n { x: initialCanvas.x, y: initialCanvas.y },\n screenDeltaX,\n screenDeltaY,\n zoomScale,\n );\n\n this.dispatchBoundsChange({\n ...newPosition,\n width: initialCanvas.width,\n height: initialCanvas.height,\n rotation: this.initialBounds.rotation,\n });\n break;\n }\n\n case \"resizing\": {\n if (!this.activeResizeHandle) return;\n\n const zoomScale = this.getZoomScale();\n const initialCanvas = this.screenToCanvas(this.initialBounds);\n const rotation = this.enableRotation\n ? (this.initialBounds.rotation ?? 0)\n : 0;\n\n // Calculate the fixed corner (opposite to handle being dragged)\n const oppositeCorner = getOppositeCorner(this.activeResizeHandle);\n const rotationRadians = (rotation * Math.PI) / 180;\n const fixedCorner = getCornerPoint(\n initialCanvas.x,\n initialCanvas.y,\n initialCanvas.width,\n initialCanvas.height,\n rotationRadians,\n oppositeCorner.x,\n oppositeCorner.y,\n );\n\n const newCanvasBounds = calculateResizeBounds(\n { width: initialCanvas.width, height: initialCanvas.height },\n { x: initialCanvas.x, y: initialCanvas.y },\n fixedCorner,\n this.activeResizeHandle,\n screenDeltaX,\n screenDeltaY,\n rotation,\n this.minSize / zoomScale,\n zoomScale,\n {\n lockAspectRatio: this.lockAspectRatio || e.shiftKey,\n resizeFromCenter: e.ctrlKey || e.metaKey,\n },\n );\n\n // Preserve rotation\n newCanvasBounds.rotation = this.initialBounds.rotation;\n this.dispatchBoundsChange(newCanvasBounds);\n break;\n }\n\n case \"rotating\": {\n // Calculate center in screen coordinates (bounds are overlay-relative)\n const overlayRect = this.offsetParent?.getBoundingClientRect() ?? {\n left: 0,\n top: 0,\n };\n const centerX =\n overlayRect.left +\n this.initialBounds.x +\n this.initialBounds.width / 2;\n const centerY =\n overlayRect.top +\n this.initialBounds.y +\n this.initialBounds.height / 2;\n\n // Calculate angle from mouse start to current position\n const startAngle =\n Math.atan2(this.mouseStart.y - centerY, this.mouseStart.x - centerX) *\n (180 / Math.PI) +\n 90;\n const currentAngle =\n Math.atan2(e.clientY - centerY, e.clientX - centerX) *\n (180 / Math.PI) +\n 90;\n\n // Normalize angle difference to [-180, 180] to avoid wrapping issues\n let deltaAngle = currentAngle - startAngle;\n while (deltaAngle > 180) deltaAngle -= 360;\n while (deltaAngle < -180) deltaAngle += 360;\n\n let newRotation = (this.initialBounds.rotation ?? 0) + deltaAngle;\n if (this.rotationStep !== undefined && this.rotationStep > 0) {\n newRotation =\n Math.round(newRotation / this.rotationStep) * this.rotationStep;\n }\n\n this.dispatchEvent(\n new CustomEvent(\"rotation-change\", {\n detail: { rotation: newRotation },\n bubbles: true,\n composed: true,\n }),\n );\n break;\n }\n\n case \"idle\":\n // No action needed\n break;\n }\n };\n\n private handleMouseUp = () => {\n this.cleanup();\n };\n\n private cleanup() {\n this.interactionMode = this.transitionInteractionMode(\"mouseup\");\n this.activeResizeHandle = null;\n this.mouseStart = null;\n this.initialBounds = null;\n document.removeEventListener(\"mousemove\", this.handleMouseMove);\n document.removeEventListener(\"mouseup\", this.handleMouseUp);\n }\n\n render() {\n // Always render from bounds prop (one-way data flow)\n // During interaction: dispatch events, parent updates element, parent updates handle bounds prop\n const currentBounds = this.bounds;\n const rotation = this.enableRotation ? (currentBounds.rotation ?? 0) : 0;\n\n const overlayStyles: Record<string, string> = {\n left: `${currentBounds.x}px`,\n top: `${currentBounds.y}px`,\n width: `${currentBounds.width}px`,\n height: `${currentBounds.height}px`,\n };\n\n if (this.enableRotation && rotation !== 0) {\n overlayStyles.transform = `rotate(${rotation}deg)`;\n overlayStyles.transformOrigin = \"center\";\n }\n\n const allHandles: ResizeHandle[] = [\n \"nw\",\n \"n\",\n \"ne\",\n \"e\",\n \"se\",\n \"s\",\n \"sw\",\n \"w\",\n ];\n const cornerHandles: ResizeHandle[] = [\"nw\", \"ne\", \"se\", \"sw\"];\n const handles = this.cornersOnly ? cornerHandles : allHandles;\n\n return html`\n <div\n class=\"overlay ${this.interactionMode === \"dragging\" ? \"dragging\" : \"\"}\"\n style=${styleMap(overlayStyles)}\n >\n ${\n this.enableDrag\n ? html`\n <div\n class=\"drag-area\"\n @mousedown=${this.handleMouseDown}\n ></div>\n `\n : \"\"\n }\n ${\n this.enableResize\n ? handles.map((handle) => {\n const rotation = this.enableRotation\n ? (currentBounds.rotation ?? 0)\n : 0;\n const cursor = getResizeHandleCursor(handle, rotation);\n return html`\n <div\n class=\"handle ${handle}\"\n style=${styleMap({ cursor })}\n @mousedown=${(e: MouseEvent) => this.handleResizeMouseDown(e, handle)}\n ></div>\n `;\n })\n : \"\"\n }\n ${\n this.enableRotation\n ? html`\n <div class=\"rotate-handle\" @mousedown=${this.handleRotateMouseDown}>\n <div class=\"rotate-handle-circle\">\n <span>↻</span>\n </div>\n </div>\n `\n : \"\"\n }\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-transform-handles\": EFTransformHandles;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EFTransformHandles.js","names":["EFTransformHandles","overlayStyles: Record<string, string>"],"sources":["../../src/gui/EFTransformHandles.ts"],"sourcesContent":["import { consume } from \"@lit/context\";\nimport { css, html, LitElement } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { styleMap } from \"lit/directives/style-map.js\";\nimport { panZoomTransformContext } from \"./panZoomTransformContext.js\";\nimport type { PanZoomTransform } from \"../elements/EFPanZoom.js\";\nimport {\n type ResizeHandle,\n calculateDragBounds,\n calculateResizeBounds,\n getResizeHandleCursor,\n} from \"./transformCalculations.js\";\nimport { getCornerPoint, getOppositeCorner } from \"./transformUtils.js\";\n\nconst DEFAULT_MIN_SIZE = 10;\n\nexport interface TransformBounds {\n x: number;\n y: number;\n width: number;\n height: number;\n rotation?: number;\n}\n\n/**\n * Interaction mode enumeration.\n * Only one mode can be active at a time (invariant).\n */\ntype InteractionMode = \"idle\" | \"dragging\" | \"resizing\" | \"rotating\";\n\n@customElement(\"ef-transform-handles\")\nexport class EFTransformHandles extends LitElement {\n @property({ type: Object })\n bounds: TransformBounds = { x: 0, y: 0, width: 100, height: 100 };\n\n @property({ type: Number })\n minSize = DEFAULT_MIN_SIZE;\n\n @property({ type: String })\n target?: string;\n\n @consume({ context: panZoomTransformContext, subscribe: true })\n panZoomTransformFromContext?: PanZoomTransform;\n\n @property({ type: Number, attribute: \"canvas-scale\" })\n canvasScale = 1;\n\n @property({ type: Boolean, attribute: \"enable-rotation\" })\n enableRotation = false;\n\n @property({ type: Boolean, attribute: \"enable-resize\" })\n enableResize = true;\n\n @property({ type: Boolean, attribute: \"corners-only\" })\n cornersOnly = false;\n\n @property({ type: Boolean, attribute: \"lock-aspect-ratio\" })\n lockAspectRatio = false;\n\n @property({ type: Boolean, attribute: \"enable-drag\" })\n enableDrag = true;\n\n @property({ type: Number, attribute: \"rotation-step\" })\n rotationStep?: number;\n\n /**\n * Current interaction mode.\n * Invariant: Only one mode active at a time.\n */\n @state()\n interactionMode: InteractionMode = \"idle\";\n\n /**\n * Active resize handle when in \"resizing\" mode.\n * Only valid when interactionMode === \"resizing\".\n */\n private activeResizeHandle: ResizeHandle | null = null;\n\n /**\n * Mouse start position for calculating deltas.\n * Only valid during active interaction.\n */\n private mouseStart: { x: number; y: number } | null = null;\n\n /**\n * Initial bounds at interaction start - NEVER mutated during interaction.\n * All calculations derive from this + mouse deltas.\n * Note: Not a @state() property to avoid re-renders during interaction.\n */\n private initialBounds: TransformBounds | null = null;\n\n static styles = css`\n :host {\n display: block;\n position: absolute;\n pointer-events: none;\n }\n .overlay {\n position: absolute;\n border: 2px solid var(--ef-transform-handles-border-color, var(--ef-color-primary));\n pointer-events: none;\n }\n .overlay.dragging {\n border-color: var(--ef-transform-handles-dragging-border-color, var(--ef-color-primary));\n }\n .drag-area {\n position: absolute;\n inset: 0;\n cursor: move;\n pointer-events: none;\n }\n /* Only enable pointer events when actively dragging */\n .drag-area:active {\n pointer-events: auto;\n }\n .handle {\n position: absolute;\n width: 8px;\n height: 8px;\n background: var(--ef-transform-handles-handle-color, var(--ef-color-bg-elevated));\n border: 1px solid var(--ef-transform-handles-handle-border-color, var(--ef-color-primary));\n pointer-events: auto;\n /* Only capture pointer events, allow wheel events to pass through */\n touch-action: none;\n }\n .handle.nw { top: -4px; left: -4px; }\n .handle.n { top: -4px; left: 50%; transform: translateX(-50%); }\n .handle.ne { top: -4px; right: -4px; }\n .handle.e { top: 50%; right: -4px; transform: translateY(-50%); }\n .handle.se { bottom: -4px; right: -4px; }\n .handle.s { bottom: -4px; left: 50%; transform: translateX(-50%); }\n .handle.sw { bottom: -4px; left: -4px; }\n .handle.w { top: 50%; left: -4px; transform: translateY(-50%); }\n .rotate-handle {\n position: absolute;\n top: -30px;\n left: 50%;\n transform: translateX(-50%);\n cursor: grab;\n pointer-events: auto;\n /* Only capture pointer events, allow wheel events to pass through */\n touch-action: none;\n }\n .rotate-handle-circle {\n width: 24px;\n height: 24px;\n background: var(--ef-transform-handles-rotate-handle-color, var(--ef-color-success));\n border: 2px solid var(--ef-color-bg-elevated);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .rotate-handle-circle span {\n font-size: 12px;\n color: white;\n }\n `;\n\n private resizeObserver?: ResizeObserver;\n\n /**\n * Single source of truth for zoom scale.\n * Priority: context > prop > 1.0\n */\n private getZoomScale(): number {\n return this.panZoomTransformFromContext?.scale ?? this.canvasScale ?? 1;\n }\n\n /**\n * Convert screen pixel bounds to canvas coordinates.\n */\n private screenToCanvas(bounds: TransformBounds): TransformBounds {\n const scale = this.getZoomScale();\n return {\n x: bounds.x / scale,\n y: bounds.y / scale,\n width: bounds.width / scale,\n height: bounds.height / scale,\n rotation: bounds.rotation,\n };\n }\n\n connectedCallback() {\n super.connectedCallback();\n this.resizeObserver = new ResizeObserver(() => {\n // Dimensions are read directly when needed\n });\n if (this.offsetParent) {\n this.resizeObserver.observe(this.offsetParent);\n }\n // Forward wheel events to parent panzoom so zoom works even when pointer is over handles\n // Wheel events should pass through, but we'll forward them to ensure panzoom receives them\n this.addEventListener(\n \"wheel\",\n (e: WheelEvent) => {\n // Only forward if not actively interacting with handles\n if (this.interactionMode === \"idle\") {\n // Find parent panzoom and forward the event\n const panZoom = this.closest(\"ef-pan-zoom\");\n if (panZoom) {\n // Create a new wheel event and dispatch it on panzoom\n const wheelEvent = new WheelEvent(\"wheel\", {\n bubbles: true,\n cancelable: true,\n clientX: e.clientX,\n clientY: e.clientY,\n deltaX: e.deltaX,\n deltaY: e.deltaY,\n deltaZ: e.deltaZ,\n deltaMode: e.deltaMode,\n ctrlKey: e.ctrlKey,\n metaKey: e.metaKey,\n shiftKey: e.shiftKey,\n altKey: e.altKey,\n });\n panZoom.dispatchEvent(wheelEvent);\n }\n }\n },\n { passive: true },\n );\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.resizeObserver?.disconnect();\n this.cleanup();\n }\n\n /**\n * Transition interaction mode state machine.\n * Ensures only one mode is active at a time (invariant).\n */\n private transitionInteractionMode(\n event: \"mousedown-drag\" | \"mousedown-resize\" | \"mousedown-rotate\" | \"mouseup\",\n ): InteractionMode {\n if (event === \"mouseup\") {\n return \"idle\";\n }\n // Only allow transition from idle\n if (this.interactionMode !== \"idle\") {\n return this.interactionMode;\n }\n switch (event) {\n case \"mousedown-drag\":\n return \"dragging\";\n case \"mousedown-resize\":\n return \"resizing\";\n case \"mousedown-rotate\":\n return \"rotating\";\n default:\n return \"idle\";\n }\n }\n\n private handleMouseDown = (e: MouseEvent) => {\n if (!this.enableDrag) return;\n e.stopPropagation();\n this.interactionMode = this.transitionInteractionMode(\"mousedown-drag\");\n this.mouseStart = { x: e.clientX, y: e.clientY };\n this.initialBounds = { ...this.bounds };\n document.addEventListener(\"mousemove\", this.handleMouseMove);\n document.addEventListener(\"mouseup\", this.handleMouseUp);\n };\n\n private handleResizeMouseDown = (e: MouseEvent, handle: ResizeHandle) => {\n if (!this.enableResize) return;\n e.stopPropagation();\n e.preventDefault();\n this.interactionMode = this.transitionInteractionMode(\"mousedown-resize\");\n this.activeResizeHandle = handle;\n this.mouseStart = { x: e.clientX, y: e.clientY };\n // Store initial bounds as-is (in screen pixels) - we'll convert to canvas when calculating\n this.initialBounds = { ...this.bounds };\n document.addEventListener(\"mousemove\", this.handleMouseMove);\n document.addEventListener(\"mouseup\", this.handleMouseUp);\n };\n\n private handleRotateMouseDown = (e: MouseEvent) => {\n if (!this.enableRotation) return;\n e.stopPropagation();\n this.interactionMode = this.transitionInteractionMode(\"mousedown-rotate\");\n this.mouseStart = { x: e.clientX, y: e.clientY };\n this.initialBounds = { ...this.bounds };\n document.addEventListener(\"mousemove\", this.handleMouseMove);\n document.addEventListener(\"mouseup\", this.handleMouseUp);\n };\n\n /**\n * Dispatch bounds change event (one-way data flow).\n * Parent updates element, then reads element and updates handle bounds prop.\n * We don't modify this.bounds directly - always render from prop.\n */\n private dispatchBoundsChange(bounds: TransformBounds): void {\n this.dispatchEvent(\n new CustomEvent(\"bounds-change\", {\n detail: { bounds },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private handleMouseMove = (e: MouseEvent) => {\n if (!this.mouseStart || !this.initialBounds) return;\n\n // Calculate mouse deltas in viewport coordinates\n const screenDeltaX = e.clientX - this.mouseStart.x;\n const screenDeltaY = e.clientY - this.mouseStart.y;\n\n switch (this.interactionMode) {\n case \"dragging\": {\n const zoomScale = this.getZoomScale();\n const initialCanvas = this.screenToCanvas(this.initialBounds);\n\n const newPosition = calculateDragBounds(\n { x: initialCanvas.x, y: initialCanvas.y },\n screenDeltaX,\n screenDeltaY,\n zoomScale,\n );\n\n this.dispatchBoundsChange({\n ...newPosition,\n width: initialCanvas.width,\n height: initialCanvas.height,\n rotation: this.initialBounds.rotation,\n });\n break;\n }\n\n case \"resizing\": {\n if (!this.activeResizeHandle) return;\n\n const zoomScale = this.getZoomScale();\n const initialCanvas = this.screenToCanvas(this.initialBounds);\n const rotation = this.enableRotation ? (this.initialBounds.rotation ?? 0) : 0;\n\n // Calculate the fixed corner (opposite to handle being dragged)\n const oppositeCorner = getOppositeCorner(this.activeResizeHandle);\n const rotationRadians = (rotation * Math.PI) / 180;\n const fixedCorner = getCornerPoint(\n initialCanvas.x,\n initialCanvas.y,\n initialCanvas.width,\n initialCanvas.height,\n rotationRadians,\n oppositeCorner.x,\n oppositeCorner.y,\n );\n\n const newCanvasBounds = calculateResizeBounds(\n { width: initialCanvas.width, height: initialCanvas.height },\n { x: initialCanvas.x, y: initialCanvas.y },\n fixedCorner,\n this.activeResizeHandle,\n screenDeltaX,\n screenDeltaY,\n rotation,\n this.minSize / zoomScale,\n zoomScale,\n {\n lockAspectRatio: this.lockAspectRatio || e.shiftKey,\n resizeFromCenter: e.ctrlKey || e.metaKey,\n },\n );\n\n // Preserve rotation\n newCanvasBounds.rotation = this.initialBounds.rotation;\n this.dispatchBoundsChange(newCanvasBounds);\n break;\n }\n\n case \"rotating\": {\n // Calculate center in screen coordinates (bounds are overlay-relative)\n const overlayRect = this.offsetParent?.getBoundingClientRect() ?? {\n left: 0,\n top: 0,\n };\n const centerX = overlayRect.left + this.initialBounds.x + this.initialBounds.width / 2;\n const centerY = overlayRect.top + this.initialBounds.y + this.initialBounds.height / 2;\n\n // Calculate angle from mouse start to current position\n const startAngle =\n Math.atan2(this.mouseStart.y - centerY, this.mouseStart.x - centerX) * (180 / Math.PI) +\n 90;\n const currentAngle =\n Math.atan2(e.clientY - centerY, e.clientX - centerX) * (180 / Math.PI) + 90;\n\n // Normalize angle difference to [-180, 180] to avoid wrapping issues\n let deltaAngle = currentAngle - startAngle;\n while (deltaAngle > 180) deltaAngle -= 360;\n while (deltaAngle < -180) deltaAngle += 360;\n\n let newRotation = (this.initialBounds.rotation ?? 0) + deltaAngle;\n if (this.rotationStep !== undefined && this.rotationStep > 0) {\n newRotation = Math.round(newRotation / this.rotationStep) * this.rotationStep;\n }\n\n this.dispatchEvent(\n new CustomEvent(\"rotation-change\", {\n detail: { rotation: newRotation },\n bubbles: true,\n composed: true,\n }),\n );\n break;\n }\n\n case \"idle\":\n // No action needed\n break;\n }\n };\n\n private handleMouseUp = () => {\n this.cleanup();\n };\n\n private cleanup() {\n this.interactionMode = this.transitionInteractionMode(\"mouseup\");\n this.activeResizeHandle = null;\n this.mouseStart = null;\n this.initialBounds = null;\n document.removeEventListener(\"mousemove\", this.handleMouseMove);\n document.removeEventListener(\"mouseup\", this.handleMouseUp);\n }\n\n render() {\n // Always render from bounds prop (one-way data flow)\n // During interaction: dispatch events, parent updates element, parent updates handle bounds prop\n const currentBounds = this.bounds;\n const rotation = this.enableRotation ? (currentBounds.rotation ?? 0) : 0;\n\n const overlayStyles: Record<string, string> = {\n left: `${currentBounds.x}px`,\n top: `${currentBounds.y}px`,\n width: `${currentBounds.width}px`,\n height: `${currentBounds.height}px`,\n };\n\n if (this.enableRotation && rotation !== 0) {\n overlayStyles.transform = `rotate(${rotation}deg)`;\n overlayStyles.transformOrigin = \"center\";\n }\n\n const allHandles: ResizeHandle[] = [\"nw\", \"n\", \"ne\", \"e\", \"se\", \"s\", \"sw\", \"w\"];\n const cornerHandles: ResizeHandle[] = [\"nw\", \"ne\", \"se\", \"sw\"];\n const handles = this.cornersOnly ? cornerHandles : allHandles;\n\n return html`\n <div\n class=\"overlay ${this.interactionMode === \"dragging\" ? \"dragging\" : \"\"}\"\n style=${styleMap(overlayStyles)}\n >\n ${\n this.enableDrag\n ? html`\n <div\n class=\"drag-area\"\n @mousedown=${this.handleMouseDown}\n ></div>\n `\n : \"\"\n }\n ${\n this.enableResize\n ? handles.map((handle) => {\n const rotation = this.enableRotation ? (currentBounds.rotation ?? 0) : 0;\n const cursor = getResizeHandleCursor(handle, rotation);\n return html`\n <div\n class=\"handle ${handle}\"\n style=${styleMap({ cursor })}\n @mousedown=${(e: MouseEvent) => this.handleResizeMouseDown(e, handle)}\n ></div>\n `;\n })\n : \"\"\n }\n ${\n this.enableRotation\n ? html`\n <div class=\"rotate-handle\" @mousedown=${this.handleRotateMouseDown}>\n <div class=\"rotate-handle-circle\">\n <span>↻</span>\n </div>\n </div>\n `\n : \"\"\n }\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-transform-handles\": EFTransformHandles;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { ContextMixinInterface } from "./ContextMixin.js";
|
|
2
2
|
import { RenderToVideoOptions } from "../preview/renderTimegroupToVideo.types.js";
|
|
3
3
|
import "./EFFitScale.js";
|
|
4
|
-
import * as
|
|
4
|
+
import * as lit8 from "lit";
|
|
5
5
|
import { LitElement, PropertyValueMap } from "lit";
|
|
6
|
-
import * as
|
|
6
|
+
import * as lit_html8 from "lit-html";
|
|
7
7
|
import * as lit_html_directives_ref_js2 from "lit-html/directives/ref.js";
|
|
8
8
|
|
|
9
9
|
//#region src/gui/EFWorkbench.d.ts
|
|
10
10
|
declare const EFWorkbench_base: (new (...args: any[]) => ContextMixinInterface) & typeof LitElement;
|
|
11
11
|
declare class EFWorkbench extends EFWorkbench_base {
|
|
12
12
|
#private;
|
|
13
|
-
static styles:
|
|
13
|
+
static styles: lit8.CSSResult[];
|
|
14
14
|
rendering: boolean;
|
|
15
15
|
private panZoomTransform;
|
|
16
16
|
private isExporting;
|
|
@@ -200,7 +200,7 @@ declare class EFWorkbench extends EFWorkbench_base {
|
|
|
200
200
|
updated(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
|
|
201
201
|
drawOverlays: () => void;
|
|
202
202
|
private renderPlaybackStats;
|
|
203
|
-
render():
|
|
203
|
+
render(): lit_html8.TemplateResult<1>;
|
|
204
204
|
}
|
|
205
205
|
declare global {
|
|
206
206
|
interface HTMLElementTagNameMap {
|