@twick/canvas 0.14.20 → 0.15.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/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -9
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/helpers/constants.ts","../src/helpers/browser.ts","../src/helpers/canvas.util.ts","../src/components/element-controls.tsx","../../media-utils/dist/index.mjs","../src/components/elements.tsx","../src/hooks/use-twick-canvas.ts"],"sourcesContent":["/**\n * Default text properties for canvas text elements.\n * Provides consistent styling defaults for text elements added to the canvas.\n * \n * @example\n * ```js\n * import { DEFAULT_TEXT_PROPS } from '@twick/canvas';\n * \n * const textElement = {\n * ...DEFAULT_TEXT_PROPS,\n * text: \"Hello World\",\n * x: 100,\n * y: 100\n * };\n * ```\n */\nexport const DEFAULT_TEXT_PROPS = {\n /** Font family for text elements */\n family: \"Poppins\",\n /** Font size in pixels */\n size: 48,\n /** Text fill color */\n fill: \"#FFFFFF\",\n /** Text stroke color */\n stroke: \"#000000\",\n /** Stroke line width */\n lineWidth: 0,\n }\n \n/**\n * Default caption properties for canvas caption elements.\n * Provides enhanced styling defaults specifically for caption elements\n * with background colors, shadows, and highlight options.\n * \n * @example\n * ```js\n * import { DEFAULT_CAPTION_PROPS } from '@twick/canvas';\n * \n * const captionElement = {\n * ...DEFAULT_CAPTION_PROPS,\n * text: \"Video Caption\",\n * x: 50,\n * y: 50\n * };\n * ```\n */\nexport const DEFAULT_CAPTION_PROPS = {\n /** Font family for caption elements */\n family: \"Poppins\",\n /** Font size in pixels */\n size: 48,\n /** Text fill color */\n fill: \"#FFFFFF\",\n /** Font weight */\n fontWeight: 600,\n /** Color configuration object */\n color: {\n /** Text color */\n text: \"#FFFFFF\",\n /** Background color */\n background: \"#000000\",\n /** Highlight color */\n highlight: \"#FFFFFF\",\n },\n /** Text stroke color */\n stroke: \"#000000\",\n /** Stroke line width */\n lineWidth: 0.2,\n /** Shadow color */\n shadowColor: \"#000000\",\n /** Shadow blur radius */\n shadowBlur: 2,\n /** Shadow offset [x, y] */\n shadowOffset: [0, 0],\n}\n\n/**\n * Canvas operation constants for tracking canvas state changes.\n * Defines the different types of operations that can occur on canvas elements.\n * \n * @example\n * ```js\n * import { CANVAS_OPERATIONS } from '@twick/canvas';\n * \n * function handleCanvasOperation(operation) {\n * switch (operation) {\n * case CANVAS_OPERATIONS.ITEM_ADDED:\n * console.log('New item added to canvas');\n * break;\n * case CANVAS_OPERATIONS.ITEM_UPDATED:\n * console.log('Item updated on canvas');\n * break;\n * }\n * }\n * ```\n */\nexport const CANVAS_OPERATIONS = {\n /** An item has been selected on the canvas */\n ITEM_SELECTED: \"ITEM_SELECTED\",\n /** An item has been updated/modified on the canvas */\n ITEM_UPDATED: \"ITEM_UPDATED\",\n /** An item has been deleted from the canvas */\n ITEM_DELETED: \"ITEM_DELETED\",\n /** A new item has been added to the canvas */\n ITEM_ADDED: \"ITEM_ADDED\",\n /** Items have been grouped together */\n ITEM_GROUPED: \"ITEM_GROUPED\",\n /** Items have been ungrouped */\n ITEM_UNGROUPED: \"ITEM_UNGROUPED\",\n /** Caption properties have been updated */\n CAPTION_PROPS_UPDATED: \"CAPTION_PROPS_UPDATED\"\n}\n\n/**\n * Element type constants for canvas elements.\n * Defines the different types of elements that can be added to the canvas.\n * \n * @example\n * ```js\n * import { ELEMENT_TYPES } from '@twick/canvas';\n * \n * if (element.type === ELEMENT_TYPES.TEXT) {\n * // Handle text element\n * } else if (element.type === ELEMENT_TYPES.IMAGE) {\n * // Handle image element\n * }\n * ```\n */\nexport const ELEMENT_TYPES = {\n /** Text element type */\n TEXT: \"text\",\n /** Caption element type */\n CAPTION: \"caption\",\n /** Image element type */\n IMAGE: \"image\",\n /** Video element type */\n VIDEO: \"video\",\n /** Rectangle element type */\n RECT: \"rect\",\n /** Circle element type */\n CIRCLE: \"circle\",\n /** Background color element type */\n BACKGROUND_COLOR: \"backgroundColor\",\n}\n","/**\n * Checks if the code is running in a browser environment.\n * Returns true if window object is available, false otherwise.\n * \n * @example\n * ```js\n * import { isBrowser } from '@twick/canvas';\n * \n * if (isBrowser) {\n * // Browser-specific code\n * console.log('Running in browser');\n * }\n * ```\n */\nexport const isBrowser = typeof window !== 'undefined';\n\n/**\n * Checks if the Canvas API is supported in the current environment.\n * Returns true if HTMLCanvasElement is available, false otherwise.\n * \n * @example\n * ```js\n * import { isCanvasSupported } from '@twick/canvas';\n * \n * if (isCanvasSupported) {\n * // Canvas operations are safe\n * createCanvas();\n * }\n * ```\n */\nexport const isCanvasSupported = isBrowser && !!window.HTMLCanvasElement;\n\n/**\n * Checks if the Video API is supported in the current environment.\n * Returns true if HTMLVideoElement is available, false otherwise.\n * \n * @example\n * ```js\n * import { isVideoSupported } from '@twick/canvas';\n * \n * if (isVideoSupported) {\n * // Video operations are safe\n * addVideoElement();\n * }\n * ```\n */\nexport const isVideoSupported = isBrowser && !!window.HTMLVideoElement;\n\n/**\n * Asserts that the code is running in a browser environment.\n * Throws an error if the code is executed in a non-browser context\n * such as Node.js server-side rendering.\n * \n * @throws Error when not running in a browser environment\n * \n * @example\n * ```js\n * assertBrowser();\n * // Code continues if in browser, throws error if not\n * ```\n */\nexport function assertBrowser() {\n if (!isBrowser) {\n throw new Error('This code can only run in a browser environment');\n }\n}\n\n/**\n * Asserts that the Canvas API is supported in the current environment.\n * Checks for HTMLCanvasElement support and throws an error if canvas\n * functionality is not available.\n * \n * @throws Error when Canvas API is not supported\n * \n * @example\n * ```js\n * assertCanvasSupport();\n * // Code continues if canvas is supported, throws error if not\n * ```\n */\nexport function assertCanvasSupport() {\n if (!isCanvasSupported) {\n throw new Error('Canvas is not supported in this environment');\n }\n}\n\n/**\n * Asserts that the Video API is supported in the current environment.\n * Checks for HTMLVideoElement support and throws an error if video\n * functionality is not available.\n * \n * @throws Error when Video API is not supported\n * \n * @example\n * ```js\n * assertVideoSupport();\n * // Code continues if video is supported, throws error if not\n * ```\n */\nexport function assertVideoSupport() {\n if (!isVideoSupported) {\n throw new Error('Video is not supported in this environment');\n }\n} ","import { Canvas as FabricCanvas } from \"fabric\";\nimport { CanvasMetadata, CanvasProps } from \"../types\";\nimport { Dimensions, Position } from \"@twick/media-utils\";\nimport { assertBrowser, assertCanvasSupport } from \"./browser\";\n\n/**\n * Creates and initializes a Fabric.js canvas with specified configurations.\n * Sets up a canvas with proper scaling, background, and interaction settings\n * based on the provided video and canvas dimensions.\n *\n * @param videoSize - The dimensions of the video\n * @param canvasSize - The dimensions of the canvas\n * @param canvasContainer - The HTML container for the canvas\n * @param backgroundColor - Background color of the canvas\n * @param selectionBorderColor - Border color for selected objects\n * @param selectionLineWidth - Width of the selection border\n * @param uniScaleTransform - Ensures uniform scaling of objects\n * @param enableRetinaScaling - Enables retina scaling for higher DPI\n * @param touchZoomThreshold - Threshold for touch zoom interactions\n * @returns Object containing the initialized canvas and its metadata\n *\n * @example\n * ```js\n * const { canvas, canvasMetadata } = createCanvas({\n * videoSize: { width: 1920, height: 1080 },\n * canvasSize: { width: 800, height: 600 },\n * canvasRef: canvasElement,\n * backgroundColor: \"#000000\",\n * selectionBorderColor: \"#2563eb\"\n * });\n * ```\n */\nexport const createCanvas = ({\n videoSize,\n canvasSize,\n canvasRef,\n backgroundColor = \"#000000\",\n selectionBorderColor = \"#2563eb\",\n selectionLineWidth = 2,\n uniScaleTransform = true,\n enableRetinaScaling = true,\n touchZoomThreshold = 10,\n}: CanvasProps): { canvas: FabricCanvas; canvasMetadata: CanvasMetadata } => {\n assertBrowser();\n assertCanvasSupport();\n\n // Metadata for scaling and positioning on the canvas\n const canvasMetadata = {\n width: canvasSize.width,\n height: canvasSize.height,\n aspectRatio: canvasSize.width / canvasSize.height,\n scaleX: Number((canvasSize.width / videoSize.width).toFixed(2)) ,\n scaleY: Number((canvasSize.height / videoSize.height).toFixed(2)),\n };\n\n // Create and configure the Fabric.js canvas\n const canvas = new FabricCanvas(canvasRef, {\n backgroundColor,\n width: canvasSize.width,\n height: canvasSize.height,\n preserveObjectStacking: true,\n enableRetinaScaling,\n selectionBorderColor,\n selectionLineWidth,\n uniScaleTransform,\n touchZoomThreshold,\n renderOnAddRemove: false,\n stateful: false,\n selection: true,\n skipTargetFind: false,\n controlsAboveOverlay: true,\n });\n\n // Set dimensions and render canvas\n if (canvasRef) {\n canvas.setDimensions({\n width: canvasMetadata.width,\n height: canvasMetadata.height,\n });\n canvas.renderAll();\n }\n\n return {\n canvas,\n canvasMetadata,\n };\n};\n\n/**\n * Reorders elements on the canvas based on their zIndex property.\n * Sorts all canvas objects by their zIndex and re-adds them to maintain\n * proper layering order for visual elements.\n *\n * @param canvas - The Fabric.js canvas instance\n *\n * @example\n * ```js\n * reorderElementsByZIndex(canvas);\n * // Elements are now properly layered based on zIndex\n * ```\n */\nexport const reorderElementsByZIndex = (canvas: FabricCanvas) => {\n if (!canvas) return;\n const backgroundColor = canvas.backgroundColor;\n\n const objects = canvas.getObjects();\n console.log(\"objects\", objects);\n // Sort objects by zIndex and re-add to the canvas in order\n objects.sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0));\n\n canvas.clear();\n canvas.backgroundColor = backgroundColor;\n\n objects.forEach((obj) => canvas.add(obj));\n canvas.renderAll();\n};\n\n/**\n * Retrieves the context of a Fabric.js canvas.\n * \n * @param canvas - The Fabric.js canvas instance\n * @returns The context of the canvas\n */\nexport const getCanvasContext = (canvas: FabricCanvas | null | undefined) => {\n if (!canvas || !canvas.elements?.lower?.ctx) return;\n return canvas.elements?.lower?.ctx;\n};\n\n/**\n * Clears all elements from the canvas and re-renders it.\n * Removes all objects from the canvas while preserving the background\n * and triggers a re-render to update the display.\n *\n * @param canvas - The Fabric.js canvas instance\n *\n * @example\n * ```js\n * clearCanvas(canvas);\n * // Canvas is now empty and ready for new elements\n * ```\n */\nexport const clearCanvas = (canvas: FabricCanvas | null | undefined) => {\n try {\n if (!canvas || !getCanvasContext(canvas)) return;\n canvas.clear();\n canvas.renderAll();\n } catch (error) {\n console.warn(\"Error clearing canvas:\", error);\n }\n};\n\n/**\n * Converts a position from the video coordinate space to the canvas coordinate space.\n * Applies scaling and centering transformations to map video coordinates\n * to the corresponding canvas pixel positions.\n *\n * @param x - X-coordinate in video space\n * @param y - Y-coordinate in video space\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @returns Object containing the corresponding position in canvas space\n *\n * @example\n * ```js\n * const canvasPos = convertToCanvasPosition(100, 200, canvasMetadata);\n * // canvasPos = { x: 450, y: 500 }\n * ```\n */\nexport const convertToCanvasPosition = (\n x: number,\n y: number,\n canvasMetadata: CanvasMetadata\n): Position => {\n return {\n x: x * canvasMetadata.scaleX + canvasMetadata.width / 2,\n y: y * canvasMetadata.scaleY + canvasMetadata.height / 2,\n };\n};\n\n/**\n * Converts a position from the canvas coordinate space to the video coordinate space.\n * Applies inverse scaling and centering transformations to map canvas coordinates\n * back to the corresponding video coordinate positions.\n *\n * @param x - X-coordinate in canvas space\n * @param y - Y-coordinate in canvas space\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @param videoSize - Dimensions of the video\n * @returns Object containing the corresponding position in video space\n *\n * @example\n * ```js\n * const videoPos = convertToVideoPosition(450, 500, canvasMetadata, videoSize);\n * // videoPos = { x: 100, y: 200 }\n * ```\n */\nexport const convertToVideoPosition = (\n x: number,\n y: number,\n canvasMetadata: CanvasMetadata,\n videoSize: Dimensions\n): Position => {\n return {\n x: Number((x / canvasMetadata.scaleX - videoSize.width / 2).toFixed(2)),\n y: Number((y / canvasMetadata.scaleY - videoSize.height / 2).toFixed(2)),\n };\n};\n\n/**\n * Retrieves the current frame effect for a given seek time.\n * Searches through the item's frame effects to find the one that is active\n * at the specified seek time based on start and end time ranges.\n *\n * @param item - The item containing frame effects\n * @param seekTime - The current time to match against frame effects\n * @returns The current frame effect active at the given seek time, or undefined if none found\n *\n * @example\n * ```js\n * const currentEffect = getCurrentFrameEffect(videoElement, 5.5);\n * // Returns the frame effect active at 5.5 seconds, if any\n * ```\n */\nexport const getCurrentFrameEffect = (item: any, seekTime: number) => {\n let currentFrameEffect;\n for (let i = 0; i < item?.frameEffects?.length; i++) {\n if (\n item.frameEffects[i].s <= seekTime &&\n item.frameEffects[i].e >= seekTime\n ) {\n currentFrameEffect = item.frameEffects[i];\n break;\n }\n }\n return currentFrameEffect;\n};\n","import { Control, controlsUtils } from \"fabric\";\n\n/**\n * Disabled control for canvas elements.\n * Creates a control that appears disabled and doesn't perform any actions\n * when interacted with. Useful for showing non-interactive control points.\n * \n * @example\n * ```js\n * import { disabledControl } from '@twick/canvas';\n * \n * // Apply to a canvas object\n * object.setControlsVisibility({\n * mt: false, // Disable top control\n * mb: false, // Disable bottom control\n * ml: false, // Disable left control\n * mr: false, // Disable right control\n * bl: disabledControl, // Use disabled control for bottom-left\n * });\n * ```\n */\nexport const disabledControl = new Control({\n /** X position offset */\n x: 0,\n /** Y position offset */\n y: -0.5,\n /** Additional Y offset */\n offsetY: 0,\n /** Cursor style when hovering */\n cursorStyle: \"pointer\",\n /** Action handler that does nothing */\n actionHandler: () => {\n return true;\n },\n /** Name of the action */\n actionName: \"scale\",\n /** Render function for the control */\n render: function (ctx: CanvasRenderingContext2D,\n left: number,\n top: number) {\n const size = 0;\n ctx.save();\n ctx.translate(left, top);\n ctx.fillStyle = \"#red\";\n ctx.fillRect(-size / 2, -size / 2, size, size);\n ctx.restore();\n },\n });\n\n/**\n * Rotation control for canvas elements.\n * Creates a control that allows rotation of canvas objects with snapping\n * functionality for precise angle adjustments.\n * \n * @example\n * ```js\n * import { rotateControl } from '@twick/canvas';\n * \n * // Apply to a canvas object\n * object.setControlsVisibility({\n * mtr: rotateControl, // Use custom rotate control for top-right\n * });\n * \n * // Enable rotation\n * object.set({\n * hasRotatingPoint: true,\n * lockRotation: false\n * });\n * ```\n */\nexport const rotateControl = new Control({\n /** X position offset */\n x: 0,\n /** Y position offset */\n y: -0.5,\n /** Additional Y offset for positioning */\n offsetY: -25,\n /** Cursor style when hovering */\n cursorStyle: \"crosshair\",\n /** Action handler with rotation and snapping */\n actionHandler: controlsUtils.rotationWithSnapping,\n /** Name of the action */\n actionName: \"rotate\",\n /** Whether to show connection line */\n withConnection: true,\n });","const imageDimensionsCache = {};\nconst videoMetaCache = {};\nconst audioDurationCache = {};\n\nconst getAudioDuration = (audioSrc) => {\n if (audioDurationCache[audioSrc]) {\n return Promise.resolve(audioDurationCache[audioSrc]);\n }\n return new Promise((resolve, reject) => {\n const audio = document.createElement(\"audio\");\n audio.preload = \"metadata\";\n const isSafeUrl = /^(https?:|blob:|data:audio\\/)/i.test(audioSrc);\n if (!isSafeUrl) {\n throw new Error(\"Unsafe audio source URL\");\n }\n audio.src = audioSrc;\n audio.onloadedmetadata = () => {\n const duration = audio.duration;\n audioDurationCache[audioSrc] = duration;\n resolve(duration);\n };\n audio.onerror = () => {\n reject(new Error(\"Failed to load audio metadata\"));\n };\n });\n};\n\nconst concurrencyLimit = 5;\nlet activeCount = 0;\nconst queue = [];\nfunction runNext() {\n if (queue.length === 0 || activeCount >= concurrencyLimit) return;\n const next = queue.shift();\n if (next) {\n activeCount++;\n next();\n }\n}\nfunction limit(fn) {\n return new Promise((resolve, reject) => {\n const task = () => {\n fn().then(resolve).catch(reject).finally(() => {\n activeCount--;\n runNext();\n });\n };\n if (activeCount < concurrencyLimit) {\n activeCount++;\n task();\n } else {\n queue.push(task);\n }\n });\n}\n\nconst loadImageDimensions = (url) => {\n return new Promise((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(new Error(\"getImageDimensions() is only available in the browser.\"));\n return;\n }\n const img = new Image();\n img.onload = () => {\n resolve({ width: img.naturalWidth, height: img.naturalHeight });\n };\n img.onerror = reject;\n img.src = url;\n });\n};\nconst getImageDimensions = (url) => {\n if (imageDimensionsCache[url]) {\n return Promise.resolve(imageDimensionsCache[url]);\n }\n return limit(() => loadImageDimensions(url)).then((dimensions) => {\n imageDimensionsCache[url] = dimensions;\n return dimensions;\n });\n};\n\nconst getVideoMeta = (videoSrc) => {\n if (videoMetaCache[videoSrc]) {\n return Promise.resolve(videoMetaCache[videoSrc]);\n }\n return new Promise((resolve, reject) => {\n const video = document.createElement(\"video\");\n video.preload = \"metadata\";\n const isSafeUrl = /^(https?:|blob:|data:video\\/)/i.test(videoSrc);\n if (!isSafeUrl) {\n reject(new Error(\"Unsafe video source URL\"));\n return;\n }\n video.src = videoSrc;\n video.onloadedmetadata = () => {\n const meta = {\n width: video.videoWidth,\n height: video.videoHeight,\n duration: video.duration\n };\n videoMetaCache[videoSrc] = meta;\n resolve(meta);\n };\n video.onerror = () => reject(new Error(\"Failed to load video metadata\"));\n });\n};\n\nconst getThumbnail = async (videoUrl, seekTime = 0.1, playbackRate = 1) => {\n return new Promise((resolve, reject) => {\n const video = document.createElement(\"video\");\n video.crossOrigin = \"anonymous\";\n video.muted = true;\n video.playsInline = true;\n video.autoplay = false;\n video.preload = \"auto\";\n video.playbackRate = playbackRate;\n video.style.position = \"absolute\";\n video.style.left = \"-9999px\";\n video.style.top = \"-9999px\";\n video.style.width = \"1px\";\n video.style.height = \"1px\";\n video.style.opacity = \"0\";\n video.style.pointerEvents = \"none\";\n video.style.zIndex = \"-1\";\n let timeoutId;\n const cleanup = () => {\n if (video.parentNode) video.remove();\n if (timeoutId) clearTimeout(timeoutId);\n };\n const handleError = () => {\n cleanup();\n reject(new Error(`Failed to load video: ${video.error?.message || \"Unknown error\"}`));\n };\n const handleSeeked = () => {\n try {\n video.pause();\n const canvas = document.createElement(\"canvas\");\n const width = video.videoWidth || 640;\n const height = video.videoHeight || 360;\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n cleanup();\n reject(new Error(\"Failed to get canvas context\"));\n return;\n }\n ctx.drawImage(video, 0, 0, width, height);\n try {\n const dataUrl = canvas.toDataURL(\"image/jpeg\", 0.8);\n cleanup();\n resolve(dataUrl);\n } catch {\n canvas.toBlob((blob) => {\n if (!blob) {\n cleanup();\n reject(new Error(\"Failed to create Blob\"));\n return;\n }\n const blobUrl = URL.createObjectURL(blob);\n cleanup();\n resolve(blobUrl);\n }, \"image/jpeg\", 0.8);\n }\n } catch (err) {\n cleanup();\n reject(new Error(`Error creating thumbnail: ${err}`));\n }\n };\n video.addEventListener(\"error\", handleError, { once: true });\n video.addEventListener(\"seeked\", handleSeeked, { once: true });\n video.addEventListener(\"loadedmetadata\", () => {\n const playPromise = video.play();\n if (playPromise !== void 0) {\n playPromise.then(() => {\n video.currentTime = seekTime;\n }).catch(() => {\n video.currentTime = seekTime;\n });\n } else {\n video.currentTime = seekTime;\n }\n }, { once: true });\n timeoutId = window.setTimeout(() => {\n cleanup();\n reject(new Error(\"Video loading timed out\"));\n }, 15e3);\n video.src = videoUrl;\n document.body.appendChild(video);\n });\n};\n\nconst extractAudio = async ({\n src,\n playbackRate = 1,\n start = 0,\n end\n}) => {\n if (!src) throw new Error(\"src is required\");\n if (playbackRate <= 0) throw new Error(\"playbackRate must be > 0\");\n const isSafeUrl = /^(https?:|blob:|data:)/i.test(src);\n if (!isSafeUrl) throw new Error(\"Unsafe media source URL\");\n const audioBuffer = await fetchAndDecodeAudio(src);\n if (audioBuffer.duration === 0 || audioBuffer.length === 0) {\n throw new Error(\"No audio track found in the media source\");\n }\n if (isAudioSilent(audioBuffer)) {\n throw new Error(\"Audio track is silent (no audio content detected)\");\n }\n const clampedStart = Math.max(0, start || 0);\n const fullDuration = audioBuffer.duration;\n const clampedEnd = Math.min(\n typeof end === \"number\" ? end : fullDuration,\n fullDuration\n );\n if (clampedEnd <= clampedStart)\n throw new Error(\"Invalid range: end must be greater than start\");\n const renderedBuffer = await renderAudioSegment(\n audioBuffer,\n clampedStart,\n clampedEnd,\n playbackRate\n );\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\nconst hasAudio = async (src) => {\n if (!src) return false;\n const isSafeUrl = /^(https?:|blob:|data:)/i.test(src);\n if (!isSafeUrl) return false;\n try {\n const audioBuffer = await fetchAndDecodeAudio(src);\n if (audioBuffer.duration === 0 || audioBuffer.length === 0) {\n return false;\n }\n if (isAudioSilent(audioBuffer)) {\n return false;\n }\n return true;\n } catch (error) {\n return false;\n }\n};\nconst stitchAudio = async (segments, totalDuration) => {\n if (!segments || segments.length === 0) {\n throw new Error(\"At least one audio segment is required\");\n }\n const duration = totalDuration || Math.max(...segments.map((s) => s.e));\n const renderedBuffer = await createAudioTimeline(segments, duration);\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\nconst fetchAndDecodeAudio = async (src) => {\n const response = await fetch(src);\n if (!response.ok) throw new Error(`Failed to fetch source: ${response.status}`);\n const arrayBuffer = await response.arrayBuffer();\n return decodeAudioData(arrayBuffer);\n};\nconst decodeAudioData = async (arrayBuffer) => {\n const AudioContextCtor = window.AudioContext || window.webkitAudioContext;\n if (!AudioContextCtor) throw new Error(\"Web Audio API not supported\");\n const audioContext = new AudioContextCtor();\n try {\n return await new Promise((resolve, reject) => {\n audioContext.decodeAudioData(\n arrayBuffer.slice(0),\n (buf) => resolve(buf),\n (err) => reject(err || new Error(\"Failed to decode audio: no audio track found or unsupported format\"))\n );\n });\n } finally {\n audioContext.close();\n }\n};\nconst isAudioSilent = (buffer, threshold = 1e-3) => {\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const channelData = buffer.getChannelData(channel);\n for (let i = 0; i < channelData.length; i += 100) {\n if (Math.abs(channelData[i]) > threshold) {\n return false;\n }\n }\n }\n return true;\n};\nconst renderAudioSegment = async (audioBuffer, start, end, playbackRate) => {\n const OfflineAudioContextCtor = window.OfflineAudioContext || window.webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n const sampleRate = audioBuffer.sampleRate;\n const numChannels = audioBuffer.numberOfChannels;\n const sourceDuration = end - start;\n const renderedFrames = Math.max(\n 1,\n Math.ceil(sourceDuration / playbackRate * sampleRate)\n );\n const offline = new OfflineAudioContextCtor(numChannels, renderedFrames, sampleRate);\n const sourceNode = offline.createBufferSource();\n sourceNode.buffer = audioBuffer;\n sourceNode.playbackRate.value = playbackRate;\n sourceNode.connect(offline.destination);\n sourceNode.start(0, start, sourceDuration);\n return await offline.startRendering();\n};\nconst createAudioTimeline = async (segments, duration) => {\n const OfflineAudioContextCtor = window.OfflineAudioContext || window.webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n const sampleRate = 44100;\n const totalFrames = Math.ceil(duration * sampleRate);\n const offline = new OfflineAudioContextCtor(2, totalFrames, sampleRate);\n for (const segment of segments) {\n if (segment.s >= segment.e) {\n console.warn(`Invalid segment: start (${segment.s}) >= end (${segment.e})`);\n continue;\n }\n const volume = segment.volume ?? 1;\n if (volume <= 0) {\n console.warn(`Skipping muted segment: ${segment.src}`);\n continue;\n }\n try {\n const audioBuffer = await fetchAndDecodeAudio(segment.src);\n const segmentDuration = segment.e - segment.s;\n const sourceDuration = Math.min(segmentDuration, audioBuffer.duration);\n const source = offline.createBufferSource();\n source.buffer = audioBuffer;\n if (volume !== 1) {\n const gainNode = offline.createGain();\n gainNode.gain.value = volume;\n source.connect(gainNode);\n gainNode.connect(offline.destination);\n } else {\n source.connect(offline.destination);\n }\n source.start(segment.s, 0, sourceDuration);\n } catch (error) {\n console.warn(`Failed to process segment: ${segment.src}`, error);\n }\n }\n return await offline.startRendering();\n};\nconst audioBufferToMp3 = async (buffer) => {\n try {\n const wavArrayBuffer = audioBufferToWavArrayBuffer(buffer);\n const pcmBuffer = await decodeAudioData(wavArrayBuffer);\n return await encodePcmToMp3(pcmBuffer);\n } catch (error) {\n return audioBufferToWavBlob(buffer);\n }\n};\nconst audioBufferToWavArrayBuffer = (buffer) => {\n const numChannels = buffer.numberOfChannels;\n const sampleRate = buffer.sampleRate;\n const numFrames = buffer.length;\n const interleaved = interleave(buffer, numChannels, numFrames);\n const bytesPerSample = 2;\n const blockAlign = numChannels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n const dataSize = interleaved.length * bytesPerSample;\n const bufferSize = 44 + dataSize;\n const arrayBuffer = new ArrayBuffer(bufferSize);\n const view = new DataView(arrayBuffer);\n writeString(view, 0, \"RIFF\");\n view.setUint32(4, 36 + dataSize, true);\n writeString(view, 8, \"WAVE\");\n writeString(view, 12, \"fmt \");\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, numChannels, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, blockAlign, true);\n view.setUint16(34, 16, true);\n writeString(view, 36, \"data\");\n view.setUint32(40, dataSize, true);\n floatTo16BitPCM(view, 44, interleaved);\n return arrayBuffer;\n};\nconst encodePcmToMp3 = async (buffer) => {\n const lamejs = await import('./index-CXhwwSX-.js').then(n => n.i);\n const channels = buffer.numberOfChannels >= 2 ? 2 : 1;\n const targetSampleRate = 22050;\n const downsampledBuffer = downsampleAudioBuffer(buffer, targetSampleRate);\n const kbps = 48;\n const mp3encoder = new lamejs.default.Mp3Encoder(channels, targetSampleRate, kbps);\n const samplesPerFrame = 1152;\n const leftFloat = downsampledBuffer.getChannelData(0);\n const left = floatTo16(leftFloat);\n let right;\n if (channels === 2) {\n const rightFloat = downsampledBuffer.getChannelData(1);\n right = floatTo16(rightFloat);\n }\n const mp3Chunks = [];\n for (let i = 0; i < left.length; i += samplesPerFrame) {\n const leftChunk = left.subarray(i, Math.min(i + samplesPerFrame, left.length));\n let mp3buf;\n if (channels === 2 && right) {\n const rightChunk = right.subarray(i, Math.min(i + samplesPerFrame, right.length));\n mp3buf = mp3encoder.encodeBuffer(leftChunk, rightChunk);\n } else {\n mp3buf = mp3encoder.encodeBuffer(leftChunk);\n }\n if (mp3buf.length > 0) mp3Chunks.push(mp3buf);\n }\n const end = mp3encoder.flush();\n if (end.length > 0) mp3Chunks.push(end);\n return new Blob(mp3Chunks, { type: \"audio/mpeg\" });\n};\nconst audioBufferToWavBlob = (buffer) => {\n const arrayBuffer = audioBufferToWavArrayBuffer(buffer);\n return new Blob([arrayBuffer], { type: \"audio/wav\" });\n};\nconst downsampleAudioBuffer = (buffer, targetSampleRate) => {\n if (buffer.sampleRate === targetSampleRate) {\n return buffer;\n }\n const ratio = buffer.sampleRate / targetSampleRate;\n const newLength = Math.round(buffer.length / ratio);\n const newBuffer = new AudioContext().createBuffer(\n buffer.numberOfChannels,\n newLength,\n targetSampleRate\n );\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const oldData = buffer.getChannelData(channel);\n const newData = newBuffer.getChannelData(channel);\n for (let i = 0; i < newLength; i++) {\n const oldIndex = Math.floor(i * ratio);\n newData[i] = oldData[oldIndex];\n }\n }\n return newBuffer;\n};\nconst interleave = (buffer, numChannels, numFrames) => {\n if (numChannels === 1) {\n return buffer.getChannelData(0).slice(0, numFrames);\n }\n const result = new Float32Array(numFrames * numChannels);\n const channelData = [];\n for (let ch = 0; ch < numChannels; ch++) {\n channelData[ch] = buffer.getChannelData(ch);\n }\n let writeIndex = 0;\n for (let i = 0; i < numFrames; i++) {\n for (let ch = 0; ch < numChannels; ch++) {\n result[writeIndex++] = channelData[ch][i];\n }\n }\n return result;\n};\nconst floatTo16BitPCM = (view, offset, input) => {\n let pos = offset;\n for (let i = 0; i < input.length; i++, pos += 2) {\n let s = Math.max(-1, Math.min(1, input[i]));\n view.setInt16(pos, s < 0 ? s * 32768 : s * 32767, true);\n }\n};\nconst floatTo16 = (input) => {\n const output = new Int16Array(input.length);\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output[i] = s < 0 ? s * 32768 : s * 32767;\n }\n return output;\n};\nconst writeString = (view, offset, str) => {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n};\n\nconst getScaledDimensions = (width, height, maxWidth, maxHeight) => {\n if (width <= maxWidth && height <= maxHeight) {\n return {\n width: width % 2 === 0 ? width : width - 1,\n height: height % 2 === 0 ? height : height - 1\n };\n }\n const widthRatio = maxWidth / width;\n const heightRatio = maxHeight / height;\n const scale = Math.min(widthRatio, heightRatio);\n let scaledWidth = Math.round(width * scale);\n let scaledHeight = Math.round(height * scale);\n if (scaledWidth % 2 !== 0) {\n scaledWidth -= 1;\n }\n if (scaledHeight % 2 !== 0) {\n scaledHeight -= 1;\n }\n return {\n width: Math.min(scaledWidth, maxWidth),\n height: Math.min(scaledHeight, maxHeight)\n };\n};\nconst getObjectFitSize = (objectFit, elementSize, containerSize) => {\n const elementAspectRatio = elementSize.width / elementSize.height;\n const containerAspectRatio = containerSize.width / containerSize.height;\n switch (objectFit) {\n case \"contain\":\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio\n };\n } else {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height\n };\n }\n case \"cover\":\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height\n };\n } else {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio\n };\n }\n case \"fill\":\n return {\n width: containerSize.width,\n height: containerSize.height\n };\n default:\n return {\n width: elementSize.width,\n height: elementSize.height\n };\n }\n};\n\nconst blobUrlToFile = async (blobUrl, fileName) => {\n const response = await fetch(blobUrl);\n const blob = await response.blob();\n return new File([blob], fileName, { type: blob.type });\n};\nconst loadFile = (accept) => {\n return new Promise((resolve, reject) => {\n try {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.accept = accept;\n input.style.display = \"none\";\n document.body.appendChild(input);\n const cleanup = () => {\n input.value = \"\";\n document.body.removeChild(input);\n };\n input.onchange = () => {\n const file = input.files && input.files[0];\n cleanup();\n if (!file) {\n reject(new Error(\"No file selected\"));\n return;\n }\n resolve(file);\n };\n input.click();\n } catch (error) {\n reject(error);\n }\n });\n};\nconst saveAsFile = (content, type, name) => {\n const blob = typeof content === \"string\" ? new Blob([content], { type }) : content;\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = name;\n a.click();\n URL.revokeObjectURL(url);\n};\nconst downloadFile = async (url, filename) => {\n try {\n const response = await fetch(url);\n const blob = await response.blob();\n const downloadUrl = window.URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = downloadUrl;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n window.URL.revokeObjectURL(downloadUrl);\n } catch (error) {\n console.error(\"Error downloading file:\", error);\n throw error;\n }\n};\n\nconst detectMediaTypeFromUrl = async (url) => {\n try {\n const response = await fetch(url, { method: \"HEAD\" });\n const contentType = response.headers.get(\"Content-Type\");\n if (!contentType) return null;\n if (contentType.startsWith(\"image/\")) return \"image\";\n if (contentType.startsWith(\"video/\")) return \"video\";\n if (contentType.startsWith(\"audio/\")) return \"audio\";\n return null;\n } catch (error) {\n console.error(\"Fetch failed:\", error);\n return null;\n }\n};\n\nexport { blobUrlToFile, detectMediaTypeFromUrl, downloadFile, extractAudio, getAudioDuration, getImageDimensions, getObjectFitSize, getScaledDimensions, getThumbnail, getVideoMeta, hasAudio, limit, loadFile, saveAsFile, stitchAudio };\n//# sourceMappingURL=index.mjs.map\n","import {\n Canvas as FabricCanvas,\n FabricText,\n Textbox,\n Group,\n FabricImage,\n Rect,\n Circle,\n Shadow,\n} from \"fabric\";\nimport { convertToCanvasPosition } from \"../helpers/canvas.util\";\nimport {\n CanvasElement,\n CanvasMetadata,\n CaptionProps,\n FrameEffect,\n} from \"../types\";\nimport {\n DEFAULT_CAPTION_PROPS,\n DEFAULT_TEXT_PROPS,\n} from \"../helpers/constants\";\nimport { disabledControl, rotateControl } from \"./element-controls\";\nimport { getObjectFitSize, getThumbnail } from \"@twick/media-utils\";\n\n/**\n * Add a text element to the canvas.\n * Creates and configures a Fabric.js Textbox object with specified properties\n * including position, styling, interactive controls, and text wrapping support.\n *\n * @param element - The canvas element configuration\n * @param index - The z-index of the element\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata about the canvas including scale and dimensions\n * @returns The configured Fabric.js Textbox object with text wrapping enabled\n *\n * @example\n * ```js\n * const textElement = addTextElement({\n * element: { id: \"text1\", props: { text: \"Hello\", x: 100, y: 100, width: 200 } },\n * index: 1,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addTextElement = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n\n let width = element.props?.width ? element.props.width * canvasMetadata.scaleX : canvasMetadata.width;\n if (element.props?.maxWidth) {\n width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);\n }\n const text = new Textbox(element.props?.text || element.t || \"\", {\n left: x,\n top: y,\n originX: \"center\",\n originY: \"center\",\n angle: element.props?.rotation || 0,\n fontSize: Math.floor(\n (element.props?.fontSize || DEFAULT_TEXT_PROPS.size) *\n canvasMetadata.scaleX\n ),\n fontFamily: element.props?.fontFamily || DEFAULT_TEXT_PROPS.family,\n fontStyle: element.props?.fontStyle || \"normal\",\n fontWeight: element.props?.fontWeight || \"normal\",\n fill: element.props?.fill || DEFAULT_TEXT_PROPS.fill,\n opacity: element.props?.opacity ?? 1,\n width: width,\n splitByGrapheme: false,\n textAlign: element.props?.textAlign || \"center\",\n stroke: element.props?.stroke || DEFAULT_TEXT_PROPS.stroke,\n strokeWidth: element.props?.lineWidth || DEFAULT_TEXT_PROPS.lineWidth,\n shadow: element.props?.shadowColor\n ? new Shadow({\n offsetX:\n element.props?.shadowOffset?.length &&\n element.props?.shadowOffset?.length > 1\n ? element.props.shadowOffset[0] / 2\n : 1,\n offsetY:\n element.props?.shadowOffset?.length &&\n element.props?.shadowOffset.length > 1\n ? element.props.shadowOffset[1] / 2\n : 1,\n blur: (element.props?.shadowBlur || 2) / 2,\n color: element.props?.shadowColor,\n })\n : undefined,\n });\n\n // Assign metadata and custom controls\n text.set(\"id\", element.id);\n text.set(\"zIndex\", index);\n\n // Disable unwanted control points\n text.controls.mt = disabledControl;\n text.controls.mb = disabledControl;\n text.controls.ml = disabledControl;\n text.controls.mr = disabledControl;\n text.controls.bl = disabledControl;\n text.controls.br = disabledControl;\n text.controls.tl = disabledControl;\n text.controls.tr = disabledControl;\n text.controls.mtr = rotateControl;\n\n canvas.add(text);\n return text;\n};\n\n/**\n * Sets image properties for a Fabric.js image object.\n * Configures position, size, and metadata for image elements\n * on the canvas with proper scaling and positioning.\n *\n * @param img - The Fabric.js image object to configure\n * @param element - The canvas element configuration\n * @param index - The z-index of the element\n * @param canvasMetadata - Metadata about the canvas including scale and dimensions\n *\n * @example\n * ```js\n * setImageProps({\n * img: fabricImage,\n * element: { id: \"img1\", props: { width: 200, height: 150, x: 50, y: 50 } },\n * index: 2,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nconst setImageProps = ({\n img,\n element,\n index,\n canvasMetadata,\n}: {\n img: FabricImage;\n element: CanvasElement;\n index: number;\n canvasMetadata: CanvasMetadata;\n}) => {\n const width =\n (element.props?.width || 0) * canvasMetadata.scaleX || canvasMetadata.width;\n const height =\n (element.props?.height || 0) * canvasMetadata.scaleY ||\n canvasMetadata.height;\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n console.log(width, height, x, y);\n img.set(\"id\", element.id);\n img.set(\"zIndex\", index);\n img.set(\"width\", width);\n img.set(\"height\", height);\n img.set(\"left\", x);\n img.set(\"top\", y);\n img.set(\"opacity\", element.props?.opacity ?? 1);\n img.set(\"selectable\", true);\n img.set(\"hasControls\", true);\n img.set(\"touchAction\", \"all\");\n};\n\n/**\n * Add a caption element to the canvas based on provided props.\n * Creates a text element with caption-specific styling including\n * shadows, positioning, and font properties.\n *\n * @param element - The canvas element configuration\n * @param index - The z-index of the element\n * @param canvas - The Fabric.js canvas instance\n * @param captionProps - Default and user-defined caption properties\n * @param canvasMetadata - Metadata about the canvas including scale and dimensions\n * @returns The configured Fabric.js caption object\n *\n * @example\n * ```js\n * const captionElement = addCaptionElement({\n * element: { id: \"caption1\", props: { text: \"Caption\", pos: { x: 100, y: 100 } } },\n * index: 3,\n * canvas: fabricCanvas,\n * captionProps: { font: { size: 24, family: \"Arial\" } },\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addCaptionElement = ({\n element,\n index,\n canvas,\n captionProps,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n captionProps: CaptionProps;\n canvasMetadata: CanvasMetadata;\n}) => {\n const { x, y } = convertToCanvasPosition(\n (captionProps?.applyToAll ? captionProps?.x : element.props?.x) ?? 0,\n (captionProps?.applyToAll ? captionProps?.y : element.props?.y) ?? 0,\n canvasMetadata\n );\n\n const caption = new FabricText(element.props?.text || element.t || \"\", {\n left: x,\n top: y,\n originX: \"center\",\n originY: \"center\",\n angle: element.props?.rotation || 0,\n fontSize: Math.round(\n ((captionProps?.applyToAll\n ? captionProps?.font?.size\n : element.props?.font?.size ?? captionProps?.font?.size) ??\n DEFAULT_CAPTION_PROPS.size) * canvasMetadata.scaleX\n ),\n fontFamily:\n (captionProps?.applyToAll\n ? captionProps?.font?.family\n : element.props?.font?.family ?? captionProps?.font?.family) ??\n DEFAULT_CAPTION_PROPS.family,\n fill:\n (captionProps?.applyToAll\n ? captionProps.color?.text\n : element.props?.fill ?? captionProps.color?.text) ??\n DEFAULT_CAPTION_PROPS.fill,\n fontWeight: (captionProps?.applyToAll\n ? captionProps?.font?.weight\n : element.props?.fontWeight ?? captionProps?.font?.weight) ??\n DEFAULT_CAPTION_PROPS.fontWeight,\n stroke: (captionProps?.applyToAll\n ? captionProps?.stroke\n : element.props?.stroke ?? captionProps?.stroke) ??\n DEFAULT_CAPTION_PROPS.stroke,\n opacity: (captionProps?.applyToAll\n ? captionProps?.opacity\n : element.props?.opacity ?? captionProps?.opacity) ?? 1,\n shadow: new Shadow({\n offsetX:\n (captionProps?.applyToAll\n ? captionProps?.shadowOffset?.[0]\n : element.props?.shadowOffset?.[0] ?? captionProps?.shadowOffset?.[0]) ?? \n DEFAULT_CAPTION_PROPS.shadowOffset?.[0],\n offsetY:\n (captionProps?.applyToAll\n ? captionProps?.shadowOffset?.[1]\n : element.props?.shadowOffset?.[1] ?? captionProps?.shadowOffset?.[1]) ??\n DEFAULT_CAPTION_PROPS.shadowOffset?.[1],\n blur: (captionProps?.applyToAll\n ? captionProps?.shadowBlur\n : element.props?.shadowBlur ?? captionProps?.shadowBlur) ?? DEFAULT_CAPTION_PROPS.shadowBlur,\n color: (captionProps?.applyToAll\n ? captionProps?.shadowColor\n : element.props?.shadowColor ?? captionProps?.shadowColor) ?? DEFAULT_CAPTION_PROPS.shadowColor,\n }),\n strokeWidth: (captionProps?.applyToAll \n ? captionProps?.lineWidth\n : element.props?.lineWidth ?? captionProps?.lineWidth) ?? DEFAULT_CAPTION_PROPS.lineWidth,\n });\n\n // Assign metadata and custom controls\n caption.set(\"id\", element.id);\n caption.set(\"zIndex\", index);\n\n // Disable unwanted control points\n caption.controls.mt = disabledControl;\n caption.controls.mb = disabledControl;\n caption.controls.ml = disabledControl;\n caption.controls.mr = disabledControl;\n caption.controls.bl = disabledControl;\n caption.controls.br = disabledControl;\n caption.controls.tl = disabledControl;\n caption.controls.tr = disabledControl;\n caption.controls.mtr = disabledControl;\n\n canvas.add(caption);\n return caption;\n};\n\n/**\n * Add a video frame as element into a Fabric.js image object and optionally groups it with a frame.\n * Creates a video element by extracting a frame at the specified time and applying\n * optional frame effects for enhanced visual presentation.\n *\n * @param element - The video element containing properties like source and frame information\n * @param index - The z-index for ordering the element on the canvas\n * @param canvas - The Fabric.js canvas instance\n * @param snapTime - The time to snap the video frame with respect to full video duration\n * @param canvasMetadata - Metadata of the canvas, including dimensions and scale factors\n * @param currentFrameEffect - Optional frame effect to apply to the image\n * @returns A Fabric.js image object or a group with an image and frame\n *\n * @example\n * ```js\n * const videoElement = await addVideoElement({\n * element: {\n * id: \"video1\",\n * props: { src: \"video.mp4\", x: 100, y: 100 }\n * },\n * index: 2,\n * canvas: fabricCanvas,\n * snapTime: 5.0,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 },\n * currentFrameEffect: { shape: \"circle\", radius: 50 }\n * });\n * ```\n */\nexport const addVideoElement = async ({\n element,\n index,\n canvas,\n snapTime,\n canvasMetadata,\n currentFrameEffect,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n snapTime: number;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n}) => {\n try {\n const thumbnailUrl = await getThumbnail(\n element?.props?.src || \"\",\n snapTime\n );\n if (!thumbnailUrl) {\n console.error(\"Failed to get thumbnail\");\n return;\n }\n\n return addImageElement({\n imageUrl: thumbnailUrl,\n element,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n });\n } catch (error) {\n console.error(\"Error loading image:\", error);\n }\n};\n\n/**\n * Add an image element to the canvas and optionally group it with a frame.\n * Loads an image from URL and creates a Fabric.js image object with proper\n * positioning, scaling, and optional frame effects.\n *\n * @param imageUrl - Optional URL of the image to be added to the canvas\n * @param element - The image element containing properties like source and frame information\n * @param index - The z-index for ordering the element on the canvas\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata of the canvas including dimensions and scale factors\n * @param currentFrameEffect - Optional frame effect to apply to the image\n * @returns A Fabric.js image object or a group with an image and frame\n *\n * @example\n * ```js\n * const imageElement = await addImageElement({\n * imageUrl: \"https://example.com/image.jpg\",\n * element: { id: \"img1\", props: { src: \"image.jpg\", width: 200, height: 150 } },\n * index: 4,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addImageElement = async ({\n imageUrl,\n element,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n}: {\n imageUrl?: string;\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n}) => {\n try {\n // Load the image from the provided source URL\n const img = await FabricImage.fromURL(imageUrl || element.props.src || \"\");\n img.set({\n originX: \"center\",\n originY: \"center\",\n lockMovementX: false,\n lockMovementY: false,\n lockUniScaling: true,\n hasControls: false,\n selectable: false,\n });\n\n // Return the group if a frame is defined, otherwise return the image\n if (element.frame) {\n return addMediaGroup({\n element,\n img,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n });\n } else {\n setImageProps({ img, element, index, canvasMetadata });\n canvas.add(img);\n return img;\n }\n } catch (error) {\n console.error(\"Error loading image:\", error);\n }\n};\n\n/**\n * Add a Fabric.js group combining an image and its associated frame.\n * Applies styling, positioning, and scaling based on the given properties\n * and creates a grouped element for complex visual effects.\n *\n * @param element - The image element containing properties like frame, position, and styling\n * @param img - The Fabric.js image object to be included in the group\n * @param index - The z-index for ordering the group on the canvas\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata of the canvas including dimensions and scale factors\n * @param currentFrameEffect - Optional current frame effect to override default frame properties\n * @returns A Fabric.js group containing the image and frame with configured properties\n *\n * @example\n * ```js\n * const mediaGroup = addMediaGroup({\n * element: { id: \"group1\", frame: { size: [200, 150], x: 100, y: 100 } },\n * img: fabricImage,\n * index: 5,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nconst addMediaGroup = ({\n element,\n img,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n}: {\n element: CanvasElement;\n img: FabricImage;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n}) => {\n let frameSize;\n let angle;\n let framePosition;\n let frameRadius = 0;\n if (currentFrameEffect) {\n frameSize = {\n width:\n (currentFrameEffect.props.frameSize?.[0] || 0) *\n canvasMetadata.scaleX || canvasMetadata.width,\n height:\n (currentFrameEffect.props.frameSize?.[1] || 0) *\n canvasMetadata.scaleY || canvasMetadata.height,\n };\n angle = currentFrameEffect.props.rotation || 0;\n framePosition = currentFrameEffect.props.framePosition;\n if (currentFrameEffect.props.shape === \"circle\") {\n frameRadius = frameSize.width / 2;\n } else {\n frameRadius = currentFrameEffect?.props?.radius || 0;\n }\n } else {\n frameRadius = element?.frame?.radius || 0;\n frameSize = {\n width:\n (element?.frame?.size?.[0] || 0) * canvasMetadata.scaleX ||\n canvasMetadata.width,\n height:\n (element?.frame?.size?.[1] || 0) * canvasMetadata.scaleY ||\n canvasMetadata.height,\n };\n angle = element?.frame?.rotation || 0;\n framePosition = {\n x: element?.frame?.x || 0,\n y: element?.frame?.y || 0,\n };\n }\n\n const newSize = getObjectFitSize(\n element.objectFit,\n { width: img.width!, height: img.height! },\n frameSize\n );\n\n const frameRect = new Rect({\n originX: \"center\",\n originY: \"center\",\n lockMovementX: false,\n lockMovementY: false,\n lockUniScaling: true,\n hasControls: false,\n selectable: false,\n width: frameSize.width,\n height: frameSize.height,\n stroke: element?.frame?.stroke || \"#ffffff\",\n strokeWidth: element?.frame?.lineWidth || 0,\n hasRotatingPoint: true,\n rx: frameRadius || 0,\n ry: frameRadius || 0,\n });\n\n img.set({\n lockUniScaling: true,\n originX: \"center\",\n originY: \"center\",\n scaleX: newSize.width / img.width,\n scaleY: newSize.height / img.height,\n opacity: element.props?.opacity ?? 1,\n });\n\n const { x, y } = convertToCanvasPosition(\n framePosition?.x || 0,\n framePosition?.y || 0,\n canvasMetadata\n );\n\n const groupProps = {\n left: x,\n top: y,\n width: frameSize.width,\n height: frameSize.height,\n angle: angle,\n };\n\n // Customize the control points for the group\n // Change only the top control to a different style, keep others as circles\n\n const group = new Group([frameRect, img], {\n ...groupProps,\n originX: \"center\",\n originY: \"center\",\n angle: groupProps.angle,\n selectable: true,\n hasControls: true,\n hasBorders: true,\n clipPath: frameRect,\n });\n\n group.controls.mt = disabledControl;\n group.controls.mb = disabledControl;\n group.controls.ml = disabledControl;\n group.controls.mr = disabledControl;\n group.controls.mtr = rotateControl;\n\n group.set(\"id\", element.id);\n group.set(\"zIndex\", index);\n canvas.add(group);\n return group;\n};\n\n/**\n * Add a rectangular element to the canvas.\n * Creates a Fabric.js rectangle with specified properties including\n * position, size, styling, and interactive controls.\n *\n * @param element - The canvas element containing properties for the rectangle\n * @param index - The zIndex value used to determine the rendering order\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @returns A Fabric.js Rect object configured with the specified properties\n *\n * @example\n * ```js\n * const rectElement = addRectElement({\n * element: { id: \"rect1\", props: { width: 100, height: 50, x: 200, y: 150 } },\n * index: 6,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addRectElement = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n // Convert element's position to canvas coordinates\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n\n // Create a new rectangular Fabric.js object\n const rect = new Rect({\n left: x, // X-coordinate on the canvas\n top: y, // Y-coordinate on the canvas\n originX: \"center\", // Center the rectangle based on its position\n originY: \"center\", // Center the rectangle based on its position\n angle: element.props?.rotation || 0, // Rotation angle\n rx: (element.props?.radius || 0) * canvasMetadata.scaleX, // Horizontal radius for rounded corners\n ry: (element.props?.radius || 0) * canvasMetadata.scaleY, // Vertical radius for rounded corners\n stroke: element.props?.stroke || \"#000000\", // Stroke color\n strokeWidth: (element.props?.lineWidth || 0) * canvasMetadata.scaleX, // Scaled stroke width\n fill: element.props?.fill || \"#000000\", // Fill color\n opacity: element.props?.opacity || 1, // Opacity level\n width: (element.props?.width || 0) * canvasMetadata.scaleX, // Scaled width\n height: (element.props?.height || 0) * canvasMetadata.scaleY, // Scaled height\n });\n\n // Set custom properties for the rectangle\n rect.set(\"id\", element.id); // Unique identifier for the rectangle\n rect.set(\"zIndex\", index); // zIndex determines rendering order\n\n // Set custom control for rotation\n rect.controls.mtr = rotateControl;\n\n canvas.add(rect);\n return rect;\n};\n\nexport const addCircleElement = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n // Convert element's position to canvas coordinates\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n\n const circle = new Circle({\n left: x, // X-coordinate on the canvas\n top: y, // Y-coordinate on the canvas\n radius: (element.props?.radius || 0) * canvasMetadata.scaleX,\n fill: element.props?.fill || \"#000000\",\n stroke: element.props?.stroke || \"#000000\",\n strokeWidth: (element.props?.lineWidth || 0) * canvasMetadata.scaleX,\n originX: \"center\",\n originY: \"center\",\n });\n\n // Set custom control for rotation\n circle.controls.mt = disabledControl;\n circle.controls.mb = disabledControl;\n circle.controls.ml = disabledControl;\n circle.controls.mr = disabledControl;\n circle.controls.mtr = disabledControl;\n\n circle.set(\"id\", element.id);\n circle.set(\"zIndex\", index);\n canvas.add(circle);\n return circle;\n};\n\n/**\n * Add a background color to the canvas.\n * Creates a full-canvas rectangle with the specified background color\n * that serves as the base layer for other elements.\n *\n * @param element - The canvas element containing properties for the background\n * @param index - The zIndex value used to determine the rendering order\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @returns A Fabric.js Rect object configured with the specified properties\n *\n * @example\n * ```js\n * const bgElement = addBackgroundColor({\n * element: { id: \"bg1\", backgoundColor: \"#ffffff\" },\n * index: 0,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addBackgroundColor = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n const bgRect = new Rect({\n width: canvasMetadata.width,\n height: canvasMetadata.height,\n left: canvasMetadata.width / 2,\n top: canvasMetadata.height / 2,\n fill: element.backgoundColor ?? \"#000000\",\n originX: \"center\",\n originY: \"center\",\n hasControls: false,\n hasBorders: false,\n selectable: false,\n });\n\n bgRect.controls.mt = disabledControl;\n bgRect.controls.mb = disabledControl;\n bgRect.controls.ml = disabledControl;\n bgRect.controls.mr = disabledControl;\n bgRect.controls.bl = disabledControl;\n bgRect.controls.br = disabledControl;\n bgRect.controls.tl = disabledControl;\n bgRect.controls.tr = disabledControl;\n bgRect.controls.mtr = disabledControl;\n bgRect.set(\"zIndex\", index - 0.5);\n\n canvas.add(bgRect);\n return bgRect;\n};\n","import { useRef, useState } from \"react\";\nimport { Canvas as FabricCanvas, FabricObject, Textbox } from \"fabric\";\nimport { Dimensions } from \"@twick/media-utils\";\nimport {\n CanvasMetadata,\n CanvasProps,\n CanvasElement,\n CaptionProps,\n} from \"../types\";\nimport {\n clearCanvas,\n convertToVideoPosition,\n createCanvas,\n getCanvasContext,\n getCurrentFrameEffect,\n reorderElementsByZIndex,\n} from \"../helpers/canvas.util\";\nimport { CANVAS_OPERATIONS, ELEMENT_TYPES } from \"../helpers/constants\";\nimport {\n addImageElement,\n addVideoElement,\n addRectElement,\n addTextElement,\n addCaptionElement,\n addBackgroundColor,\n addCircleElement,\n} from \"../components/elements\";\n\n/**\n * Custom hook to manage a Fabric.js canvas and associated operations.\n * Provides functionality for canvas initialization, element management,\n * and event handling for interactive canvas operations.\n *\n * @param onCanvasReady - Callback executed when the canvas is ready\n * @param onCanvasOperation - Callback executed on canvas operations such as item selection or updates\n * @returns Object containing canvas-related functions and state\n *\n * @example\n * ```js\n * const { twickCanvas, buildCanvas, addElementToCanvas } = useTwickCanvas({\n * onCanvasReady: (canvas) => console.log('Canvas ready:', canvas),\n * onCanvasOperation: (operation, data) => console.log('Operation:', operation, data)\n * });\n * ```\n */\nexport const useTwickCanvas = ({\n onCanvasReady,\n onCanvasOperation,\n}: {\n onCanvasReady?: (canvas: FabricCanvas) => void;\n onCanvasOperation?: (operation: string, data: any) => void;\n}) => {\n const [twickCanvas, setTwickCanvas] = useState<FabricCanvas | null>(null); // Canvas instance\n const elementMap = useRef<Record<string, any>>({}); // Maps element IDs to their data\n const elementFrameMap = useRef<Record<string, any>>({}); // Maps element IDs to their frame effects\n const twickCanvasRef = useRef<FabricCanvas | null>(null);\n const videoSizeRef = useRef<Dimensions>({ width: 1, height: 1 }); // Stores the video dimensions\n const canvasResolutionRef = useRef<Dimensions>({ width: 1, height: 1 }); // Stores the canvas dimensions\n const captionPropsRef = useRef<CaptionProps | null>(null);\n const canvasMetadataRef = useRef<CanvasMetadata>({\n width: 0,\n height: 0,\n aspectRatio: 0,\n scaleX: 1,\n scaleY: 1,\n }); // Metadata for the canvas\n\n /**\n * Updates canvas metadata when the video size changes.\n * Recalculates scale factors based on the new video dimensions\n * to maintain proper coordinate mapping between canvas and video.\n *\n * @param videoSize - New video dimensions\n *\n * @example\n * ```js\n * onVideoSizeChange({ width: 1920, height: 1080 });\n * ```\n */\n const onVideoSizeChange = (videoSize: Dimensions) => {\n if (videoSize) {\n videoSizeRef.current = videoSize;\n canvasMetadataRef.current.scaleX =\n canvasMetadataRef.current.width / videoSize.width;\n canvasMetadataRef.current.scaleY =\n canvasMetadataRef.current.height / videoSize.height;\n }\n };\n\n /**\n * Initializes the Fabric.js canvas with the provided configuration.\n * Creates a new canvas instance with the specified properties and sets up\n * event listeners for interactive operations.\n *\n * @param props - Canvas configuration properties including size, colors, and behavior settings\n *\n * @example\n * ```js\n * buildCanvas({\n * videoSize: { width: 1920, height: 1080 },\n * canvasSize: { width: 800, height: 600 },\n * canvasRef: canvasElement,\n * backgroundColor: \"#000000\",\n * selectionBorderColor: \"#2563eb\"\n * });\n * ```\n */\n const buildCanvas = ({\n videoSize,\n canvasSize,\n canvasRef,\n backgroundColor = \"#000000\",\n selectionBorderColor = \"#2563eb\",\n selectionLineWidth = 2,\n uniScaleTransform = true,\n enableRetinaScaling = true,\n touchZoomThreshold = 10,\n forceBuild = false,\n }: CanvasProps & { forceBuild?: boolean }) => {\n if (!canvasRef) return;\n\n if (\n !forceBuild &&\n canvasResolutionRef.current.width === canvasSize.width &&\n canvasResolutionRef.current.height === canvasSize.height\n ) {\n return;\n }\n\n // Dispose of the old canvas if it exists\n if (twickCanvasRef.current) {\n console.log(\"Destroying twickCanvas\");\n twickCanvasRef.current.off(\"mouse:up\", handleMouseUp);\n twickCanvasRef.current.off(\"text:editing:exited\", onTextEdit);\n twickCanvasRef.current.dispose();\n }\n\n // Create a new canvas and update metadata\n const { canvas, canvasMetadata } = createCanvas({\n videoSize,\n canvasSize,\n canvasRef,\n backgroundColor,\n selectionBorderColor,\n selectionLineWidth,\n uniScaleTransform,\n enableRetinaScaling,\n touchZoomThreshold,\n });\n canvasMetadataRef.current = canvasMetadata;\n videoSizeRef.current = videoSize;\n // Attach event listeners\n canvas?.on(\"mouse:up\", handleMouseUp);\n canvas?.on(\"text:editing:exited\", onTextEdit);\n canvasResolutionRef.current = canvasSize;\n setTwickCanvas(canvas);\n twickCanvasRef.current = canvas;\n // Notify when canvas is ready\n if (onCanvasReady) {\n onCanvasReady(canvas);\n }\n };\n\n const onTextEdit = (event: any) => {\n if (event.target) {\n const object: FabricObject = event.target;\n const elementId = object.get(\"id\");\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n text:\n (object as Textbox).text ??\n elementMap.current[elementId].props.text,\n },\n };\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_UPDATED,\n elementMap.current[elementId]\n );\n }\n };\n\n /**\n * Handles mouse up events on the canvas.\n * Processes user interactions like dragging, scaling, and rotating elements,\n * updating element properties and triggering appropriate callbacks.\n *\n * @param event - Mouse event object containing interaction details\n */\n const handleMouseUp = (event: any) => {\n if (event.target) {\n const object: FabricObject = event.target;\n const elementId = object.get(\"id\");\n if (event.transform?.action === \"drag\") {\n const original = event.transform.original;\n if (object.left === original.left && object.top === original.top) {\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_SELECTED,\n elementMap.current[elementId]\n );\n return;\n }\n }\n switch (event.transform?.action) {\n case \"drag\":\n case \"scale\":\n case \"scaleX\":\n case \"scaleY\":\n case \"rotate\":\n const { x, y } = convertToVideoPosition(\n object.left,\n object.top,\n canvasMetadataRef.current,\n videoSizeRef.current\n );\n if (elementMap.current[elementId].type === \"caption\") {\n if (captionPropsRef.current?.applyToAll) {\n onCanvasOperation?.(CANVAS_OPERATIONS.CAPTION_PROPS_UPDATED, {\n element: elementMap.current[elementId],\n props: {\n ...captionPropsRef.current,\n x,\n y,\n },\n });\n } else {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n x,\n y,\n },\n };\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_UPDATED,\n elementMap.current[elementId]\n );\n }\n } else {\n if (object?.type === \"group\") {\n const currentFrameEffect = elementFrameMap.current[elementId];\n let updatedFrameSize;\n if (currentFrameEffect) {\n updatedFrameSize = [\n currentFrameEffect.props.frameSize[0] * object.scaleX,\n currentFrameEffect.props.frameSize[1] * object.scaleY,\n ];\n } else {\n updatedFrameSize = [\n elementMap.current[elementId].frame.size[0] * object.scaleX,\n elementMap.current[elementId].frame.size[1] * object.scaleY,\n ];\n }\n\n if (currentFrameEffect) {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n frameEffects: (\n elementMap.current[elementId].frameEffects || []\n ).map((frameEffect: any) =>\n frameEffect.id === currentFrameEffect?.id\n ? {\n ...frameEffect,\n props: {\n ...frameEffect.props,\n framePosition: {\n x,\n y,\n },\n frameSize: updatedFrameSize,\n },\n }\n : frameEffect\n ),\n };\n elementFrameMap.current[elementId] = {\n ...elementFrameMap.current[elementId],\n framePosition: {\n x,\n y,\n },\n frameSize: updatedFrameSize,\n };\n } else {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n frame: {\n ...elementMap.current[elementId].frame,\n rotation: object.angle,\n size: updatedFrameSize,\n x,\n y,\n },\n };\n }\n } else {\n if (object?.type === \"text\") {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n rotation: object.angle,\n x,\n y,\n },\n };\n } else if (object?.type === \"circle\") {\n const radius = Number(\n (\n elementMap.current[elementId].props.radius * object.scaleX\n ).toFixed(2)\n );\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n rotation: object.angle,\n radius: radius,\n height: radius * 2,\n width: radius * 2,\n x,\n y,\n },\n };\n } else {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n rotation: object.angle,\n width:\n elementMap.current[elementId].props.width * object.scaleX,\n height:\n elementMap.current[elementId].props.height *\n object.scaleY,\n x,\n y,\n },\n };\n }\n }\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_UPDATED,\n elementMap.current[elementId]\n );\n }\n break;\n }\n }\n };\n\n /**\n * Sets elements to the canvas.\n * Adds multiple elements to the canvas with optional cleanup and ordering.\n * Supports batch operations for efficient element management.\n *\n * @param options - Object containing elements, seek time, and additional options\n *\n * @example\n * ```js\n * await setCanvasElements({\n * elements: [element1, element2, element3],\n * seekTime: 5.0,\n * cleanAndAdd: true\n * });\n * ```\n */\n const setCanvasElements = async ({\n elements,\n seekTime = 0,\n captionProps,\n cleanAndAdd = false,\n }: {\n elements: CanvasElement[];\n seekTime?: number;\n captionProps?: any;\n cleanAndAdd?: boolean;\n }) => {\n if (!twickCanvas || !getCanvasContext(twickCanvas)) {\n console.warn(\"Canvas not properly initialized\");\n return;\n }\n\n try {\n if (cleanAndAdd && getCanvasContext(twickCanvas)) {\n // Store background color before clearing\n const backgroundColor = twickCanvas.backgroundColor;\n\n // Clear canvas before adding new elements\n clearCanvas(twickCanvas);\n\n // Restore background color\n if (backgroundColor) {\n twickCanvas.backgroundColor = backgroundColor;\n twickCanvas.renderAll();\n }\n }\n\n captionPropsRef.current = captionProps;\n await Promise.all(\n elements.map(async (element, index) => {\n try {\n if (!element) {\n console.warn(\"Element not found\");\n return;\n }\n await addElementToCanvas({\n element,\n index,\n reorder: false,\n seekTime,\n captionProps,\n });\n } catch (error) {\n console.error(`Error adding element ${element.id}:`, error);\n }\n })\n );\n reorderElementsByZIndex(twickCanvas);\n } catch (error) {\n console.error(\"Error in setCanvasElements:\", error);\n }\n };\n\n /**\n * Add element to the canvas.\n * Adds a single element to the canvas based on its type and properties.\n * Handles different element types (video, image, text, etc.) with appropriate rendering.\n *\n * @param options - Object containing element data, index, and rendering options\n *\n * @example\n * ```js\n * await addElementToCanvas({\n * element: videoElement,\n * index: 0,\n * reorder: true,\n * seekTime: 2.5\n * });\n * ```\n */\n const addElementToCanvas = async ({\n element,\n index,\n reorder = true,\n seekTime,\n captionProps,\n }: {\n element: CanvasElement;\n index: number;\n reorder: boolean;\n seekTime?: number;\n captionProps?: any;\n }) => {\n if (!twickCanvas) {\n console.warn(\"Canvas not initialized\");\n return;\n }\n // Add element based on type\n switch (element.type) {\n case ELEMENT_TYPES.VIDEO:\n const currentFrameEffect = getCurrentFrameEffect(\n element,\n seekTime || 0\n );\n elementFrameMap.current[element.id] = currentFrameEffect;\n const snapTime =\n ((seekTime || 0) - (element?.s || 0)) *\n (element?.props?.playbackRate || 1) +\n (element?.props?.time || 0);\n await addVideoElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n currentFrameEffect,\n snapTime,\n });\n if (element.timelineType === \"scene\") {\n await addBackgroundColor({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n }\n break;\n case ELEMENT_TYPES.IMAGE:\n await addImageElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n if (element.timelineType === \"scene\") {\n await addBackgroundColor({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n }\n break;\n case ELEMENT_TYPES.RECT:\n await addRectElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n case ELEMENT_TYPES.CIRCLE:\n await addCircleElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n case ELEMENT_TYPES.TEXT:\n await addTextElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n case ELEMENT_TYPES.CAPTION:\n await addCaptionElement({\n element,\n index,\n canvas: twickCanvas,\n captionProps,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n default:\n break;\n }\n elementMap.current[element.id] = element;\n if (reorder) {\n reorderElementsByZIndex(twickCanvas);\n }\n };\n\n return {\n twickCanvas,\n buildCanvas,\n onVideoSizeChange,\n addElementToCanvas,\n setCanvasElements,\n };\n};\n"],"names":["FabricCanvas","Control","controlsUtils","Textbox","Shadow","FabricText","FabricImage","Rect","Group","Circle","useState","useRef"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/helpers/constants.ts","../src/helpers/browser.ts","../src/helpers/canvas.util.ts","../src/components/element-controls.tsx","../../media-utils/dist/index.mjs","../src/components/elements.tsx","../src/hooks/use-twick-canvas.ts"],"sourcesContent":["/**\n * Default text properties for canvas text elements.\n * Provides consistent styling defaults for text elements added to the canvas.\n * \n * @example\n * ```js\n * import { DEFAULT_TEXT_PROPS } from '@twick/canvas';\n * \n * const textElement = {\n * ...DEFAULT_TEXT_PROPS,\n * text: \"Hello World\",\n * x: 100,\n * y: 100\n * };\n * ```\n */\nexport const DEFAULT_TEXT_PROPS = {\n /** Font family for text elements */\n family: \"Poppins\",\n /** Font size in pixels */\n size: 48,\n /** Text fill color */\n fill: \"#FFFFFF\",\n /** Text stroke color */\n stroke: \"#000000\",\n /** Stroke line width */\n lineWidth: 0,\n }\n \n/**\n * Default caption properties for canvas caption elements.\n * Provides enhanced styling defaults specifically for caption elements\n * with background colors, shadows, and highlight options.\n * \n * @example\n * ```js\n * import { DEFAULT_CAPTION_PROPS } from '@twick/canvas';\n * \n * const captionElement = {\n * ...DEFAULT_CAPTION_PROPS,\n * text: \"Video Caption\",\n * x: 50,\n * y: 50\n * };\n * ```\n */\nexport const DEFAULT_CAPTION_PROPS = {\n /** Font family for caption elements */\n family: \"Poppins\",\n /** Font size in pixels */\n size: 48,\n /** Text fill color */\n fill: \"#FFFFFF\",\n /** Font weight */\n fontWeight: 600,\n /** Color configuration object */\n color: {\n /** Text color */\n text: \"#FFFFFF\",\n /** Background color */\n background: \"#000000\",\n /** Highlight color */\n highlight: \"#FFFFFF\",\n },\n /** Text stroke color */\n stroke: \"#000000\",\n /** Stroke line width */\n lineWidth: 0.2,\n /** Shadow color */\n shadowColor: \"#000000\",\n /** Shadow blur radius */\n shadowBlur: 2,\n /** Shadow offset [x, y] */\n shadowOffset: [0, 0],\n}\n\n/**\n * Canvas operation constants for tracking canvas state changes.\n * Defines the different types of operations that can occur on canvas elements.\n * \n * @example\n * ```js\n * import { CANVAS_OPERATIONS } from '@twick/canvas';\n * \n * function handleCanvasOperation(operation) {\n * switch (operation) {\n * case CANVAS_OPERATIONS.ITEM_ADDED:\n * console.log('New item added to canvas');\n * break;\n * case CANVAS_OPERATIONS.ITEM_UPDATED:\n * console.log('Item updated on canvas');\n * break;\n * }\n * }\n * ```\n */\nexport const CANVAS_OPERATIONS = {\n /** An item has been selected on the canvas */\n ITEM_SELECTED: \"ITEM_SELECTED\",\n /** An item has been updated/modified on the canvas */\n ITEM_UPDATED: \"ITEM_UPDATED\",\n /** An item has been deleted from the canvas */\n ITEM_DELETED: \"ITEM_DELETED\",\n /** A new item has been added to the canvas */\n ITEM_ADDED: \"ITEM_ADDED\",\n /** Items have been grouped together */\n ITEM_GROUPED: \"ITEM_GROUPED\",\n /** Items have been ungrouped */\n ITEM_UNGROUPED: \"ITEM_UNGROUPED\",\n /** Caption properties have been updated */\n CAPTION_PROPS_UPDATED: \"CAPTION_PROPS_UPDATED\"\n}\n\n/**\n * Element type constants for canvas elements.\n * Defines the different types of elements that can be added to the canvas.\n * \n * @example\n * ```js\n * import { ELEMENT_TYPES } from '@twick/canvas';\n * \n * if (element.type === ELEMENT_TYPES.TEXT) {\n * // Handle text element\n * } else if (element.type === ELEMENT_TYPES.IMAGE) {\n * // Handle image element\n * }\n * ```\n */\nexport const ELEMENT_TYPES = {\n /** Text element type */\n TEXT: \"text\",\n /** Caption element type */\n CAPTION: \"caption\",\n /** Image element type */\n IMAGE: \"image\",\n /** Video element type */\n VIDEO: \"video\",\n /** Rectangle element type */\n RECT: \"rect\",\n /** Circle element type */\n CIRCLE: \"circle\",\n /** Background color element type */\n BACKGROUND_COLOR: \"backgroundColor\",\n}\n","/**\n * Checks if the code is running in a browser environment.\n * Returns true if window object is available, false otherwise.\n * \n * @example\n * ```js\n * import { isBrowser } from '@twick/canvas';\n * \n * if (isBrowser) {\n * // Browser-specific code\n * console.log('Running in browser');\n * }\n * ```\n */\nexport const isBrowser = typeof window !== 'undefined';\n\n/**\n * Checks if the Canvas API is supported in the current environment.\n * Returns true if HTMLCanvasElement is available, false otherwise.\n * \n * @example\n * ```js\n * import { isCanvasSupported } from '@twick/canvas';\n * \n * if (isCanvasSupported) {\n * // Canvas operations are safe\n * createCanvas();\n * }\n * ```\n */\nexport const isCanvasSupported = isBrowser && !!window.HTMLCanvasElement;\n\n/**\n * Checks if the Video API is supported in the current environment.\n * Returns true if HTMLVideoElement is available, false otherwise.\n * \n * @example\n * ```js\n * import { isVideoSupported } from '@twick/canvas';\n * \n * if (isVideoSupported) {\n * // Video operations are safe\n * addVideoElement();\n * }\n * ```\n */\nexport const isVideoSupported = isBrowser && !!window.HTMLVideoElement;\n\n/**\n * Asserts that the code is running in a browser environment.\n * Throws an error if the code is executed in a non-browser context\n * such as Node.js server-side rendering.\n * \n * @throws Error when not running in a browser environment\n * \n * @example\n * ```js\n * assertBrowser();\n * // Code continues if in browser, throws error if not\n * ```\n */\nexport function assertBrowser() {\n if (!isBrowser) {\n throw new Error('This code can only run in a browser environment');\n }\n}\n\n/**\n * Asserts that the Canvas API is supported in the current environment.\n * Checks for HTMLCanvasElement support and throws an error if canvas\n * functionality is not available.\n * \n * @throws Error when Canvas API is not supported\n * \n * @example\n * ```js\n * assertCanvasSupport();\n * // Code continues if canvas is supported, throws error if not\n * ```\n */\nexport function assertCanvasSupport() {\n if (!isCanvasSupported) {\n throw new Error('Canvas is not supported in this environment');\n }\n}\n\n/**\n * Asserts that the Video API is supported in the current environment.\n * Checks for HTMLVideoElement support and throws an error if video\n * functionality is not available.\n * \n * @throws Error when Video API is not supported\n * \n * @example\n * ```js\n * assertVideoSupport();\n * // Code continues if video is supported, throws error if not\n * ```\n */\nexport function assertVideoSupport() {\n if (!isVideoSupported) {\n throw new Error('Video is not supported in this environment');\n }\n} ","import { Canvas as FabricCanvas } from \"fabric\";\nimport { CanvasMetadata, CanvasProps } from \"../types\";\nimport { Dimensions, Position } from \"@twick/media-utils\";\nimport { assertBrowser, assertCanvasSupport } from \"./browser\";\n\n/**\n * Creates and initializes a Fabric.js canvas with specified configurations.\n * Sets up a canvas with proper scaling, background, and interaction settings\n * based on the provided video and canvas dimensions.\n *\n * @param videoSize - The dimensions of the video\n * @param canvasSize - The dimensions of the canvas\n * @param canvasContainer - The HTML container for the canvas\n * @param backgroundColor - Background color of the canvas\n * @param selectionBorderColor - Border color for selected objects\n * @param selectionLineWidth - Width of the selection border\n * @param uniScaleTransform - Ensures uniform scaling of objects\n * @param enableRetinaScaling - Enables retina scaling for higher DPI\n * @param touchZoomThreshold - Threshold for touch zoom interactions\n * @returns Object containing the initialized canvas and its metadata\n *\n * @example\n * ```js\n * const { canvas, canvasMetadata } = createCanvas({\n * videoSize: { width: 1920, height: 1080 },\n * canvasSize: { width: 800, height: 600 },\n * canvasRef: canvasElement,\n * backgroundColor: \"#000000\",\n * selectionBorderColor: \"#2563eb\"\n * });\n * ```\n */\nexport const createCanvas = ({\n videoSize,\n canvasSize,\n canvasRef,\n backgroundColor = \"#000000\",\n selectionBorderColor = \"#2563eb\",\n selectionLineWidth = 2,\n uniScaleTransform = true,\n enableRetinaScaling = true,\n touchZoomThreshold = 10,\n}: CanvasProps): { canvas: FabricCanvas; canvasMetadata: CanvasMetadata } => {\n assertBrowser();\n assertCanvasSupport();\n\n // Metadata for scaling and positioning on the canvas\n const canvasMetadata = {\n width: canvasSize.width,\n height: canvasSize.height,\n aspectRatio: canvasSize.width / canvasSize.height,\n scaleX: Number((canvasSize.width / videoSize.width).toFixed(2)) ,\n scaleY: Number((canvasSize.height / videoSize.height).toFixed(2)),\n };\n\n // Create and configure the Fabric.js canvas\n const canvas = new FabricCanvas(canvasRef, {\n backgroundColor,\n width: canvasSize.width,\n height: canvasSize.height,\n preserveObjectStacking: true,\n enableRetinaScaling,\n selectionBorderColor,\n selectionLineWidth,\n uniScaleTransform,\n touchZoomThreshold,\n renderOnAddRemove: false,\n stateful: false,\n selection: true,\n skipTargetFind: false,\n controlsAboveOverlay: true,\n });\n\n // Set dimensions and render canvas\n if (canvasRef) {\n canvas.setDimensions({\n width: canvasMetadata.width,\n height: canvasMetadata.height,\n });\n canvas.renderAll();\n }\n\n return {\n canvas,\n canvasMetadata,\n };\n};\n\n/**\n * Reorders elements on the canvas based on their zIndex property.\n * Sorts all canvas objects by their zIndex and re-adds them to maintain\n * proper layering order for visual elements.\n *\n * @param canvas - The Fabric.js canvas instance\n *\n * @example\n * ```js\n * reorderElementsByZIndex(canvas);\n * // Elements are now properly layered based on zIndex\n * ```\n */\nexport const reorderElementsByZIndex = (canvas: FabricCanvas) => {\n if (!canvas) return;\n const backgroundColor = canvas.backgroundColor;\n\n const objects = canvas.getObjects();\n console.log(\"objects\", objects);\n // Sort objects by zIndex and re-add to the canvas in order\n objects.sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0));\n\n canvas.clear();\n canvas.backgroundColor = backgroundColor;\n\n objects.forEach((obj) => canvas.add(obj));\n canvas.renderAll();\n};\n\n/**\n * Retrieves the context of a Fabric.js canvas.\n * \n * @param canvas - The Fabric.js canvas instance\n * @returns The context of the canvas\n */\nexport const getCanvasContext = (canvas: FabricCanvas | null | undefined) => {\n if (!canvas || !canvas.elements?.lower?.ctx) return;\n return canvas.elements?.lower?.ctx;\n};\n\n/**\n * Clears all elements from the canvas and re-renders it.\n * Removes all objects from the canvas while preserving the background\n * and triggers a re-render to update the display.\n *\n * @param canvas - The Fabric.js canvas instance\n *\n * @example\n * ```js\n * clearCanvas(canvas);\n * // Canvas is now empty and ready for new elements\n * ```\n */\nexport const clearCanvas = (canvas: FabricCanvas | null | undefined) => {\n try {\n if (!canvas || !getCanvasContext(canvas)) return;\n canvas.clear();\n canvas.renderAll();\n } catch (error) {\n console.warn(\"Error clearing canvas:\", error);\n }\n};\n\n/**\n * Converts a position from the video coordinate space to the canvas coordinate space.\n * Applies scaling and centering transformations to map video coordinates\n * to the corresponding canvas pixel positions.\n *\n * @param x - X-coordinate in video space\n * @param y - Y-coordinate in video space\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @returns Object containing the corresponding position in canvas space\n *\n * @example\n * ```js\n * const canvasPos = convertToCanvasPosition(100, 200, canvasMetadata);\n * // canvasPos = { x: 450, y: 500 }\n * ```\n */\nexport const convertToCanvasPosition = (\n x: number,\n y: number,\n canvasMetadata: CanvasMetadata\n): Position => {\n return {\n x: x * canvasMetadata.scaleX + canvasMetadata.width / 2,\n y: y * canvasMetadata.scaleY + canvasMetadata.height / 2,\n };\n};\n\n/**\n * Converts a position from the canvas coordinate space to the video coordinate space.\n * Applies inverse scaling and centering transformations to map canvas coordinates\n * back to the corresponding video coordinate positions.\n *\n * @param x - X-coordinate in canvas space\n * @param y - Y-coordinate in canvas space\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @param videoSize - Dimensions of the video\n * @returns Object containing the corresponding position in video space\n *\n * @example\n * ```js\n * const videoPos = convertToVideoPosition(450, 500, canvasMetadata, videoSize);\n * // videoPos = { x: 100, y: 200 }\n * ```\n */\nexport const convertToVideoPosition = (\n x: number,\n y: number,\n canvasMetadata: CanvasMetadata,\n videoSize: Dimensions\n): Position => {\n return {\n x: Number((x / canvasMetadata.scaleX - videoSize.width / 2).toFixed(2)),\n y: Number((y / canvasMetadata.scaleY - videoSize.height / 2).toFixed(2)),\n };\n};\n\n/**\n * Retrieves the current frame effect for a given seek time.\n * Searches through the item's frame effects to find the one that is active\n * at the specified seek time based on start and end time ranges.\n *\n * @param item - The item containing frame effects\n * @param seekTime - The current time to match against frame effects\n * @returns The current frame effect active at the given seek time, or undefined if none found\n *\n * @example\n * ```js\n * const currentEffect = getCurrentFrameEffect(videoElement, 5.5);\n * // Returns the frame effect active at 5.5 seconds, if any\n * ```\n */\nexport const getCurrentFrameEffect = (item: any, seekTime: number) => {\n let currentFrameEffect;\n for (let i = 0; i < item?.frameEffects?.length; i++) {\n if (\n item.frameEffects[i].s <= seekTime &&\n item.frameEffects[i].e >= seekTime\n ) {\n currentFrameEffect = item.frameEffects[i];\n break;\n }\n }\n return currentFrameEffect;\n};\n","import { Control, controlsUtils } from \"fabric\";\n\n/**\n * Disabled control for canvas elements.\n * Creates a control that appears disabled and doesn't perform any actions\n * when interacted with. Useful for showing non-interactive control points.\n * \n * @example\n * ```js\n * import { disabledControl } from '@twick/canvas';\n * \n * // Apply to a canvas object\n * object.setControlsVisibility({\n * mt: false, // Disable top control\n * mb: false, // Disable bottom control\n * ml: false, // Disable left control\n * mr: false, // Disable right control\n * bl: disabledControl, // Use disabled control for bottom-left\n * });\n * ```\n */\nexport const disabledControl = new Control({\n /** X position offset */\n x: 0,\n /** Y position offset */\n y: -0.5,\n /** Additional Y offset */\n offsetY: 0,\n /** Cursor style when hovering */\n cursorStyle: \"pointer\",\n /** Action handler that does nothing */\n actionHandler: () => {\n return true;\n },\n /** Name of the action */\n actionName: \"scale\",\n /** Render function for the control */\n render: function (ctx: CanvasRenderingContext2D,\n left: number,\n top: number) {\n const size = 0;\n ctx.save();\n ctx.translate(left, top);\n ctx.fillStyle = \"#red\";\n ctx.fillRect(-size / 2, -size / 2, size, size);\n ctx.restore();\n },\n });\n\n/**\n * Rotation control for canvas elements.\n * Creates a control that allows rotation of canvas objects with snapping\n * functionality for precise angle adjustments.\n * \n * @example\n * ```js\n * import { rotateControl } from '@twick/canvas';\n * \n * // Apply to a canvas object\n * object.setControlsVisibility({\n * mtr: rotateControl, // Use custom rotate control for top-right\n * });\n * \n * // Enable rotation\n * object.set({\n * hasRotatingPoint: true,\n * lockRotation: false\n * });\n * ```\n */\nexport const rotateControl = new Control({\n /** X position offset */\n x: 0,\n /** Y position offset */\n y: -0.5,\n /** Additional Y offset for positioning */\n offsetY: -25,\n /** Cursor style when hovering */\n cursorStyle: \"crosshair\",\n /** Action handler with rotation and snapping */\n actionHandler: controlsUtils.rotationWithSnapping,\n /** Name of the action */\n actionName: \"rotate\",\n /** Whether to show connection line */\n withConnection: true,\n });","const imageDimensionsCache = {};\nconst videoMetaCache = {};\nconst audioDurationCache = {};\n\nconst getAudioDuration = (audioSrc) => {\n if (audioDurationCache[audioSrc]) {\n return Promise.resolve(audioDurationCache[audioSrc]);\n }\n return new Promise((resolve, reject) => {\n const audio = document.createElement(\"audio\");\n audio.preload = \"metadata\";\n const isSafeUrl = /^(https?:|blob:|data:audio\\/)/i.test(audioSrc);\n if (!isSafeUrl) {\n throw new Error(\"Unsafe audio source URL\");\n }\n audio.src = audioSrc;\n audio.onloadedmetadata = () => {\n const duration = audio.duration;\n audioDurationCache[audioSrc] = duration;\n resolve(duration);\n };\n audio.onerror = () => {\n reject(new Error(\"Failed to load audio metadata\"));\n };\n });\n};\n\nconst concurrencyLimit = 5;\nlet activeCount = 0;\nconst queue = [];\nfunction runNext() {\n if (queue.length === 0 || activeCount >= concurrencyLimit) return;\n const next = queue.shift();\n if (next) {\n activeCount++;\n next();\n }\n}\nfunction limit(fn) {\n return new Promise((resolve, reject) => {\n const task = () => {\n fn().then(resolve).catch(reject).finally(() => {\n activeCount--;\n runNext();\n });\n };\n if (activeCount < concurrencyLimit) {\n activeCount++;\n task();\n } else {\n queue.push(task);\n }\n });\n}\n\nconst loadImageDimensions = (url) => {\n return new Promise((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(new Error(\"getImageDimensions() is only available in the browser.\"));\n return;\n }\n const img = new Image();\n img.onload = () => {\n resolve({ width: img.naturalWidth, height: img.naturalHeight });\n };\n img.onerror = reject;\n img.src = url;\n });\n};\nconst getImageDimensions = (url) => {\n if (imageDimensionsCache[url]) {\n return Promise.resolve(imageDimensionsCache[url]);\n }\n return limit(() => loadImageDimensions(url)).then((dimensions) => {\n imageDimensionsCache[url] = dimensions;\n return dimensions;\n });\n};\n\nconst getVideoMeta = (videoSrc) => {\n if (videoMetaCache[videoSrc]) {\n return Promise.resolve(videoMetaCache[videoSrc]);\n }\n return new Promise((resolve, reject) => {\n const video = document.createElement(\"video\");\n video.preload = \"metadata\";\n const isSafeUrl = /^(https?:|blob:|data:video\\/)/i.test(videoSrc);\n if (!isSafeUrl) {\n reject(new Error(\"Unsafe video source URL\"));\n return;\n }\n video.src = videoSrc;\n video.onloadedmetadata = () => {\n const meta = {\n width: video.videoWidth,\n height: video.videoHeight,\n duration: video.duration\n };\n videoMetaCache[videoSrc] = meta;\n resolve(meta);\n };\n video.onerror = () => reject(new Error(\"Failed to load video metadata\"));\n });\n};\n\nconst getThumbnail = async (videoUrl, seekTime = 0.1, playbackRate = 1) => {\n return new Promise((resolve, reject) => {\n const video = document.createElement(\"video\");\n video.crossOrigin = \"anonymous\";\n video.muted = true;\n video.playsInline = true;\n video.autoplay = false;\n video.preload = \"auto\";\n video.playbackRate = playbackRate;\n video.style.position = \"absolute\";\n video.style.left = \"-9999px\";\n video.style.top = \"-9999px\";\n video.style.width = \"1px\";\n video.style.height = \"1px\";\n video.style.opacity = \"0\";\n video.style.pointerEvents = \"none\";\n video.style.zIndex = \"-1\";\n let timeoutId;\n const cleanup = () => {\n if (video.parentNode) video.remove();\n if (timeoutId) clearTimeout(timeoutId);\n };\n const handleError = () => {\n cleanup();\n reject(new Error(`Failed to load video: ${video.error?.message || \"Unknown error\"}`));\n };\n const handleSeeked = () => {\n try {\n video.pause();\n const canvas = document.createElement(\"canvas\");\n const width = video.videoWidth || 640;\n const height = video.videoHeight || 360;\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n cleanup();\n reject(new Error(\"Failed to get canvas context\"));\n return;\n }\n ctx.drawImage(video, 0, 0, width, height);\n try {\n const dataUrl = canvas.toDataURL(\"image/jpeg\", 0.8);\n cleanup();\n resolve(dataUrl);\n } catch {\n canvas.toBlob((blob) => {\n if (!blob) {\n cleanup();\n reject(new Error(\"Failed to create Blob\"));\n return;\n }\n const blobUrl = URL.createObjectURL(blob);\n cleanup();\n resolve(blobUrl);\n }, \"image/jpeg\", 0.8);\n }\n } catch (err) {\n cleanup();\n reject(new Error(`Error creating thumbnail: ${err}`));\n }\n };\n video.addEventListener(\"error\", handleError, { once: true });\n video.addEventListener(\"seeked\", handleSeeked, { once: true });\n video.addEventListener(\"loadedmetadata\", () => {\n const playPromise = video.play();\n if (playPromise !== void 0) {\n playPromise.then(() => {\n video.currentTime = seekTime;\n }).catch(() => {\n video.currentTime = seekTime;\n });\n } else {\n video.currentTime = seekTime;\n }\n }, { once: true });\n timeoutId = window.setTimeout(() => {\n cleanup();\n reject(new Error(\"Video loading timed out\"));\n }, 15e3);\n video.src = videoUrl;\n document.body.appendChild(video);\n });\n};\n\nconst extractAudio = async ({\n src,\n playbackRate = 1,\n start = 0,\n end\n}) => {\n if (!src) throw new Error(\"src is required\");\n if (playbackRate <= 0) throw new Error(\"playbackRate must be > 0\");\n const isSafeUrl = /^(https?:|blob:|data:)/i.test(src);\n if (!isSafeUrl) throw new Error(\"Unsafe media source URL\");\n const audioBuffer = await fetchAndDecodeAudio(src);\n if (audioBuffer.duration === 0 || audioBuffer.length === 0) {\n throw new Error(\"No audio track found in the media source\");\n }\n if (isAudioSilent(audioBuffer)) {\n throw new Error(\"Audio track is silent (no audio content detected)\");\n }\n const clampedStart = Math.max(0, start || 0);\n const fullDuration = audioBuffer.duration;\n const clampedEnd = Math.min(\n typeof end === \"number\" ? end : fullDuration,\n fullDuration\n );\n if (clampedEnd <= clampedStart)\n throw new Error(\"Invalid range: end must be greater than start\");\n const renderedBuffer = await renderAudioSegment(\n audioBuffer,\n clampedStart,\n clampedEnd,\n playbackRate\n );\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\nconst hasAudio = async (src) => {\n if (!src) return false;\n const isSafeUrl = /^(https?:|blob:|data:)/i.test(src);\n if (!isSafeUrl) return false;\n try {\n const audioBuffer = await fetchAndDecodeAudio(src);\n if (audioBuffer.duration === 0 || audioBuffer.length === 0) {\n return false;\n }\n if (isAudioSilent(audioBuffer)) {\n return false;\n }\n return true;\n } catch (error) {\n return false;\n }\n};\nconst stitchAudio = async (segments, totalDuration) => {\n if (!segments || segments.length === 0) {\n throw new Error(\"At least one audio segment is required\");\n }\n const duration = totalDuration || Math.max(...segments.map((s) => s.e));\n const renderedBuffer = await createAudioTimeline(segments, duration);\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\nconst fetchAndDecodeAudio = async (src) => {\n const response = await fetch(src);\n if (!response.ok) throw new Error(`Failed to fetch source: ${response.status}`);\n const arrayBuffer = await response.arrayBuffer();\n return decodeAudioData(arrayBuffer);\n};\nconst decodeAudioData = async (arrayBuffer) => {\n const AudioContextCtor = window.AudioContext || window.webkitAudioContext;\n if (!AudioContextCtor) throw new Error(\"Web Audio API not supported\");\n const audioContext = new AudioContextCtor();\n try {\n return await new Promise((resolve, reject) => {\n audioContext.decodeAudioData(\n arrayBuffer.slice(0),\n (buf) => resolve(buf),\n (err) => reject(err || new Error(\"Failed to decode audio: no audio track found or unsupported format\"))\n );\n });\n } finally {\n audioContext.close();\n }\n};\nconst isAudioSilent = (buffer, threshold = 1e-3) => {\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const channelData = buffer.getChannelData(channel);\n for (let i = 0; i < channelData.length; i += 100) {\n if (Math.abs(channelData[i]) > threshold) {\n return false;\n }\n }\n }\n return true;\n};\nconst renderAudioSegment = async (audioBuffer, start, end, playbackRate) => {\n const OfflineAudioContextCtor = window.OfflineAudioContext || window.webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n const sampleRate = audioBuffer.sampleRate;\n const numChannels = audioBuffer.numberOfChannels;\n const sourceDuration = end - start;\n const renderedFrames = Math.max(\n 1,\n Math.ceil(sourceDuration / playbackRate * sampleRate)\n );\n const offline = new OfflineAudioContextCtor(numChannels, renderedFrames, sampleRate);\n const sourceNode = offline.createBufferSource();\n sourceNode.buffer = audioBuffer;\n sourceNode.playbackRate.value = playbackRate;\n sourceNode.connect(offline.destination);\n sourceNode.start(0, start, sourceDuration);\n return await offline.startRendering();\n};\nconst createAudioTimeline = async (segments, duration) => {\n const OfflineAudioContextCtor = window.OfflineAudioContext || window.webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n const sampleRate = 44100;\n const totalFrames = Math.ceil(duration * sampleRate);\n const offline = new OfflineAudioContextCtor(2, totalFrames, sampleRate);\n for (const segment of segments) {\n if (segment.s >= segment.e) {\n console.warn(`Invalid segment: start (${segment.s}) >= end (${segment.e})`);\n continue;\n }\n const volume = segment.volume ?? 1;\n if (volume <= 0) {\n console.warn(`Skipping muted segment: ${segment.src}`);\n continue;\n }\n try {\n const audioBuffer = await fetchAndDecodeAudio(segment.src);\n const segmentDuration = segment.e - segment.s;\n const sourceDuration = Math.min(segmentDuration, audioBuffer.duration);\n const source = offline.createBufferSource();\n source.buffer = audioBuffer;\n if (volume !== 1) {\n const gainNode = offline.createGain();\n gainNode.gain.value = volume;\n source.connect(gainNode);\n gainNode.connect(offline.destination);\n } else {\n source.connect(offline.destination);\n }\n source.start(segment.s, 0, sourceDuration);\n } catch (error) {\n console.warn(`Failed to process segment: ${segment.src}`, error);\n }\n }\n return await offline.startRendering();\n};\nconst audioBufferToMp3 = async (buffer) => {\n try {\n const wavArrayBuffer = audioBufferToWavArrayBuffer(buffer);\n const pcmBuffer = await decodeAudioData(wavArrayBuffer);\n return await encodePcmToMp3(pcmBuffer);\n } catch (error) {\n return audioBufferToWavBlob(buffer);\n }\n};\nconst audioBufferToWavArrayBuffer = (buffer) => {\n const numChannels = buffer.numberOfChannels;\n const sampleRate = buffer.sampleRate;\n const numFrames = buffer.length;\n const interleaved = interleave(buffer, numChannels, numFrames);\n const bytesPerSample = 2;\n const blockAlign = numChannels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n const dataSize = interleaved.length * bytesPerSample;\n const bufferSize = 44 + dataSize;\n const arrayBuffer = new ArrayBuffer(bufferSize);\n const view = new DataView(arrayBuffer);\n writeString(view, 0, \"RIFF\");\n view.setUint32(4, 36 + dataSize, true);\n writeString(view, 8, \"WAVE\");\n writeString(view, 12, \"fmt \");\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, numChannels, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, blockAlign, true);\n view.setUint16(34, 16, true);\n writeString(view, 36, \"data\");\n view.setUint32(40, dataSize, true);\n floatTo16BitPCM(view, 44, interleaved);\n return arrayBuffer;\n};\nconst encodePcmToMp3 = async (buffer) => {\n const lamejs = await import('./index-CXhwwSX-.mjs').then(n => n.i);\n const channels = buffer.numberOfChannels >= 2 ? 2 : 1;\n const targetSampleRate = 22050;\n const downsampledBuffer = downsampleAudioBuffer(buffer, targetSampleRate);\n const kbps = 48;\n const mp3encoder = new lamejs.default.Mp3Encoder(channels, targetSampleRate, kbps);\n const samplesPerFrame = 1152;\n const leftFloat = downsampledBuffer.getChannelData(0);\n const left = floatTo16(leftFloat);\n let right;\n if (channels === 2) {\n const rightFloat = downsampledBuffer.getChannelData(1);\n right = floatTo16(rightFloat);\n }\n const mp3Chunks = [];\n for (let i = 0; i < left.length; i += samplesPerFrame) {\n const leftChunk = left.subarray(i, Math.min(i + samplesPerFrame, left.length));\n let mp3buf;\n if (channels === 2 && right) {\n const rightChunk = right.subarray(i, Math.min(i + samplesPerFrame, right.length));\n mp3buf = mp3encoder.encodeBuffer(leftChunk, rightChunk);\n } else {\n mp3buf = mp3encoder.encodeBuffer(leftChunk);\n }\n if (mp3buf.length > 0) mp3Chunks.push(mp3buf);\n }\n const end = mp3encoder.flush();\n if (end.length > 0) mp3Chunks.push(end);\n return new Blob(mp3Chunks, { type: \"audio/mpeg\" });\n};\nconst audioBufferToWavBlob = (buffer) => {\n const arrayBuffer = audioBufferToWavArrayBuffer(buffer);\n return new Blob([arrayBuffer], { type: \"audio/wav\" });\n};\nconst downsampleAudioBuffer = (buffer, targetSampleRate) => {\n if (buffer.sampleRate === targetSampleRate) {\n return buffer;\n }\n const ratio = buffer.sampleRate / targetSampleRate;\n const newLength = Math.round(buffer.length / ratio);\n const newBuffer = new AudioContext().createBuffer(\n buffer.numberOfChannels,\n newLength,\n targetSampleRate\n );\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const oldData = buffer.getChannelData(channel);\n const newData = newBuffer.getChannelData(channel);\n for (let i = 0; i < newLength; i++) {\n const oldIndex = Math.floor(i * ratio);\n newData[i] = oldData[oldIndex];\n }\n }\n return newBuffer;\n};\nconst interleave = (buffer, numChannels, numFrames) => {\n if (numChannels === 1) {\n return buffer.getChannelData(0).slice(0, numFrames);\n }\n const result = new Float32Array(numFrames * numChannels);\n const channelData = [];\n for (let ch = 0; ch < numChannels; ch++) {\n channelData[ch] = buffer.getChannelData(ch);\n }\n let writeIndex = 0;\n for (let i = 0; i < numFrames; i++) {\n for (let ch = 0; ch < numChannels; ch++) {\n result[writeIndex++] = channelData[ch][i];\n }\n }\n return result;\n};\nconst floatTo16BitPCM = (view, offset, input) => {\n let pos = offset;\n for (let i = 0; i < input.length; i++, pos += 2) {\n let s = Math.max(-1, Math.min(1, input[i]));\n view.setInt16(pos, s < 0 ? s * 32768 : s * 32767, true);\n }\n};\nconst floatTo16 = (input) => {\n const output = new Int16Array(input.length);\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output[i] = s < 0 ? s * 32768 : s * 32767;\n }\n return output;\n};\nconst writeString = (view, offset, str) => {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n};\n\nconst getScaledDimensions = (width, height, maxWidth, maxHeight) => {\n if (width <= maxWidth && height <= maxHeight) {\n return {\n width: width % 2 === 0 ? width : width - 1,\n height: height % 2 === 0 ? height : height - 1\n };\n }\n const widthRatio = maxWidth / width;\n const heightRatio = maxHeight / height;\n const scale = Math.min(widthRatio, heightRatio);\n let scaledWidth = Math.round(width * scale);\n let scaledHeight = Math.round(height * scale);\n if (scaledWidth % 2 !== 0) {\n scaledWidth -= 1;\n }\n if (scaledHeight % 2 !== 0) {\n scaledHeight -= 1;\n }\n return {\n width: Math.min(scaledWidth, maxWidth),\n height: Math.min(scaledHeight, maxHeight)\n };\n};\nconst getObjectFitSize = (objectFit, elementSize, containerSize) => {\n const elementAspectRatio = elementSize.width / elementSize.height;\n const containerAspectRatio = containerSize.width / containerSize.height;\n switch (objectFit) {\n case \"contain\":\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio\n };\n } else {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height\n };\n }\n case \"cover\":\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height\n };\n } else {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio\n };\n }\n case \"fill\":\n return {\n width: containerSize.width,\n height: containerSize.height\n };\n default:\n return {\n width: elementSize.width,\n height: elementSize.height\n };\n }\n};\n\nconst blobUrlToFile = async (blobUrl, fileName) => {\n const response = await fetch(blobUrl);\n const blob = await response.blob();\n return new File([blob], fileName, { type: blob.type });\n};\nconst loadFile = (accept) => {\n return new Promise((resolve, reject) => {\n try {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.accept = accept;\n input.style.display = \"none\";\n document.body.appendChild(input);\n const cleanup = () => {\n input.value = \"\";\n document.body.removeChild(input);\n };\n input.onchange = () => {\n const file = input.files && input.files[0];\n cleanup();\n if (!file) {\n reject(new Error(\"No file selected\"));\n return;\n }\n resolve(file);\n };\n input.click();\n } catch (error) {\n reject(error);\n }\n });\n};\nconst saveAsFile = (content, type, name) => {\n const blob = typeof content === \"string\" ? new Blob([content], { type }) : content;\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = name;\n a.click();\n URL.revokeObjectURL(url);\n};\nconst downloadFile = async (url, filename) => {\n try {\n const response = await fetch(url);\n const blob = await response.blob();\n const downloadUrl = window.URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = downloadUrl;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n window.URL.revokeObjectURL(downloadUrl);\n } catch (error) {\n console.error(\"Error downloading file:\", error);\n throw error;\n }\n};\n\nconst detectMediaTypeFromUrl = async (url) => {\n try {\n const response = await fetch(url, { method: \"HEAD\" });\n const contentType = response.headers.get(\"Content-Type\");\n if (!contentType) return null;\n if (contentType.startsWith(\"image/\")) return \"image\";\n if (contentType.startsWith(\"video/\")) return \"video\";\n if (contentType.startsWith(\"audio/\")) return \"audio\";\n return null;\n } catch (error) {\n console.error(\"Fetch failed:\", error);\n return null;\n }\n};\n\nexport { blobUrlToFile, detectMediaTypeFromUrl, downloadFile, extractAudio, getAudioDuration, getImageDimensions, getObjectFitSize, getScaledDimensions, getThumbnail, getVideoMeta, hasAudio, limit, loadFile, saveAsFile, stitchAudio };\n//# sourceMappingURL=index.mjs.map\n","import {\n Canvas as FabricCanvas,\n FabricText,\n Textbox,\n Group,\n FabricImage,\n Rect,\n Circle,\n Shadow,\n} from \"fabric\";\nimport { convertToCanvasPosition } from \"../helpers/canvas.util\";\nimport {\n CanvasElement,\n CanvasMetadata,\n CaptionProps,\n FrameEffect,\n} from \"../types\";\nimport {\n DEFAULT_CAPTION_PROPS,\n DEFAULT_TEXT_PROPS,\n} from \"../helpers/constants\";\nimport { disabledControl, rotateControl } from \"./element-controls\";\nimport { getObjectFitSize, getThumbnail } from \"@twick/media-utils\";\n\n/**\n * Add a text element to the canvas.\n * Creates and configures a Fabric.js Textbox object with specified properties\n * including position, styling, interactive controls, and text wrapping support.\n *\n * @param element - The canvas element configuration\n * @param index - The z-index of the element\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata about the canvas including scale and dimensions\n * @returns The configured Fabric.js Textbox object with text wrapping enabled\n *\n * @example\n * ```js\n * const textElement = addTextElement({\n * element: { id: \"text1\", props: { text: \"Hello\", x: 100, y: 100, width: 200 } },\n * index: 1,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addTextElement = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n\n let width = element.props?.width ? element.props.width * canvasMetadata.scaleX : canvasMetadata.width;\n if (element.props?.maxWidth) {\n width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);\n }\n const text = new Textbox(element.props?.text || element.t || \"\", {\n left: x,\n top: y,\n originX: \"center\",\n originY: \"center\",\n angle: element.props?.rotation || 0,\n fontSize: Math.floor(\n (element.props?.fontSize || DEFAULT_TEXT_PROPS.size) *\n canvasMetadata.scaleX\n ),\n fontFamily: element.props?.fontFamily || DEFAULT_TEXT_PROPS.family,\n fontStyle: element.props?.fontStyle || \"normal\",\n fontWeight: element.props?.fontWeight || \"normal\",\n fill: element.props?.fill || DEFAULT_TEXT_PROPS.fill,\n opacity: element.props?.opacity ?? 1,\n width: width,\n splitByGrapheme: false,\n textAlign: element.props?.textAlign || \"center\",\n stroke: element.props?.stroke || DEFAULT_TEXT_PROPS.stroke,\n strokeWidth: element.props?.lineWidth || DEFAULT_TEXT_PROPS.lineWidth,\n shadow: element.props?.shadowColor\n ? new Shadow({\n offsetX:\n element.props?.shadowOffset?.length &&\n element.props?.shadowOffset?.length > 1\n ? element.props.shadowOffset[0] / 2\n : 1,\n offsetY:\n element.props?.shadowOffset?.length &&\n element.props?.shadowOffset.length > 1\n ? element.props.shadowOffset[1] / 2\n : 1,\n blur: (element.props?.shadowBlur || 2) / 2,\n color: element.props?.shadowColor,\n })\n : undefined,\n });\n\n // Assign metadata and custom controls\n text.set(\"id\", element.id);\n text.set(\"zIndex\", index);\n\n // Disable unwanted control points\n text.controls.mt = disabledControl;\n text.controls.mb = disabledControl;\n text.controls.ml = disabledControl;\n text.controls.mr = disabledControl;\n text.controls.bl = disabledControl;\n text.controls.br = disabledControl;\n text.controls.tl = disabledControl;\n text.controls.tr = disabledControl;\n text.controls.mtr = rotateControl;\n\n canvas.add(text);\n return text;\n};\n\n/**\n * Sets image properties for a Fabric.js image object.\n * Configures position, size, and metadata for image elements\n * on the canvas with proper scaling and positioning.\n *\n * @param img - The Fabric.js image object to configure\n * @param element - The canvas element configuration\n * @param index - The z-index of the element\n * @param canvasMetadata - Metadata about the canvas including scale and dimensions\n *\n * @example\n * ```js\n * setImageProps({\n * img: fabricImage,\n * element: { id: \"img1\", props: { width: 200, height: 150, x: 50, y: 50 } },\n * index: 2,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nconst setImageProps = ({\n img,\n element,\n index,\n canvasMetadata,\n}: {\n img: FabricImage;\n element: CanvasElement;\n index: number;\n canvasMetadata: CanvasMetadata;\n}) => {\n const width =\n (element.props?.width || 0) * canvasMetadata.scaleX || canvasMetadata.width;\n const height =\n (element.props?.height || 0) * canvasMetadata.scaleY ||\n canvasMetadata.height;\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n console.log(width, height, x, y);\n img.set(\"id\", element.id);\n img.set(\"zIndex\", index);\n img.set(\"width\", width);\n img.set(\"height\", height);\n img.set(\"left\", x);\n img.set(\"top\", y);\n img.set(\"opacity\", element.props?.opacity ?? 1);\n img.set(\"selectable\", true);\n img.set(\"hasControls\", true);\n img.set(\"touchAction\", \"all\");\n};\n\n/**\n * Add a caption element to the canvas based on provided props.\n * Creates a text element with caption-specific styling including\n * shadows, positioning, and font properties.\n *\n * @param element - The canvas element configuration\n * @param index - The z-index of the element\n * @param canvas - The Fabric.js canvas instance\n * @param captionProps - Default and user-defined caption properties\n * @param canvasMetadata - Metadata about the canvas including scale and dimensions\n * @returns The configured Fabric.js caption object\n *\n * @example\n * ```js\n * const captionElement = addCaptionElement({\n * element: { id: \"caption1\", props: { text: \"Caption\", pos: { x: 100, y: 100 } } },\n * index: 3,\n * canvas: fabricCanvas,\n * captionProps: { font: { size: 24, family: \"Arial\" } },\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addCaptionElement = ({\n element,\n index,\n canvas,\n captionProps,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n captionProps: CaptionProps;\n canvasMetadata: CanvasMetadata;\n}) => {\n const { x, y } = convertToCanvasPosition(\n (captionProps?.applyToAll ? captionProps?.x : element.props?.x) ?? 0,\n (captionProps?.applyToAll ? captionProps?.y : element.props?.y) ?? 0,\n canvasMetadata\n );\n\n const caption = new FabricText(element.props?.text || element.t || \"\", {\n left: x,\n top: y,\n originX: \"center\",\n originY: \"center\",\n angle: element.props?.rotation || 0,\n fontSize: Math.round(\n ((captionProps?.applyToAll\n ? captionProps?.font?.size\n : element.props?.font?.size ?? captionProps?.font?.size) ??\n DEFAULT_CAPTION_PROPS.size) * canvasMetadata.scaleX\n ),\n fontFamily:\n (captionProps?.applyToAll\n ? captionProps?.font?.family\n : element.props?.font?.family ?? captionProps?.font?.family) ??\n DEFAULT_CAPTION_PROPS.family,\n fill:\n (captionProps?.applyToAll\n ? captionProps.color?.text\n : element.props?.fill ?? captionProps.color?.text) ??\n DEFAULT_CAPTION_PROPS.fill,\n fontWeight: (captionProps?.applyToAll\n ? captionProps?.font?.weight\n : element.props?.fontWeight ?? captionProps?.font?.weight) ??\n DEFAULT_CAPTION_PROPS.fontWeight,\n stroke: (captionProps?.applyToAll\n ? captionProps?.stroke\n : element.props?.stroke ?? captionProps?.stroke) ??\n DEFAULT_CAPTION_PROPS.stroke,\n opacity: (captionProps?.applyToAll\n ? captionProps?.opacity\n : element.props?.opacity ?? captionProps?.opacity) ?? 1,\n shadow: new Shadow({\n offsetX:\n (captionProps?.applyToAll\n ? captionProps?.shadowOffset?.[0]\n : element.props?.shadowOffset?.[0] ?? captionProps?.shadowOffset?.[0]) ?? \n DEFAULT_CAPTION_PROPS.shadowOffset?.[0],\n offsetY:\n (captionProps?.applyToAll\n ? captionProps?.shadowOffset?.[1]\n : element.props?.shadowOffset?.[1] ?? captionProps?.shadowOffset?.[1]) ??\n DEFAULT_CAPTION_PROPS.shadowOffset?.[1],\n blur: (captionProps?.applyToAll\n ? captionProps?.shadowBlur\n : element.props?.shadowBlur ?? captionProps?.shadowBlur) ?? DEFAULT_CAPTION_PROPS.shadowBlur,\n color: (captionProps?.applyToAll\n ? captionProps?.shadowColor\n : element.props?.shadowColor ?? captionProps?.shadowColor) ?? DEFAULT_CAPTION_PROPS.shadowColor,\n }),\n strokeWidth: (captionProps?.applyToAll \n ? captionProps?.lineWidth\n : element.props?.lineWidth ?? captionProps?.lineWidth) ?? DEFAULT_CAPTION_PROPS.lineWidth,\n });\n\n // Assign metadata and custom controls\n caption.set(\"id\", element.id);\n caption.set(\"zIndex\", index);\n\n // Disable unwanted control points\n caption.controls.mt = disabledControl;\n caption.controls.mb = disabledControl;\n caption.controls.ml = disabledControl;\n caption.controls.mr = disabledControl;\n caption.controls.bl = disabledControl;\n caption.controls.br = disabledControl;\n caption.controls.tl = disabledControl;\n caption.controls.tr = disabledControl;\n caption.controls.mtr = disabledControl;\n\n canvas.add(caption);\n return caption;\n};\n\n/**\n * Add a video frame as element into a Fabric.js image object and optionally groups it with a frame.\n * Creates a video element by extracting a frame at the specified time and applying\n * optional frame effects for enhanced visual presentation.\n *\n * @param element - The video element containing properties like source and frame information\n * @param index - The z-index for ordering the element on the canvas\n * @param canvas - The Fabric.js canvas instance\n * @param snapTime - The time to snap the video frame with respect to full video duration\n * @param canvasMetadata - Metadata of the canvas, including dimensions and scale factors\n * @param currentFrameEffect - Optional frame effect to apply to the image\n * @returns A Fabric.js image object or a group with an image and frame\n *\n * @example\n * ```js\n * const videoElement = await addVideoElement({\n * element: {\n * id: \"video1\",\n * props: { src: \"video.mp4\", x: 100, y: 100 }\n * },\n * index: 2,\n * canvas: fabricCanvas,\n * snapTime: 5.0,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 },\n * currentFrameEffect: { shape: \"circle\", radius: 50 }\n * });\n * ```\n */\nexport const addVideoElement = async ({\n element,\n index,\n canvas,\n snapTime,\n canvasMetadata,\n currentFrameEffect,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n snapTime: number;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n}) => {\n try {\n const thumbnailUrl = await getThumbnail(\n element?.props?.src || \"\",\n snapTime\n );\n if (!thumbnailUrl) {\n console.error(\"Failed to get thumbnail\");\n return;\n }\n\n return addImageElement({\n imageUrl: thumbnailUrl,\n element,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n });\n } catch (error) {\n console.error(\"Error loading image:\", error);\n }\n};\n\n/**\n * Add an image element to the canvas and optionally group it with a frame.\n * Loads an image from URL and creates a Fabric.js image object with proper\n * positioning, scaling, and optional frame effects.\n *\n * @param imageUrl - Optional URL of the image to be added to the canvas\n * @param element - The image element containing properties like source and frame information\n * @param index - The z-index for ordering the element on the canvas\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata of the canvas including dimensions and scale factors\n * @param currentFrameEffect - Optional frame effect to apply to the image\n * @returns A Fabric.js image object or a group with an image and frame\n *\n * @example\n * ```js\n * const imageElement = await addImageElement({\n * imageUrl: \"https://example.com/image.jpg\",\n * element: { id: \"img1\", props: { src: \"image.jpg\", width: 200, height: 150 } },\n * index: 4,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addImageElement = async ({\n imageUrl,\n element,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n}: {\n imageUrl?: string;\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n}) => {\n try {\n // Load the image from the provided source URL\n const img = await FabricImage.fromURL(imageUrl || element.props.src || \"\");\n img.set({\n originX: \"center\",\n originY: \"center\",\n lockMovementX: false,\n lockMovementY: false,\n lockUniScaling: true,\n hasControls: false,\n selectable: false,\n });\n\n // Return the group if a frame is defined, otherwise return the image\n if (element.frame) {\n return addMediaGroup({\n element,\n img,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n });\n } else {\n setImageProps({ img, element, index, canvasMetadata });\n canvas.add(img);\n return img;\n }\n } catch (error) {\n console.error(\"Error loading image:\", error);\n }\n};\n\n/**\n * Add a Fabric.js group combining an image and its associated frame.\n * Applies styling, positioning, and scaling based on the given properties\n * and creates a grouped element for complex visual effects.\n *\n * @param element - The image element containing properties like frame, position, and styling\n * @param img - The Fabric.js image object to be included in the group\n * @param index - The z-index for ordering the group on the canvas\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata of the canvas including dimensions and scale factors\n * @param currentFrameEffect - Optional current frame effect to override default frame properties\n * @returns A Fabric.js group containing the image and frame with configured properties\n *\n * @example\n * ```js\n * const mediaGroup = addMediaGroup({\n * element: { id: \"group1\", frame: { size: [200, 150], x: 100, y: 100 } },\n * img: fabricImage,\n * index: 5,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nconst addMediaGroup = ({\n element,\n img,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n}: {\n element: CanvasElement;\n img: FabricImage;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n}) => {\n let frameSize;\n let angle;\n let framePosition;\n let frameRadius = 0;\n if (currentFrameEffect) {\n frameSize = {\n width:\n (currentFrameEffect.props.frameSize?.[0] || 0) *\n canvasMetadata.scaleX || canvasMetadata.width,\n height:\n (currentFrameEffect.props.frameSize?.[1] || 0) *\n canvasMetadata.scaleY || canvasMetadata.height,\n };\n angle = currentFrameEffect.props.rotation || 0;\n framePosition = currentFrameEffect.props.framePosition;\n if (currentFrameEffect.props.shape === \"circle\") {\n frameRadius = frameSize.width / 2;\n } else {\n frameRadius = currentFrameEffect?.props?.radius || 0;\n }\n } else {\n frameRadius = element?.frame?.radius || 0;\n frameSize = {\n width:\n (element?.frame?.size?.[0] || 0) * canvasMetadata.scaleX ||\n canvasMetadata.width,\n height:\n (element?.frame?.size?.[1] || 0) * canvasMetadata.scaleY ||\n canvasMetadata.height,\n };\n angle = element?.frame?.rotation || 0;\n framePosition = {\n x: element?.frame?.x || 0,\n y: element?.frame?.y || 0,\n };\n }\n\n const newSize = getObjectFitSize(\n element.objectFit,\n { width: img.width!, height: img.height! },\n frameSize\n );\n\n const frameRect = new Rect({\n originX: \"center\",\n originY: \"center\",\n lockMovementX: false,\n lockMovementY: false,\n lockUniScaling: true,\n hasControls: false,\n selectable: false,\n width: frameSize.width,\n height: frameSize.height,\n stroke: element?.frame?.stroke || \"#ffffff\",\n strokeWidth: element?.frame?.lineWidth || 0,\n hasRotatingPoint: true,\n rx: frameRadius || 0,\n ry: frameRadius || 0,\n });\n\n img.set({\n lockUniScaling: true,\n originX: \"center\",\n originY: \"center\",\n scaleX: newSize.width / img.width,\n scaleY: newSize.height / img.height,\n opacity: element.props?.opacity ?? 1,\n });\n\n const { x, y } = convertToCanvasPosition(\n framePosition?.x || 0,\n framePosition?.y || 0,\n canvasMetadata\n );\n\n const groupProps = {\n left: x,\n top: y,\n width: frameSize.width,\n height: frameSize.height,\n angle: angle,\n };\n\n // Customize the control points for the group\n // Change only the top control to a different style, keep others as circles\n\n const group = new Group([frameRect, img], {\n ...groupProps,\n originX: \"center\",\n originY: \"center\",\n angle: groupProps.angle,\n selectable: true,\n hasControls: true,\n hasBorders: true,\n clipPath: frameRect,\n });\n\n group.controls.mt = disabledControl;\n group.controls.mb = disabledControl;\n group.controls.ml = disabledControl;\n group.controls.mr = disabledControl;\n group.controls.mtr = rotateControl;\n\n group.set(\"id\", element.id);\n group.set(\"zIndex\", index);\n canvas.add(group);\n return group;\n};\n\n/**\n * Add a rectangular element to the canvas.\n * Creates a Fabric.js rectangle with specified properties including\n * position, size, styling, and interactive controls.\n *\n * @param element - The canvas element containing properties for the rectangle\n * @param index - The zIndex value used to determine the rendering order\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @returns A Fabric.js Rect object configured with the specified properties\n *\n * @example\n * ```js\n * const rectElement = addRectElement({\n * element: { id: \"rect1\", props: { width: 100, height: 50, x: 200, y: 150 } },\n * index: 6,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addRectElement = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n // Convert element's position to canvas coordinates\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n\n // Create a new rectangular Fabric.js object\n const rect = new Rect({\n left: x, // X-coordinate on the canvas\n top: y, // Y-coordinate on the canvas\n originX: \"center\", // Center the rectangle based on its position\n originY: \"center\", // Center the rectangle based on its position\n angle: element.props?.rotation || 0, // Rotation angle\n rx: (element.props?.radius || 0) * canvasMetadata.scaleX, // Horizontal radius for rounded corners\n ry: (element.props?.radius || 0) * canvasMetadata.scaleY, // Vertical radius for rounded corners\n stroke: element.props?.stroke || \"#000000\", // Stroke color\n strokeWidth: (element.props?.lineWidth || 0) * canvasMetadata.scaleX, // Scaled stroke width\n fill: element.props?.fill || \"#000000\", // Fill color\n opacity: element.props?.opacity || 1, // Opacity level\n width: (element.props?.width || 0) * canvasMetadata.scaleX, // Scaled width\n height: (element.props?.height || 0) * canvasMetadata.scaleY, // Scaled height\n });\n\n // Set custom properties for the rectangle\n rect.set(\"id\", element.id); // Unique identifier for the rectangle\n rect.set(\"zIndex\", index); // zIndex determines rendering order\n\n // Set custom control for rotation\n rect.controls.mtr = rotateControl;\n\n canvas.add(rect);\n return rect;\n};\n\nexport const addCircleElement = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n // Convert element's position to canvas coordinates\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n\n const circle = new Circle({\n left: x, // X-coordinate on the canvas\n top: y, // Y-coordinate on the canvas\n radius: (element.props?.radius || 0) * canvasMetadata.scaleX,\n fill: element.props?.fill || \"#000000\",\n stroke: element.props?.stroke || \"#000000\",\n strokeWidth: (element.props?.lineWidth || 0) * canvasMetadata.scaleX,\n originX: \"center\",\n originY: \"center\",\n });\n\n // Set custom control for rotation\n circle.controls.mt = disabledControl;\n circle.controls.mb = disabledControl;\n circle.controls.ml = disabledControl;\n circle.controls.mr = disabledControl;\n circle.controls.mtr = disabledControl;\n\n circle.set(\"id\", element.id);\n circle.set(\"zIndex\", index);\n canvas.add(circle);\n return circle;\n};\n\n/**\n * Add a background color to the canvas.\n * Creates a full-canvas rectangle with the specified background color\n * that serves as the base layer for other elements.\n *\n * @param element - The canvas element containing properties for the background\n * @param index - The zIndex value used to determine the rendering order\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @returns A Fabric.js Rect object configured with the specified properties\n *\n * @example\n * ```js\n * const bgElement = addBackgroundColor({\n * element: { id: \"bg1\", backgoundColor: \"#ffffff\" },\n * index: 0,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addBackgroundColor = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n const bgRect = new Rect({\n width: canvasMetadata.width,\n height: canvasMetadata.height,\n left: canvasMetadata.width / 2,\n top: canvasMetadata.height / 2,\n fill: element.backgoundColor ?? \"#000000\",\n originX: \"center\",\n originY: \"center\",\n hasControls: false,\n hasBorders: false,\n selectable: false,\n });\n\n bgRect.controls.mt = disabledControl;\n bgRect.controls.mb = disabledControl;\n bgRect.controls.ml = disabledControl;\n bgRect.controls.mr = disabledControl;\n bgRect.controls.bl = disabledControl;\n bgRect.controls.br = disabledControl;\n bgRect.controls.tl = disabledControl;\n bgRect.controls.tr = disabledControl;\n bgRect.controls.mtr = disabledControl;\n bgRect.set(\"zIndex\", index - 0.5);\n\n canvas.add(bgRect);\n return bgRect;\n};\n","import { useRef, useState } from \"react\";\nimport { Canvas as FabricCanvas, FabricObject, Textbox } from \"fabric\";\nimport { Dimensions } from \"@twick/media-utils\";\nimport {\n CanvasMetadata,\n CanvasProps,\n CanvasElement,\n CaptionProps,\n} from \"../types\";\nimport {\n clearCanvas,\n convertToVideoPosition,\n createCanvas,\n getCanvasContext,\n getCurrentFrameEffect,\n reorderElementsByZIndex,\n} from \"../helpers/canvas.util\";\nimport { CANVAS_OPERATIONS, ELEMENT_TYPES } from \"../helpers/constants\";\nimport {\n addImageElement,\n addVideoElement,\n addRectElement,\n addTextElement,\n addCaptionElement,\n addBackgroundColor,\n addCircleElement,\n} from \"../components/elements\";\n\n/**\n * Custom hook to manage a Fabric.js canvas and associated operations.\n * Provides functionality for canvas initialization, element management,\n * and event handling for interactive canvas operations.\n *\n * @param onCanvasReady - Callback executed when the canvas is ready\n * @param onCanvasOperation - Callback executed on canvas operations such as item selection or updates\n * @returns Object containing canvas-related functions and state\n *\n * @example\n * ```js\n * const { twickCanvas, buildCanvas, addElementToCanvas } = useTwickCanvas({\n * onCanvasReady: (canvas) => console.log('Canvas ready:', canvas),\n * onCanvasOperation: (operation, data) => console.log('Operation:', operation, data)\n * });\n * ```\n */\nexport const useTwickCanvas = ({\n onCanvasReady,\n onCanvasOperation,\n}: {\n onCanvasReady?: (canvas: FabricCanvas) => void;\n onCanvasOperation?: (operation: string, data: any) => void;\n}) => {\n const [twickCanvas, setTwickCanvas] = useState<FabricCanvas | null>(null); // Canvas instance\n const elementMap = useRef<Record<string, any>>({}); // Maps element IDs to their data\n const elementFrameMap = useRef<Record<string, any>>({}); // Maps element IDs to their frame effects\n const twickCanvasRef = useRef<FabricCanvas | null>(null);\n const videoSizeRef = useRef<Dimensions>({ width: 1, height: 1 }); // Stores the video dimensions\n const canvasResolutionRef = useRef<Dimensions>({ width: 1, height: 1 }); // Stores the canvas dimensions\n const captionPropsRef = useRef<CaptionProps | null>(null);\n const canvasMetadataRef = useRef<CanvasMetadata>({\n width: 0,\n height: 0,\n aspectRatio: 0,\n scaleX: 1,\n scaleY: 1,\n }); // Metadata for the canvas\n\n /**\n * Updates canvas metadata when the video size changes.\n * Recalculates scale factors based on the new video dimensions\n * to maintain proper coordinate mapping between canvas and video.\n *\n * @param videoSize - New video dimensions\n *\n * @example\n * ```js\n * onVideoSizeChange({ width: 1920, height: 1080 });\n * ```\n */\n const onVideoSizeChange = (videoSize: Dimensions) => {\n if (videoSize) {\n videoSizeRef.current = videoSize;\n canvasMetadataRef.current.scaleX =\n canvasMetadataRef.current.width / videoSize.width;\n canvasMetadataRef.current.scaleY =\n canvasMetadataRef.current.height / videoSize.height;\n }\n };\n\n /**\n * Initializes the Fabric.js canvas with the provided configuration.\n * Creates a new canvas instance with the specified properties and sets up\n * event listeners for interactive operations.\n *\n * @param props - Canvas configuration properties including size, colors, and behavior settings\n *\n * @example\n * ```js\n * buildCanvas({\n * videoSize: { width: 1920, height: 1080 },\n * canvasSize: { width: 800, height: 600 },\n * canvasRef: canvasElement,\n * backgroundColor: \"#000000\",\n * selectionBorderColor: \"#2563eb\"\n * });\n * ```\n */\n const buildCanvas = ({\n videoSize,\n canvasSize,\n canvasRef,\n backgroundColor = \"#000000\",\n selectionBorderColor = \"#2563eb\",\n selectionLineWidth = 2,\n uniScaleTransform = true,\n enableRetinaScaling = true,\n touchZoomThreshold = 10,\n forceBuild = false,\n }: CanvasProps & { forceBuild?: boolean }) => {\n if (!canvasRef) return;\n\n if (\n !forceBuild &&\n canvasResolutionRef.current.width === canvasSize.width &&\n canvasResolutionRef.current.height === canvasSize.height\n ) {\n return;\n }\n\n // Dispose of the old canvas if it exists\n if (twickCanvasRef.current) {\n console.log(\"Destroying twickCanvas\");\n twickCanvasRef.current.off(\"mouse:up\", handleMouseUp);\n twickCanvasRef.current.off(\"text:editing:exited\", onTextEdit);\n twickCanvasRef.current.dispose();\n }\n\n // Create a new canvas and update metadata\n const { canvas, canvasMetadata } = createCanvas({\n videoSize,\n canvasSize,\n canvasRef,\n backgroundColor,\n selectionBorderColor,\n selectionLineWidth,\n uniScaleTransform,\n enableRetinaScaling,\n touchZoomThreshold,\n });\n canvasMetadataRef.current = canvasMetadata;\n videoSizeRef.current = videoSize;\n // Attach event listeners\n canvas?.on(\"mouse:up\", handleMouseUp);\n canvas?.on(\"text:editing:exited\", onTextEdit);\n canvasResolutionRef.current = canvasSize;\n setTwickCanvas(canvas);\n twickCanvasRef.current = canvas;\n // Notify when canvas is ready\n if (onCanvasReady) {\n onCanvasReady(canvas);\n }\n };\n\n const onTextEdit = (event: any) => {\n if (event.target) {\n const object: FabricObject = event.target;\n const elementId = object.get(\"id\");\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n text:\n (object as Textbox).text ??\n elementMap.current[elementId].props.text,\n },\n };\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_UPDATED,\n elementMap.current[elementId]\n );\n }\n };\n\n /**\n * Handles mouse up events on the canvas.\n * Processes user interactions like dragging, scaling, and rotating elements,\n * updating element properties and triggering appropriate callbacks.\n *\n * @param event - Mouse event object containing interaction details\n */\n const handleMouseUp = (event: any) => {\n if (event.target) {\n const object: FabricObject = event.target;\n const elementId = object.get(\"id\");\n if (event.transform?.action === \"drag\") {\n const original = event.transform.original;\n if (object.left === original.left && object.top === original.top) {\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_SELECTED,\n elementMap.current[elementId]\n );\n return;\n }\n }\n switch (event.transform?.action) {\n case \"drag\":\n case \"scale\":\n case \"scaleX\":\n case \"scaleY\":\n case \"rotate\":\n const { x, y } = convertToVideoPosition(\n object.left,\n object.top,\n canvasMetadataRef.current,\n videoSizeRef.current\n );\n if (elementMap.current[elementId].type === \"caption\") {\n if (captionPropsRef.current?.applyToAll) {\n onCanvasOperation?.(CANVAS_OPERATIONS.CAPTION_PROPS_UPDATED, {\n element: elementMap.current[elementId],\n props: {\n ...captionPropsRef.current,\n x,\n y,\n },\n });\n } else {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n x,\n y,\n },\n };\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_UPDATED,\n elementMap.current[elementId]\n );\n }\n } else {\n if (object?.type === \"group\") {\n const currentFrameEffect = elementFrameMap.current[elementId];\n let updatedFrameSize;\n if (currentFrameEffect) {\n updatedFrameSize = [\n currentFrameEffect.props.frameSize[0] * object.scaleX,\n currentFrameEffect.props.frameSize[1] * object.scaleY,\n ];\n } else {\n updatedFrameSize = [\n elementMap.current[elementId].frame.size[0] * object.scaleX,\n elementMap.current[elementId].frame.size[1] * object.scaleY,\n ];\n }\n\n if (currentFrameEffect) {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n frameEffects: (\n elementMap.current[elementId].frameEffects || []\n ).map((frameEffect: any) =>\n frameEffect.id === currentFrameEffect?.id\n ? {\n ...frameEffect,\n props: {\n ...frameEffect.props,\n framePosition: {\n x,\n y,\n },\n frameSize: updatedFrameSize,\n },\n }\n : frameEffect\n ),\n };\n elementFrameMap.current[elementId] = {\n ...elementFrameMap.current[elementId],\n framePosition: {\n x,\n y,\n },\n frameSize: updatedFrameSize,\n };\n } else {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n frame: {\n ...elementMap.current[elementId].frame,\n rotation: object.angle,\n size: updatedFrameSize,\n x,\n y,\n },\n };\n }\n } else {\n if (object?.type === \"text\") {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n rotation: object.angle,\n x,\n y,\n },\n };\n } else if (object?.type === \"circle\") {\n const radius = Number(\n (\n elementMap.current[elementId].props.radius * object.scaleX\n ).toFixed(2)\n );\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n rotation: object.angle,\n radius: radius,\n height: radius * 2,\n width: radius * 2,\n x,\n y,\n },\n };\n } else {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n rotation: object.angle,\n width:\n elementMap.current[elementId].props.width * object.scaleX,\n height:\n elementMap.current[elementId].props.height *\n object.scaleY,\n x,\n y,\n },\n };\n }\n }\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_UPDATED,\n elementMap.current[elementId]\n );\n }\n break;\n }\n }\n };\n\n /**\n * Sets elements to the canvas.\n * Adds multiple elements to the canvas with optional cleanup and ordering.\n * Supports batch operations for efficient element management.\n *\n * @param options - Object containing elements, seek time, and additional options\n *\n * @example\n * ```js\n * await setCanvasElements({\n * elements: [element1, element2, element3],\n * seekTime: 5.0,\n * cleanAndAdd: true\n * });\n * ```\n */\n const setCanvasElements = async ({\n elements,\n seekTime = 0,\n captionProps,\n cleanAndAdd = false,\n }: {\n elements: CanvasElement[];\n seekTime?: number;\n captionProps?: any;\n cleanAndAdd?: boolean;\n }) => {\n if (!twickCanvas || !getCanvasContext(twickCanvas)) {\n console.warn(\"Canvas not properly initialized\");\n return;\n }\n\n try {\n if (cleanAndAdd && getCanvasContext(twickCanvas)) {\n // Store background color before clearing\n const backgroundColor = twickCanvas.backgroundColor;\n\n // Clear canvas before adding new elements\n clearCanvas(twickCanvas);\n\n // Restore background color\n if (backgroundColor) {\n twickCanvas.backgroundColor = backgroundColor;\n twickCanvas.renderAll();\n }\n }\n\n captionPropsRef.current = captionProps;\n await Promise.all(\n elements.map(async (element, index) => {\n try {\n if (!element) {\n console.warn(\"Element not found\");\n return;\n }\n await addElementToCanvas({\n element,\n index,\n reorder: false,\n seekTime,\n captionProps,\n });\n } catch (error) {\n console.error(`Error adding element ${element.id}:`, error);\n }\n })\n );\n reorderElementsByZIndex(twickCanvas);\n } catch (error) {\n console.error(\"Error in setCanvasElements:\", error);\n }\n };\n\n /**\n * Add element to the canvas.\n * Adds a single element to the canvas based on its type and properties.\n * Handles different element types (video, image, text, etc.) with appropriate rendering.\n *\n * @param options - Object containing element data, index, and rendering options\n *\n * @example\n * ```js\n * await addElementToCanvas({\n * element: videoElement,\n * index: 0,\n * reorder: true,\n * seekTime: 2.5\n * });\n * ```\n */\n const addElementToCanvas = async ({\n element,\n index,\n reorder = true,\n seekTime,\n captionProps,\n }: {\n element: CanvasElement;\n index: number;\n reorder: boolean;\n seekTime?: number;\n captionProps?: any;\n }) => {\n if (!twickCanvas) {\n console.warn(\"Canvas not initialized\");\n return;\n }\n // Add element based on type\n switch (element.type) {\n case ELEMENT_TYPES.VIDEO:\n const currentFrameEffect = getCurrentFrameEffect(\n element,\n seekTime || 0\n );\n elementFrameMap.current[element.id] = currentFrameEffect;\n const snapTime =\n ((seekTime || 0) - (element?.s || 0)) *\n (element?.props?.playbackRate || 1) +\n (element?.props?.time || 0);\n await addVideoElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n currentFrameEffect,\n snapTime,\n });\n if (element.timelineType === \"scene\") {\n await addBackgroundColor({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n }\n break;\n case ELEMENT_TYPES.IMAGE:\n await addImageElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n if (element.timelineType === \"scene\") {\n await addBackgroundColor({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n }\n break;\n case ELEMENT_TYPES.RECT:\n await addRectElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n case ELEMENT_TYPES.CIRCLE:\n await addCircleElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n case ELEMENT_TYPES.TEXT:\n await addTextElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n case ELEMENT_TYPES.CAPTION:\n await addCaptionElement({\n element,\n index,\n canvas: twickCanvas,\n captionProps,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n default:\n break;\n }\n elementMap.current[element.id] = element;\n if (reorder) {\n reorderElementsByZIndex(twickCanvas);\n }\n };\n\n return {\n twickCanvas,\n buildCanvas,\n onVideoSizeChange,\n addElementToCanvas,\n setCanvasElements,\n };\n};\n"],"names":["FabricCanvas","Control","controlsUtils","Textbox","Shadow","FabricText","FabricImage","Rect","Group","Circle","useState","useRef"],"mappings":"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"}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/helpers/constants.ts","../src/helpers/browser.ts","../src/helpers/canvas.util.ts","../src/components/element-controls.tsx","../../media-utils/dist/index.mjs","../src/components/elements.tsx","../src/hooks/use-twick-canvas.ts"],"sourcesContent":["/**\n * Default text properties for canvas text elements.\n * Provides consistent styling defaults for text elements added to the canvas.\n * \n * @example\n * ```js\n * import { DEFAULT_TEXT_PROPS } from '@twick/canvas';\n * \n * const textElement = {\n * ...DEFAULT_TEXT_PROPS,\n * text: \"Hello World\",\n * x: 100,\n * y: 100\n * };\n * ```\n */\nexport const DEFAULT_TEXT_PROPS = {\n /** Font family for text elements */\n family: \"Poppins\",\n /** Font size in pixels */\n size: 48,\n /** Text fill color */\n fill: \"#FFFFFF\",\n /** Text stroke color */\n stroke: \"#000000\",\n /** Stroke line width */\n lineWidth: 0,\n }\n \n/**\n * Default caption properties for canvas caption elements.\n * Provides enhanced styling defaults specifically for caption elements\n * with background colors, shadows, and highlight options.\n * \n * @example\n * ```js\n * import { DEFAULT_CAPTION_PROPS } from '@twick/canvas';\n * \n * const captionElement = {\n * ...DEFAULT_CAPTION_PROPS,\n * text: \"Video Caption\",\n * x: 50,\n * y: 50\n * };\n * ```\n */\nexport const DEFAULT_CAPTION_PROPS = {\n /** Font family for caption elements */\n family: \"Poppins\",\n /** Font size in pixels */\n size: 48,\n /** Text fill color */\n fill: \"#FFFFFF\",\n /** Font weight */\n fontWeight: 600,\n /** Color configuration object */\n color: {\n /** Text color */\n text: \"#FFFFFF\",\n /** Background color */\n background: \"#000000\",\n /** Highlight color */\n highlight: \"#FFFFFF\",\n },\n /** Text stroke color */\n stroke: \"#000000\",\n /** Stroke line width */\n lineWidth: 0.2,\n /** Shadow color */\n shadowColor: \"#000000\",\n /** Shadow blur radius */\n shadowBlur: 2,\n /** Shadow offset [x, y] */\n shadowOffset: [0, 0],\n}\n\n/**\n * Canvas operation constants for tracking canvas state changes.\n * Defines the different types of operations that can occur on canvas elements.\n * \n * @example\n * ```js\n * import { CANVAS_OPERATIONS } from '@twick/canvas';\n * \n * function handleCanvasOperation(operation) {\n * switch (operation) {\n * case CANVAS_OPERATIONS.ITEM_ADDED:\n * console.log('New item added to canvas');\n * break;\n * case CANVAS_OPERATIONS.ITEM_UPDATED:\n * console.log('Item updated on canvas');\n * break;\n * }\n * }\n * ```\n */\nexport const CANVAS_OPERATIONS = {\n /** An item has been selected on the canvas */\n ITEM_SELECTED: \"ITEM_SELECTED\",\n /** An item has been updated/modified on the canvas */\n ITEM_UPDATED: \"ITEM_UPDATED\",\n /** An item has been deleted from the canvas */\n ITEM_DELETED: \"ITEM_DELETED\",\n /** A new item has been added to the canvas */\n ITEM_ADDED: \"ITEM_ADDED\",\n /** Items have been grouped together */\n ITEM_GROUPED: \"ITEM_GROUPED\",\n /** Items have been ungrouped */\n ITEM_UNGROUPED: \"ITEM_UNGROUPED\",\n /** Caption properties have been updated */\n CAPTION_PROPS_UPDATED: \"CAPTION_PROPS_UPDATED\"\n}\n\n/**\n * Element type constants for canvas elements.\n * Defines the different types of elements that can be added to the canvas.\n * \n * @example\n * ```js\n * import { ELEMENT_TYPES } from '@twick/canvas';\n * \n * if (element.type === ELEMENT_TYPES.TEXT) {\n * // Handle text element\n * } else if (element.type === ELEMENT_TYPES.IMAGE) {\n * // Handle image element\n * }\n * ```\n */\nexport const ELEMENT_TYPES = {\n /** Text element type */\n TEXT: \"text\",\n /** Caption element type */\n CAPTION: \"caption\",\n /** Image element type */\n IMAGE: \"image\",\n /** Video element type */\n VIDEO: \"video\",\n /** Rectangle element type */\n RECT: \"rect\",\n /** Circle element type */\n CIRCLE: \"circle\",\n /** Background color element type */\n BACKGROUND_COLOR: \"backgroundColor\",\n}\n","/**\n * Checks if the code is running in a browser environment.\n * Returns true if window object is available, false otherwise.\n * \n * @example\n * ```js\n * import { isBrowser } from '@twick/canvas';\n * \n * if (isBrowser) {\n * // Browser-specific code\n * console.log('Running in browser');\n * }\n * ```\n */\nexport const isBrowser = typeof window !== 'undefined';\n\n/**\n * Checks if the Canvas API is supported in the current environment.\n * Returns true if HTMLCanvasElement is available, false otherwise.\n * \n * @example\n * ```js\n * import { isCanvasSupported } from '@twick/canvas';\n * \n * if (isCanvasSupported) {\n * // Canvas operations are safe\n * createCanvas();\n * }\n * ```\n */\nexport const isCanvasSupported = isBrowser && !!window.HTMLCanvasElement;\n\n/**\n * Checks if the Video API is supported in the current environment.\n * Returns true if HTMLVideoElement is available, false otherwise.\n * \n * @example\n * ```js\n * import { isVideoSupported } from '@twick/canvas';\n * \n * if (isVideoSupported) {\n * // Video operations are safe\n * addVideoElement();\n * }\n * ```\n */\nexport const isVideoSupported = isBrowser && !!window.HTMLVideoElement;\n\n/**\n * Asserts that the code is running in a browser environment.\n * Throws an error if the code is executed in a non-browser context\n * such as Node.js server-side rendering.\n * \n * @throws Error when not running in a browser environment\n * \n * @example\n * ```js\n * assertBrowser();\n * // Code continues if in browser, throws error if not\n * ```\n */\nexport function assertBrowser() {\n if (!isBrowser) {\n throw new Error('This code can only run in a browser environment');\n }\n}\n\n/**\n * Asserts that the Canvas API is supported in the current environment.\n * Checks for HTMLCanvasElement support and throws an error if canvas\n * functionality is not available.\n * \n * @throws Error when Canvas API is not supported\n * \n * @example\n * ```js\n * assertCanvasSupport();\n * // Code continues if canvas is supported, throws error if not\n * ```\n */\nexport function assertCanvasSupport() {\n if (!isCanvasSupported) {\n throw new Error('Canvas is not supported in this environment');\n }\n}\n\n/**\n * Asserts that the Video API is supported in the current environment.\n * Checks for HTMLVideoElement support and throws an error if video\n * functionality is not available.\n * \n * @throws Error when Video API is not supported\n * \n * @example\n * ```js\n * assertVideoSupport();\n * // Code continues if video is supported, throws error if not\n * ```\n */\nexport function assertVideoSupport() {\n if (!isVideoSupported) {\n throw new Error('Video is not supported in this environment');\n }\n} ","import { Canvas as FabricCanvas } from \"fabric\";\nimport { CanvasMetadata, CanvasProps } from \"../types\";\nimport { Dimensions, Position } from \"@twick/media-utils\";\nimport { assertBrowser, assertCanvasSupport } from \"./browser\";\n\n/**\n * Creates and initializes a Fabric.js canvas with specified configurations.\n * Sets up a canvas with proper scaling, background, and interaction settings\n * based on the provided video and canvas dimensions.\n *\n * @param videoSize - The dimensions of the video\n * @param canvasSize - The dimensions of the canvas\n * @param canvasContainer - The HTML container for the canvas\n * @param backgroundColor - Background color of the canvas\n * @param selectionBorderColor - Border color for selected objects\n * @param selectionLineWidth - Width of the selection border\n * @param uniScaleTransform - Ensures uniform scaling of objects\n * @param enableRetinaScaling - Enables retina scaling for higher DPI\n * @param touchZoomThreshold - Threshold for touch zoom interactions\n * @returns Object containing the initialized canvas and its metadata\n *\n * @example\n * ```js\n * const { canvas, canvasMetadata } = createCanvas({\n * videoSize: { width: 1920, height: 1080 },\n * canvasSize: { width: 800, height: 600 },\n * canvasRef: canvasElement,\n * backgroundColor: \"#000000\",\n * selectionBorderColor: \"#2563eb\"\n * });\n * ```\n */\nexport const createCanvas = ({\n videoSize,\n canvasSize,\n canvasRef,\n backgroundColor = \"#000000\",\n selectionBorderColor = \"#2563eb\",\n selectionLineWidth = 2,\n uniScaleTransform = true,\n enableRetinaScaling = true,\n touchZoomThreshold = 10,\n}: CanvasProps): { canvas: FabricCanvas; canvasMetadata: CanvasMetadata } => {\n assertBrowser();\n assertCanvasSupport();\n\n // Metadata for scaling and positioning on the canvas\n const canvasMetadata = {\n width: canvasSize.width,\n height: canvasSize.height,\n aspectRatio: canvasSize.width / canvasSize.height,\n scaleX: Number((canvasSize.width / videoSize.width).toFixed(2)) ,\n scaleY: Number((canvasSize.height / videoSize.height).toFixed(2)),\n };\n\n // Create and configure the Fabric.js canvas\n const canvas = new FabricCanvas(canvasRef, {\n backgroundColor,\n width: canvasSize.width,\n height: canvasSize.height,\n preserveObjectStacking: true,\n enableRetinaScaling,\n selectionBorderColor,\n selectionLineWidth,\n uniScaleTransform,\n touchZoomThreshold,\n renderOnAddRemove: false,\n stateful: false,\n selection: true,\n skipTargetFind: false,\n controlsAboveOverlay: true,\n });\n\n // Set dimensions and render canvas\n if (canvasRef) {\n canvas.setDimensions({\n width: canvasMetadata.width,\n height: canvasMetadata.height,\n });\n canvas.renderAll();\n }\n\n return {\n canvas,\n canvasMetadata,\n };\n};\n\n/**\n * Reorders elements on the canvas based on their zIndex property.\n * Sorts all canvas objects by their zIndex and re-adds them to maintain\n * proper layering order for visual elements.\n *\n * @param canvas - The Fabric.js canvas instance\n *\n * @example\n * ```js\n * reorderElementsByZIndex(canvas);\n * // Elements are now properly layered based on zIndex\n * ```\n */\nexport const reorderElementsByZIndex = (canvas: FabricCanvas) => {\n if (!canvas) return;\n const backgroundColor = canvas.backgroundColor;\n\n const objects = canvas.getObjects();\n console.log(\"objects\", objects);\n // Sort objects by zIndex and re-add to the canvas in order\n objects.sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0));\n\n canvas.clear();\n canvas.backgroundColor = backgroundColor;\n\n objects.forEach((obj) => canvas.add(obj));\n canvas.renderAll();\n};\n\n/**\n * Retrieves the context of a Fabric.js canvas.\n * \n * @param canvas - The Fabric.js canvas instance\n * @returns The context of the canvas\n */\nexport const getCanvasContext = (canvas: FabricCanvas | null | undefined) => {\n if (!canvas || !canvas.elements?.lower?.ctx) return;\n return canvas.elements?.lower?.ctx;\n};\n\n/**\n * Clears all elements from the canvas and re-renders it.\n * Removes all objects from the canvas while preserving the background\n * and triggers a re-render to update the display.\n *\n * @param canvas - The Fabric.js canvas instance\n *\n * @example\n * ```js\n * clearCanvas(canvas);\n * // Canvas is now empty and ready for new elements\n * ```\n */\nexport const clearCanvas = (canvas: FabricCanvas | null | undefined) => {\n try {\n if (!canvas || !getCanvasContext(canvas)) return;\n canvas.clear();\n canvas.renderAll();\n } catch (error) {\n console.warn(\"Error clearing canvas:\", error);\n }\n};\n\n/**\n * Converts a position from the video coordinate space to the canvas coordinate space.\n * Applies scaling and centering transformations to map video coordinates\n * to the corresponding canvas pixel positions.\n *\n * @param x - X-coordinate in video space\n * @param y - Y-coordinate in video space\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @returns Object containing the corresponding position in canvas space\n *\n * @example\n * ```js\n * const canvasPos = convertToCanvasPosition(100, 200, canvasMetadata);\n * // canvasPos = { x: 450, y: 500 }\n * ```\n */\nexport const convertToCanvasPosition = (\n x: number,\n y: number,\n canvasMetadata: CanvasMetadata\n): Position => {\n return {\n x: x * canvasMetadata.scaleX + canvasMetadata.width / 2,\n y: y * canvasMetadata.scaleY + canvasMetadata.height / 2,\n };\n};\n\n/**\n * Converts a position from the canvas coordinate space to the video coordinate space.\n * Applies inverse scaling and centering transformations to map canvas coordinates\n * back to the corresponding video coordinate positions.\n *\n * @param x - X-coordinate in canvas space\n * @param y - Y-coordinate in canvas space\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @param videoSize - Dimensions of the video\n * @returns Object containing the corresponding position in video space\n *\n * @example\n * ```js\n * const videoPos = convertToVideoPosition(450, 500, canvasMetadata, videoSize);\n * // videoPos = { x: 100, y: 200 }\n * ```\n */\nexport const convertToVideoPosition = (\n x: number,\n y: number,\n canvasMetadata: CanvasMetadata,\n videoSize: Dimensions\n): Position => {\n return {\n x: Number((x / canvasMetadata.scaleX - videoSize.width / 2).toFixed(2)),\n y: Number((y / canvasMetadata.scaleY - videoSize.height / 2).toFixed(2)),\n };\n};\n\n/**\n * Retrieves the current frame effect for a given seek time.\n * Searches through the item's frame effects to find the one that is active\n * at the specified seek time based on start and end time ranges.\n *\n * @param item - The item containing frame effects\n * @param seekTime - The current time to match against frame effects\n * @returns The current frame effect active at the given seek time, or undefined if none found\n *\n * @example\n * ```js\n * const currentEffect = getCurrentFrameEffect(videoElement, 5.5);\n * // Returns the frame effect active at 5.5 seconds, if any\n * ```\n */\nexport const getCurrentFrameEffect = (item: any, seekTime: number) => {\n let currentFrameEffect;\n for (let i = 0; i < item?.frameEffects?.length; i++) {\n if (\n item.frameEffects[i].s <= seekTime &&\n item.frameEffects[i].e >= seekTime\n ) {\n currentFrameEffect = item.frameEffects[i];\n break;\n }\n }\n return currentFrameEffect;\n};\n","import { Control, controlsUtils } from \"fabric\";\n\n/**\n * Disabled control for canvas elements.\n * Creates a control that appears disabled and doesn't perform any actions\n * when interacted with. Useful for showing non-interactive control points.\n * \n * @example\n * ```js\n * import { disabledControl } from '@twick/canvas';\n * \n * // Apply to a canvas object\n * object.setControlsVisibility({\n * mt: false, // Disable top control\n * mb: false, // Disable bottom control\n * ml: false, // Disable left control\n * mr: false, // Disable right control\n * bl: disabledControl, // Use disabled control for bottom-left\n * });\n * ```\n */\nexport const disabledControl = new Control({\n /** X position offset */\n x: 0,\n /** Y position offset */\n y: -0.5,\n /** Additional Y offset */\n offsetY: 0,\n /** Cursor style when hovering */\n cursorStyle: \"pointer\",\n /** Action handler that does nothing */\n actionHandler: () => {\n return true;\n },\n /** Name of the action */\n actionName: \"scale\",\n /** Render function for the control */\n render: function (ctx: CanvasRenderingContext2D,\n left: number,\n top: number) {\n const size = 0;\n ctx.save();\n ctx.translate(left, top);\n ctx.fillStyle = \"#red\";\n ctx.fillRect(-size / 2, -size / 2, size, size);\n ctx.restore();\n },\n });\n\n/**\n * Rotation control for canvas elements.\n * Creates a control that allows rotation of canvas objects with snapping\n * functionality for precise angle adjustments.\n * \n * @example\n * ```js\n * import { rotateControl } from '@twick/canvas';\n * \n * // Apply to a canvas object\n * object.setControlsVisibility({\n * mtr: rotateControl, // Use custom rotate control for top-right\n * });\n * \n * // Enable rotation\n * object.set({\n * hasRotatingPoint: true,\n * lockRotation: false\n * });\n * ```\n */\nexport const rotateControl = new Control({\n /** X position offset */\n x: 0,\n /** Y position offset */\n y: -0.5,\n /** Additional Y offset for positioning */\n offsetY: -25,\n /** Cursor style when hovering */\n cursorStyle: \"crosshair\",\n /** Action handler with rotation and snapping */\n actionHandler: controlsUtils.rotationWithSnapping,\n /** Name of the action */\n actionName: \"rotate\",\n /** Whether to show connection line */\n withConnection: true,\n });","const imageDimensionsCache = {};\nconst videoMetaCache = {};\nconst audioDurationCache = {};\n\nconst getAudioDuration = (audioSrc) => {\n if (audioDurationCache[audioSrc]) {\n return Promise.resolve(audioDurationCache[audioSrc]);\n }\n return new Promise((resolve, reject) => {\n const audio = document.createElement(\"audio\");\n audio.preload = \"metadata\";\n const isSafeUrl = /^(https?:|blob:|data:audio\\/)/i.test(audioSrc);\n if (!isSafeUrl) {\n throw new Error(\"Unsafe audio source URL\");\n }\n audio.src = audioSrc;\n audio.onloadedmetadata = () => {\n const duration = audio.duration;\n audioDurationCache[audioSrc] = duration;\n resolve(duration);\n };\n audio.onerror = () => {\n reject(new Error(\"Failed to load audio metadata\"));\n };\n });\n};\n\nconst concurrencyLimit = 5;\nlet activeCount = 0;\nconst queue = [];\nfunction runNext() {\n if (queue.length === 0 || activeCount >= concurrencyLimit) return;\n const next = queue.shift();\n if (next) {\n activeCount++;\n next();\n }\n}\nfunction limit(fn) {\n return new Promise((resolve, reject) => {\n const task = () => {\n fn().then(resolve).catch(reject).finally(() => {\n activeCount--;\n runNext();\n });\n };\n if (activeCount < concurrencyLimit) {\n activeCount++;\n task();\n } else {\n queue.push(task);\n }\n });\n}\n\nconst loadImageDimensions = (url) => {\n return new Promise((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(new Error(\"getImageDimensions() is only available in the browser.\"));\n return;\n }\n const img = new Image();\n img.onload = () => {\n resolve({ width: img.naturalWidth, height: img.naturalHeight });\n };\n img.onerror = reject;\n img.src = url;\n });\n};\nconst getImageDimensions = (url) => {\n if (imageDimensionsCache[url]) {\n return Promise.resolve(imageDimensionsCache[url]);\n }\n return limit(() => loadImageDimensions(url)).then((dimensions) => {\n imageDimensionsCache[url] = dimensions;\n return dimensions;\n });\n};\n\nconst getVideoMeta = (videoSrc) => {\n if (videoMetaCache[videoSrc]) {\n return Promise.resolve(videoMetaCache[videoSrc]);\n }\n return new Promise((resolve, reject) => {\n const video = document.createElement(\"video\");\n video.preload = \"metadata\";\n const isSafeUrl = /^(https?:|blob:|data:video\\/)/i.test(videoSrc);\n if (!isSafeUrl) {\n reject(new Error(\"Unsafe video source URL\"));\n return;\n }\n video.src = videoSrc;\n video.onloadedmetadata = () => {\n const meta = {\n width: video.videoWidth,\n height: video.videoHeight,\n duration: video.duration\n };\n videoMetaCache[videoSrc] = meta;\n resolve(meta);\n };\n video.onerror = () => reject(new Error(\"Failed to load video metadata\"));\n });\n};\n\nconst getThumbnail = async (videoUrl, seekTime = 0.1, playbackRate = 1) => {\n return new Promise((resolve, reject) => {\n const video = document.createElement(\"video\");\n video.crossOrigin = \"anonymous\";\n video.muted = true;\n video.playsInline = true;\n video.autoplay = false;\n video.preload = \"auto\";\n video.playbackRate = playbackRate;\n video.style.position = \"absolute\";\n video.style.left = \"-9999px\";\n video.style.top = \"-9999px\";\n video.style.width = \"1px\";\n video.style.height = \"1px\";\n video.style.opacity = \"0\";\n video.style.pointerEvents = \"none\";\n video.style.zIndex = \"-1\";\n let timeoutId;\n const cleanup = () => {\n if (video.parentNode) video.remove();\n if (timeoutId) clearTimeout(timeoutId);\n };\n const handleError = () => {\n cleanup();\n reject(new Error(`Failed to load video: ${video.error?.message || \"Unknown error\"}`));\n };\n const handleSeeked = () => {\n try {\n video.pause();\n const canvas = document.createElement(\"canvas\");\n const width = video.videoWidth || 640;\n const height = video.videoHeight || 360;\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n cleanup();\n reject(new Error(\"Failed to get canvas context\"));\n return;\n }\n ctx.drawImage(video, 0, 0, width, height);\n try {\n const dataUrl = canvas.toDataURL(\"image/jpeg\", 0.8);\n cleanup();\n resolve(dataUrl);\n } catch {\n canvas.toBlob((blob) => {\n if (!blob) {\n cleanup();\n reject(new Error(\"Failed to create Blob\"));\n return;\n }\n const blobUrl = URL.createObjectURL(blob);\n cleanup();\n resolve(blobUrl);\n }, \"image/jpeg\", 0.8);\n }\n } catch (err) {\n cleanup();\n reject(new Error(`Error creating thumbnail: ${err}`));\n }\n };\n video.addEventListener(\"error\", handleError, { once: true });\n video.addEventListener(\"seeked\", handleSeeked, { once: true });\n video.addEventListener(\"loadedmetadata\", () => {\n const playPromise = video.play();\n if (playPromise !== void 0) {\n playPromise.then(() => {\n video.currentTime = seekTime;\n }).catch(() => {\n video.currentTime = seekTime;\n });\n } else {\n video.currentTime = seekTime;\n }\n }, { once: true });\n timeoutId = window.setTimeout(() => {\n cleanup();\n reject(new Error(\"Video loading timed out\"));\n }, 15e3);\n video.src = videoUrl;\n document.body.appendChild(video);\n });\n};\n\nconst extractAudio = async ({\n src,\n playbackRate = 1,\n start = 0,\n end\n}) => {\n if (!src) throw new Error(\"src is required\");\n if (playbackRate <= 0) throw new Error(\"playbackRate must be > 0\");\n const isSafeUrl = /^(https?:|blob:|data:)/i.test(src);\n if (!isSafeUrl) throw new Error(\"Unsafe media source URL\");\n const audioBuffer = await fetchAndDecodeAudio(src);\n if (audioBuffer.duration === 0 || audioBuffer.length === 0) {\n throw new Error(\"No audio track found in the media source\");\n }\n if (isAudioSilent(audioBuffer)) {\n throw new Error(\"Audio track is silent (no audio content detected)\");\n }\n const clampedStart = Math.max(0, start || 0);\n const fullDuration = audioBuffer.duration;\n const clampedEnd = Math.min(\n typeof end === \"number\" ? end : fullDuration,\n fullDuration\n );\n if (clampedEnd <= clampedStart)\n throw new Error(\"Invalid range: end must be greater than start\");\n const renderedBuffer = await renderAudioSegment(\n audioBuffer,\n clampedStart,\n clampedEnd,\n playbackRate\n );\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\nconst hasAudio = async (src) => {\n if (!src) return false;\n const isSafeUrl = /^(https?:|blob:|data:)/i.test(src);\n if (!isSafeUrl) return false;\n try {\n const audioBuffer = await fetchAndDecodeAudio(src);\n if (audioBuffer.duration === 0 || audioBuffer.length === 0) {\n return false;\n }\n if (isAudioSilent(audioBuffer)) {\n return false;\n }\n return true;\n } catch (error) {\n return false;\n }\n};\nconst stitchAudio = async (segments, totalDuration) => {\n if (!segments || segments.length === 0) {\n throw new Error(\"At least one audio segment is required\");\n }\n const duration = totalDuration || Math.max(...segments.map((s) => s.e));\n const renderedBuffer = await createAudioTimeline(segments, duration);\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\nconst fetchAndDecodeAudio = async (src) => {\n const response = await fetch(src);\n if (!response.ok) throw new Error(`Failed to fetch source: ${response.status}`);\n const arrayBuffer = await response.arrayBuffer();\n return decodeAudioData(arrayBuffer);\n};\nconst decodeAudioData = async (arrayBuffer) => {\n const AudioContextCtor = window.AudioContext || window.webkitAudioContext;\n if (!AudioContextCtor) throw new Error(\"Web Audio API not supported\");\n const audioContext = new AudioContextCtor();\n try {\n return await new Promise((resolve, reject) => {\n audioContext.decodeAudioData(\n arrayBuffer.slice(0),\n (buf) => resolve(buf),\n (err) => reject(err || new Error(\"Failed to decode audio: no audio track found or unsupported format\"))\n );\n });\n } finally {\n audioContext.close();\n }\n};\nconst isAudioSilent = (buffer, threshold = 1e-3) => {\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const channelData = buffer.getChannelData(channel);\n for (let i = 0; i < channelData.length; i += 100) {\n if (Math.abs(channelData[i]) > threshold) {\n return false;\n }\n }\n }\n return true;\n};\nconst renderAudioSegment = async (audioBuffer, start, end, playbackRate) => {\n const OfflineAudioContextCtor = window.OfflineAudioContext || window.webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n const sampleRate = audioBuffer.sampleRate;\n const numChannels = audioBuffer.numberOfChannels;\n const sourceDuration = end - start;\n const renderedFrames = Math.max(\n 1,\n Math.ceil(sourceDuration / playbackRate * sampleRate)\n );\n const offline = new OfflineAudioContextCtor(numChannels, renderedFrames, sampleRate);\n const sourceNode = offline.createBufferSource();\n sourceNode.buffer = audioBuffer;\n sourceNode.playbackRate.value = playbackRate;\n sourceNode.connect(offline.destination);\n sourceNode.start(0, start, sourceDuration);\n return await offline.startRendering();\n};\nconst createAudioTimeline = async (segments, duration) => {\n const OfflineAudioContextCtor = window.OfflineAudioContext || window.webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n const sampleRate = 44100;\n const totalFrames = Math.ceil(duration * sampleRate);\n const offline = new OfflineAudioContextCtor(2, totalFrames, sampleRate);\n for (const segment of segments) {\n if (segment.s >= segment.e) {\n console.warn(`Invalid segment: start (${segment.s}) >= end (${segment.e})`);\n continue;\n }\n const volume = segment.volume ?? 1;\n if (volume <= 0) {\n console.warn(`Skipping muted segment: ${segment.src}`);\n continue;\n }\n try {\n const audioBuffer = await fetchAndDecodeAudio(segment.src);\n const segmentDuration = segment.e - segment.s;\n const sourceDuration = Math.min(segmentDuration, audioBuffer.duration);\n const source = offline.createBufferSource();\n source.buffer = audioBuffer;\n if (volume !== 1) {\n const gainNode = offline.createGain();\n gainNode.gain.value = volume;\n source.connect(gainNode);\n gainNode.connect(offline.destination);\n } else {\n source.connect(offline.destination);\n }\n source.start(segment.s, 0, sourceDuration);\n } catch (error) {\n console.warn(`Failed to process segment: ${segment.src}`, error);\n }\n }\n return await offline.startRendering();\n};\nconst audioBufferToMp3 = async (buffer) => {\n try {\n const wavArrayBuffer = audioBufferToWavArrayBuffer(buffer);\n const pcmBuffer = await decodeAudioData(wavArrayBuffer);\n return await encodePcmToMp3(pcmBuffer);\n } catch (error) {\n return audioBufferToWavBlob(buffer);\n }\n};\nconst audioBufferToWavArrayBuffer = (buffer) => {\n const numChannels = buffer.numberOfChannels;\n const sampleRate = buffer.sampleRate;\n const numFrames = buffer.length;\n const interleaved = interleave(buffer, numChannels, numFrames);\n const bytesPerSample = 2;\n const blockAlign = numChannels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n const dataSize = interleaved.length * bytesPerSample;\n const bufferSize = 44 + dataSize;\n const arrayBuffer = new ArrayBuffer(bufferSize);\n const view = new DataView(arrayBuffer);\n writeString(view, 0, \"RIFF\");\n view.setUint32(4, 36 + dataSize, true);\n writeString(view, 8, \"WAVE\");\n writeString(view, 12, \"fmt \");\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, numChannels, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, blockAlign, true);\n view.setUint16(34, 16, true);\n writeString(view, 36, \"data\");\n view.setUint32(40, dataSize, true);\n floatTo16BitPCM(view, 44, interleaved);\n return arrayBuffer;\n};\nconst encodePcmToMp3 = async (buffer) => {\n const lamejs = await import('./index-CXhwwSX-.js').then(n => n.i);\n const channels = buffer.numberOfChannels >= 2 ? 2 : 1;\n const targetSampleRate = 22050;\n const downsampledBuffer = downsampleAudioBuffer(buffer, targetSampleRate);\n const kbps = 48;\n const mp3encoder = new lamejs.default.Mp3Encoder(channels, targetSampleRate, kbps);\n const samplesPerFrame = 1152;\n const leftFloat = downsampledBuffer.getChannelData(0);\n const left = floatTo16(leftFloat);\n let right;\n if (channels === 2) {\n const rightFloat = downsampledBuffer.getChannelData(1);\n right = floatTo16(rightFloat);\n }\n const mp3Chunks = [];\n for (let i = 0; i < left.length; i += samplesPerFrame) {\n const leftChunk = left.subarray(i, Math.min(i + samplesPerFrame, left.length));\n let mp3buf;\n if (channels === 2 && right) {\n const rightChunk = right.subarray(i, Math.min(i + samplesPerFrame, right.length));\n mp3buf = mp3encoder.encodeBuffer(leftChunk, rightChunk);\n } else {\n mp3buf = mp3encoder.encodeBuffer(leftChunk);\n }\n if (mp3buf.length > 0) mp3Chunks.push(mp3buf);\n }\n const end = mp3encoder.flush();\n if (end.length > 0) mp3Chunks.push(end);\n return new Blob(mp3Chunks, { type: \"audio/mpeg\" });\n};\nconst audioBufferToWavBlob = (buffer) => {\n const arrayBuffer = audioBufferToWavArrayBuffer(buffer);\n return new Blob([arrayBuffer], { type: \"audio/wav\" });\n};\nconst downsampleAudioBuffer = (buffer, targetSampleRate) => {\n if (buffer.sampleRate === targetSampleRate) {\n return buffer;\n }\n const ratio = buffer.sampleRate / targetSampleRate;\n const newLength = Math.round(buffer.length / ratio);\n const newBuffer = new AudioContext().createBuffer(\n buffer.numberOfChannels,\n newLength,\n targetSampleRate\n );\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const oldData = buffer.getChannelData(channel);\n const newData = newBuffer.getChannelData(channel);\n for (let i = 0; i < newLength; i++) {\n const oldIndex = Math.floor(i * ratio);\n newData[i] = oldData[oldIndex];\n }\n }\n return newBuffer;\n};\nconst interleave = (buffer, numChannels, numFrames) => {\n if (numChannels === 1) {\n return buffer.getChannelData(0).slice(0, numFrames);\n }\n const result = new Float32Array(numFrames * numChannels);\n const channelData = [];\n for (let ch = 0; ch < numChannels; ch++) {\n channelData[ch] = buffer.getChannelData(ch);\n }\n let writeIndex = 0;\n for (let i = 0; i < numFrames; i++) {\n for (let ch = 0; ch < numChannels; ch++) {\n result[writeIndex++] = channelData[ch][i];\n }\n }\n return result;\n};\nconst floatTo16BitPCM = (view, offset, input) => {\n let pos = offset;\n for (let i = 0; i < input.length; i++, pos += 2) {\n let s = Math.max(-1, Math.min(1, input[i]));\n view.setInt16(pos, s < 0 ? s * 32768 : s * 32767, true);\n }\n};\nconst floatTo16 = (input) => {\n const output = new Int16Array(input.length);\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output[i] = s < 0 ? s * 32768 : s * 32767;\n }\n return output;\n};\nconst writeString = (view, offset, str) => {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n};\n\nconst getScaledDimensions = (width, height, maxWidth, maxHeight) => {\n if (width <= maxWidth && height <= maxHeight) {\n return {\n width: width % 2 === 0 ? width : width - 1,\n height: height % 2 === 0 ? height : height - 1\n };\n }\n const widthRatio = maxWidth / width;\n const heightRatio = maxHeight / height;\n const scale = Math.min(widthRatio, heightRatio);\n let scaledWidth = Math.round(width * scale);\n let scaledHeight = Math.round(height * scale);\n if (scaledWidth % 2 !== 0) {\n scaledWidth -= 1;\n }\n if (scaledHeight % 2 !== 0) {\n scaledHeight -= 1;\n }\n return {\n width: Math.min(scaledWidth, maxWidth),\n height: Math.min(scaledHeight, maxHeight)\n };\n};\nconst getObjectFitSize = (objectFit, elementSize, containerSize) => {\n const elementAspectRatio = elementSize.width / elementSize.height;\n const containerAspectRatio = containerSize.width / containerSize.height;\n switch (objectFit) {\n case \"contain\":\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio\n };\n } else {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height\n };\n }\n case \"cover\":\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height\n };\n } else {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio\n };\n }\n case \"fill\":\n return {\n width: containerSize.width,\n height: containerSize.height\n };\n default:\n return {\n width: elementSize.width,\n height: elementSize.height\n };\n }\n};\n\nconst blobUrlToFile = async (blobUrl, fileName) => {\n const response = await fetch(blobUrl);\n const blob = await response.blob();\n return new File([blob], fileName, { type: blob.type });\n};\nconst loadFile = (accept) => {\n return new Promise((resolve, reject) => {\n try {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.accept = accept;\n input.style.display = \"none\";\n document.body.appendChild(input);\n const cleanup = () => {\n input.value = \"\";\n document.body.removeChild(input);\n };\n input.onchange = () => {\n const file = input.files && input.files[0];\n cleanup();\n if (!file) {\n reject(new Error(\"No file selected\"));\n return;\n }\n resolve(file);\n };\n input.click();\n } catch (error) {\n reject(error);\n }\n });\n};\nconst saveAsFile = (content, type, name) => {\n const blob = typeof content === \"string\" ? new Blob([content], { type }) : content;\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = name;\n a.click();\n URL.revokeObjectURL(url);\n};\nconst downloadFile = async (url, filename) => {\n try {\n const response = await fetch(url);\n const blob = await response.blob();\n const downloadUrl = window.URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = downloadUrl;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n window.URL.revokeObjectURL(downloadUrl);\n } catch (error) {\n console.error(\"Error downloading file:\", error);\n throw error;\n }\n};\n\nconst detectMediaTypeFromUrl = async (url) => {\n try {\n const response = await fetch(url, { method: \"HEAD\" });\n const contentType = response.headers.get(\"Content-Type\");\n if (!contentType) return null;\n if (contentType.startsWith(\"image/\")) return \"image\";\n if (contentType.startsWith(\"video/\")) return \"video\";\n if (contentType.startsWith(\"audio/\")) return \"audio\";\n return null;\n } catch (error) {\n console.error(\"Fetch failed:\", error);\n return null;\n }\n};\n\nexport { blobUrlToFile, detectMediaTypeFromUrl, downloadFile, extractAudio, getAudioDuration, getImageDimensions, getObjectFitSize, getScaledDimensions, getThumbnail, getVideoMeta, hasAudio, limit, loadFile, saveAsFile, stitchAudio };\n//# sourceMappingURL=index.mjs.map\n","import {\n Canvas as FabricCanvas,\n FabricText,\n Textbox,\n Group,\n FabricImage,\n Rect,\n Circle,\n Shadow,\n} from \"fabric\";\nimport { convertToCanvasPosition } from \"../helpers/canvas.util\";\nimport {\n CanvasElement,\n CanvasMetadata,\n CaptionProps,\n FrameEffect,\n} from \"../types\";\nimport {\n DEFAULT_CAPTION_PROPS,\n DEFAULT_TEXT_PROPS,\n} from \"../helpers/constants\";\nimport { disabledControl, rotateControl } from \"./element-controls\";\nimport { getObjectFitSize, getThumbnail } from \"@twick/media-utils\";\n\n/**\n * Add a text element to the canvas.\n * Creates and configures a Fabric.js Textbox object with specified properties\n * including position, styling, interactive controls, and text wrapping support.\n *\n * @param element - The canvas element configuration\n * @param index - The z-index of the element\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata about the canvas including scale and dimensions\n * @returns The configured Fabric.js Textbox object with text wrapping enabled\n *\n * @example\n * ```js\n * const textElement = addTextElement({\n * element: { id: \"text1\", props: { text: \"Hello\", x: 100, y: 100, width: 200 } },\n * index: 1,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addTextElement = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n\n let width = element.props?.width ? element.props.width * canvasMetadata.scaleX : canvasMetadata.width;\n if (element.props?.maxWidth) {\n width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);\n }\n const text = new Textbox(element.props?.text || element.t || \"\", {\n left: x,\n top: y,\n originX: \"center\",\n originY: \"center\",\n angle: element.props?.rotation || 0,\n fontSize: Math.floor(\n (element.props?.fontSize || DEFAULT_TEXT_PROPS.size) *\n canvasMetadata.scaleX\n ),\n fontFamily: element.props?.fontFamily || DEFAULT_TEXT_PROPS.family,\n fontStyle: element.props?.fontStyle || \"normal\",\n fontWeight: element.props?.fontWeight || \"normal\",\n fill: element.props?.fill || DEFAULT_TEXT_PROPS.fill,\n opacity: element.props?.opacity ?? 1,\n width: width,\n splitByGrapheme: false,\n textAlign: element.props?.textAlign || \"center\",\n stroke: element.props?.stroke || DEFAULT_TEXT_PROPS.stroke,\n strokeWidth: element.props?.lineWidth || DEFAULT_TEXT_PROPS.lineWidth,\n shadow: element.props?.shadowColor\n ? new Shadow({\n offsetX:\n element.props?.shadowOffset?.length &&\n element.props?.shadowOffset?.length > 1\n ? element.props.shadowOffset[0] / 2\n : 1,\n offsetY:\n element.props?.shadowOffset?.length &&\n element.props?.shadowOffset.length > 1\n ? element.props.shadowOffset[1] / 2\n : 1,\n blur: (element.props?.shadowBlur || 2) / 2,\n color: element.props?.shadowColor,\n })\n : undefined,\n });\n\n // Assign metadata and custom controls\n text.set(\"id\", element.id);\n text.set(\"zIndex\", index);\n\n // Disable unwanted control points\n text.controls.mt = disabledControl;\n text.controls.mb = disabledControl;\n text.controls.ml = disabledControl;\n text.controls.mr = disabledControl;\n text.controls.bl = disabledControl;\n text.controls.br = disabledControl;\n text.controls.tl = disabledControl;\n text.controls.tr = disabledControl;\n text.controls.mtr = rotateControl;\n\n canvas.add(text);\n return text;\n};\n\n/**\n * Sets image properties for a Fabric.js image object.\n * Configures position, size, and metadata for image elements\n * on the canvas with proper scaling and positioning.\n *\n * @param img - The Fabric.js image object to configure\n * @param element - The canvas element configuration\n * @param index - The z-index of the element\n * @param canvasMetadata - Metadata about the canvas including scale and dimensions\n *\n * @example\n * ```js\n * setImageProps({\n * img: fabricImage,\n * element: { id: \"img1\", props: { width: 200, height: 150, x: 50, y: 50 } },\n * index: 2,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nconst setImageProps = ({\n img,\n element,\n index,\n canvasMetadata,\n}: {\n img: FabricImage;\n element: CanvasElement;\n index: number;\n canvasMetadata: CanvasMetadata;\n}) => {\n const width =\n (element.props?.width || 0) * canvasMetadata.scaleX || canvasMetadata.width;\n const height =\n (element.props?.height || 0) * canvasMetadata.scaleY ||\n canvasMetadata.height;\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n console.log(width, height, x, y);\n img.set(\"id\", element.id);\n img.set(\"zIndex\", index);\n img.set(\"width\", width);\n img.set(\"height\", height);\n img.set(\"left\", x);\n img.set(\"top\", y);\n img.set(\"opacity\", element.props?.opacity ?? 1);\n img.set(\"selectable\", true);\n img.set(\"hasControls\", true);\n img.set(\"touchAction\", \"all\");\n};\n\n/**\n * Add a caption element to the canvas based on provided props.\n * Creates a text element with caption-specific styling including\n * shadows, positioning, and font properties.\n *\n * @param element - The canvas element configuration\n * @param index - The z-index of the element\n * @param canvas - The Fabric.js canvas instance\n * @param captionProps - Default and user-defined caption properties\n * @param canvasMetadata - Metadata about the canvas including scale and dimensions\n * @returns The configured Fabric.js caption object\n *\n * @example\n * ```js\n * const captionElement = addCaptionElement({\n * element: { id: \"caption1\", props: { text: \"Caption\", pos: { x: 100, y: 100 } } },\n * index: 3,\n * canvas: fabricCanvas,\n * captionProps: { font: { size: 24, family: \"Arial\" } },\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addCaptionElement = ({\n element,\n index,\n canvas,\n captionProps,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n captionProps: CaptionProps;\n canvasMetadata: CanvasMetadata;\n}) => {\n const { x, y } = convertToCanvasPosition(\n (captionProps?.applyToAll ? captionProps?.x : element.props?.x) ?? 0,\n (captionProps?.applyToAll ? captionProps?.y : element.props?.y) ?? 0,\n canvasMetadata\n );\n\n const caption = new FabricText(element.props?.text || element.t || \"\", {\n left: x,\n top: y,\n originX: \"center\",\n originY: \"center\",\n angle: element.props?.rotation || 0,\n fontSize: Math.round(\n ((captionProps?.applyToAll\n ? captionProps?.font?.size\n : element.props?.font?.size ?? captionProps?.font?.size) ??\n DEFAULT_CAPTION_PROPS.size) * canvasMetadata.scaleX\n ),\n fontFamily:\n (captionProps?.applyToAll\n ? captionProps?.font?.family\n : element.props?.font?.family ?? captionProps?.font?.family) ??\n DEFAULT_CAPTION_PROPS.family,\n fill:\n (captionProps?.applyToAll\n ? captionProps.color?.text\n : element.props?.fill ?? captionProps.color?.text) ??\n DEFAULT_CAPTION_PROPS.fill,\n fontWeight: (captionProps?.applyToAll\n ? captionProps?.font?.weight\n : element.props?.fontWeight ?? captionProps?.font?.weight) ??\n DEFAULT_CAPTION_PROPS.fontWeight,\n stroke: (captionProps?.applyToAll\n ? captionProps?.stroke\n : element.props?.stroke ?? captionProps?.stroke) ??\n DEFAULT_CAPTION_PROPS.stroke,\n opacity: (captionProps?.applyToAll\n ? captionProps?.opacity\n : element.props?.opacity ?? captionProps?.opacity) ?? 1,\n shadow: new Shadow({\n offsetX:\n (captionProps?.applyToAll\n ? captionProps?.shadowOffset?.[0]\n : element.props?.shadowOffset?.[0] ?? captionProps?.shadowOffset?.[0]) ?? \n DEFAULT_CAPTION_PROPS.shadowOffset?.[0],\n offsetY:\n (captionProps?.applyToAll\n ? captionProps?.shadowOffset?.[1]\n : element.props?.shadowOffset?.[1] ?? captionProps?.shadowOffset?.[1]) ??\n DEFAULT_CAPTION_PROPS.shadowOffset?.[1],\n blur: (captionProps?.applyToAll\n ? captionProps?.shadowBlur\n : element.props?.shadowBlur ?? captionProps?.shadowBlur) ?? DEFAULT_CAPTION_PROPS.shadowBlur,\n color: (captionProps?.applyToAll\n ? captionProps?.shadowColor\n : element.props?.shadowColor ?? captionProps?.shadowColor) ?? DEFAULT_CAPTION_PROPS.shadowColor,\n }),\n strokeWidth: (captionProps?.applyToAll \n ? captionProps?.lineWidth\n : element.props?.lineWidth ?? captionProps?.lineWidth) ?? DEFAULT_CAPTION_PROPS.lineWidth,\n });\n\n // Assign metadata and custom controls\n caption.set(\"id\", element.id);\n caption.set(\"zIndex\", index);\n\n // Disable unwanted control points\n caption.controls.mt = disabledControl;\n caption.controls.mb = disabledControl;\n caption.controls.ml = disabledControl;\n caption.controls.mr = disabledControl;\n caption.controls.bl = disabledControl;\n caption.controls.br = disabledControl;\n caption.controls.tl = disabledControl;\n caption.controls.tr = disabledControl;\n caption.controls.mtr = disabledControl;\n\n canvas.add(caption);\n return caption;\n};\n\n/**\n * Add a video frame as element into a Fabric.js image object and optionally groups it with a frame.\n * Creates a video element by extracting a frame at the specified time and applying\n * optional frame effects for enhanced visual presentation.\n *\n * @param element - The video element containing properties like source and frame information\n * @param index - The z-index for ordering the element on the canvas\n * @param canvas - The Fabric.js canvas instance\n * @param snapTime - The time to snap the video frame with respect to full video duration\n * @param canvasMetadata - Metadata of the canvas, including dimensions and scale factors\n * @param currentFrameEffect - Optional frame effect to apply to the image\n * @returns A Fabric.js image object or a group with an image and frame\n *\n * @example\n * ```js\n * const videoElement = await addVideoElement({\n * element: {\n * id: \"video1\",\n * props: { src: \"video.mp4\", x: 100, y: 100 }\n * },\n * index: 2,\n * canvas: fabricCanvas,\n * snapTime: 5.0,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 },\n * currentFrameEffect: { shape: \"circle\", radius: 50 }\n * });\n * ```\n */\nexport const addVideoElement = async ({\n element,\n index,\n canvas,\n snapTime,\n canvasMetadata,\n currentFrameEffect,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n snapTime: number;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n}) => {\n try {\n const thumbnailUrl = await getThumbnail(\n element?.props?.src || \"\",\n snapTime\n );\n if (!thumbnailUrl) {\n console.error(\"Failed to get thumbnail\");\n return;\n }\n\n return addImageElement({\n imageUrl: thumbnailUrl,\n element,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n });\n } catch (error) {\n console.error(\"Error loading image:\", error);\n }\n};\n\n/**\n * Add an image element to the canvas and optionally group it with a frame.\n * Loads an image from URL and creates a Fabric.js image object with proper\n * positioning, scaling, and optional frame effects.\n *\n * @param imageUrl - Optional URL of the image to be added to the canvas\n * @param element - The image element containing properties like source and frame information\n * @param index - The z-index for ordering the element on the canvas\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata of the canvas including dimensions and scale factors\n * @param currentFrameEffect - Optional frame effect to apply to the image\n * @returns A Fabric.js image object or a group with an image and frame\n *\n * @example\n * ```js\n * const imageElement = await addImageElement({\n * imageUrl: \"https://example.com/image.jpg\",\n * element: { id: \"img1\", props: { src: \"image.jpg\", width: 200, height: 150 } },\n * index: 4,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addImageElement = async ({\n imageUrl,\n element,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n}: {\n imageUrl?: string;\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n}) => {\n try {\n // Load the image from the provided source URL\n const img = await FabricImage.fromURL(imageUrl || element.props.src || \"\");\n img.set({\n originX: \"center\",\n originY: \"center\",\n lockMovementX: false,\n lockMovementY: false,\n lockUniScaling: true,\n hasControls: false,\n selectable: false,\n });\n\n // Return the group if a frame is defined, otherwise return the image\n if (element.frame) {\n return addMediaGroup({\n element,\n img,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n });\n } else {\n setImageProps({ img, element, index, canvasMetadata });\n canvas.add(img);\n return img;\n }\n } catch (error) {\n console.error(\"Error loading image:\", error);\n }\n};\n\n/**\n * Add a Fabric.js group combining an image and its associated frame.\n * Applies styling, positioning, and scaling based on the given properties\n * and creates a grouped element for complex visual effects.\n *\n * @param element - The image element containing properties like frame, position, and styling\n * @param img - The Fabric.js image object to be included in the group\n * @param index - The z-index for ordering the group on the canvas\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata of the canvas including dimensions and scale factors\n * @param currentFrameEffect - Optional current frame effect to override default frame properties\n * @returns A Fabric.js group containing the image and frame with configured properties\n *\n * @example\n * ```js\n * const mediaGroup = addMediaGroup({\n * element: { id: \"group1\", frame: { size: [200, 150], x: 100, y: 100 } },\n * img: fabricImage,\n * index: 5,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nconst addMediaGroup = ({\n element,\n img,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n}: {\n element: CanvasElement;\n img: FabricImage;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n}) => {\n let frameSize;\n let angle;\n let framePosition;\n let frameRadius = 0;\n if (currentFrameEffect) {\n frameSize = {\n width:\n (currentFrameEffect.props.frameSize?.[0] || 0) *\n canvasMetadata.scaleX || canvasMetadata.width,\n height:\n (currentFrameEffect.props.frameSize?.[1] || 0) *\n canvasMetadata.scaleY || canvasMetadata.height,\n };\n angle = currentFrameEffect.props.rotation || 0;\n framePosition = currentFrameEffect.props.framePosition;\n if (currentFrameEffect.props.shape === \"circle\") {\n frameRadius = frameSize.width / 2;\n } else {\n frameRadius = currentFrameEffect?.props?.radius || 0;\n }\n } else {\n frameRadius = element?.frame?.radius || 0;\n frameSize = {\n width:\n (element?.frame?.size?.[0] || 0) * canvasMetadata.scaleX ||\n canvasMetadata.width,\n height:\n (element?.frame?.size?.[1] || 0) * canvasMetadata.scaleY ||\n canvasMetadata.height,\n };\n angle = element?.frame?.rotation || 0;\n framePosition = {\n x: element?.frame?.x || 0,\n y: element?.frame?.y || 0,\n };\n }\n\n const newSize = getObjectFitSize(\n element.objectFit,\n { width: img.width!, height: img.height! },\n frameSize\n );\n\n const frameRect = new Rect({\n originX: \"center\",\n originY: \"center\",\n lockMovementX: false,\n lockMovementY: false,\n lockUniScaling: true,\n hasControls: false,\n selectable: false,\n width: frameSize.width,\n height: frameSize.height,\n stroke: element?.frame?.stroke || \"#ffffff\",\n strokeWidth: element?.frame?.lineWidth || 0,\n hasRotatingPoint: true,\n rx: frameRadius || 0,\n ry: frameRadius || 0,\n });\n\n img.set({\n lockUniScaling: true,\n originX: \"center\",\n originY: \"center\",\n scaleX: newSize.width / img.width,\n scaleY: newSize.height / img.height,\n opacity: element.props?.opacity ?? 1,\n });\n\n const { x, y } = convertToCanvasPosition(\n framePosition?.x || 0,\n framePosition?.y || 0,\n canvasMetadata\n );\n\n const groupProps = {\n left: x,\n top: y,\n width: frameSize.width,\n height: frameSize.height,\n angle: angle,\n };\n\n // Customize the control points for the group\n // Change only the top control to a different style, keep others as circles\n\n const group = new Group([frameRect, img], {\n ...groupProps,\n originX: \"center\",\n originY: \"center\",\n angle: groupProps.angle,\n selectable: true,\n hasControls: true,\n hasBorders: true,\n clipPath: frameRect,\n });\n\n group.controls.mt = disabledControl;\n group.controls.mb = disabledControl;\n group.controls.ml = disabledControl;\n group.controls.mr = disabledControl;\n group.controls.mtr = rotateControl;\n\n group.set(\"id\", element.id);\n group.set(\"zIndex\", index);\n canvas.add(group);\n return group;\n};\n\n/**\n * Add a rectangular element to the canvas.\n * Creates a Fabric.js rectangle with specified properties including\n * position, size, styling, and interactive controls.\n *\n * @param element - The canvas element containing properties for the rectangle\n * @param index - The zIndex value used to determine the rendering order\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @returns A Fabric.js Rect object configured with the specified properties\n *\n * @example\n * ```js\n * const rectElement = addRectElement({\n * element: { id: \"rect1\", props: { width: 100, height: 50, x: 200, y: 150 } },\n * index: 6,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addRectElement = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n // Convert element's position to canvas coordinates\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n\n // Create a new rectangular Fabric.js object\n const rect = new Rect({\n left: x, // X-coordinate on the canvas\n top: y, // Y-coordinate on the canvas\n originX: \"center\", // Center the rectangle based on its position\n originY: \"center\", // Center the rectangle based on its position\n angle: element.props?.rotation || 0, // Rotation angle\n rx: (element.props?.radius || 0) * canvasMetadata.scaleX, // Horizontal radius for rounded corners\n ry: (element.props?.radius || 0) * canvasMetadata.scaleY, // Vertical radius for rounded corners\n stroke: element.props?.stroke || \"#000000\", // Stroke color\n strokeWidth: (element.props?.lineWidth || 0) * canvasMetadata.scaleX, // Scaled stroke width\n fill: element.props?.fill || \"#000000\", // Fill color\n opacity: element.props?.opacity || 1, // Opacity level\n width: (element.props?.width || 0) * canvasMetadata.scaleX, // Scaled width\n height: (element.props?.height || 0) * canvasMetadata.scaleY, // Scaled height\n });\n\n // Set custom properties for the rectangle\n rect.set(\"id\", element.id); // Unique identifier for the rectangle\n rect.set(\"zIndex\", index); // zIndex determines rendering order\n\n // Set custom control for rotation\n rect.controls.mtr = rotateControl;\n\n canvas.add(rect);\n return rect;\n};\n\nexport const addCircleElement = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n // Convert element's position to canvas coordinates\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n\n const circle = new Circle({\n left: x, // X-coordinate on the canvas\n top: y, // Y-coordinate on the canvas\n radius: (element.props?.radius || 0) * canvasMetadata.scaleX,\n fill: element.props?.fill || \"#000000\",\n stroke: element.props?.stroke || \"#000000\",\n strokeWidth: (element.props?.lineWidth || 0) * canvasMetadata.scaleX,\n originX: \"center\",\n originY: \"center\",\n });\n\n // Set custom control for rotation\n circle.controls.mt = disabledControl;\n circle.controls.mb = disabledControl;\n circle.controls.ml = disabledControl;\n circle.controls.mr = disabledControl;\n circle.controls.mtr = disabledControl;\n\n circle.set(\"id\", element.id);\n circle.set(\"zIndex\", index);\n canvas.add(circle);\n return circle;\n};\n\n/**\n * Add a background color to the canvas.\n * Creates a full-canvas rectangle with the specified background color\n * that serves as the base layer for other elements.\n *\n * @param element - The canvas element containing properties for the background\n * @param index - The zIndex value used to determine the rendering order\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @returns A Fabric.js Rect object configured with the specified properties\n *\n * @example\n * ```js\n * const bgElement = addBackgroundColor({\n * element: { id: \"bg1\", backgoundColor: \"#ffffff\" },\n * index: 0,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addBackgroundColor = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n const bgRect = new Rect({\n width: canvasMetadata.width,\n height: canvasMetadata.height,\n left: canvasMetadata.width / 2,\n top: canvasMetadata.height / 2,\n fill: element.backgoundColor ?? \"#000000\",\n originX: \"center\",\n originY: \"center\",\n hasControls: false,\n hasBorders: false,\n selectable: false,\n });\n\n bgRect.controls.mt = disabledControl;\n bgRect.controls.mb = disabledControl;\n bgRect.controls.ml = disabledControl;\n bgRect.controls.mr = disabledControl;\n bgRect.controls.bl = disabledControl;\n bgRect.controls.br = disabledControl;\n bgRect.controls.tl = disabledControl;\n bgRect.controls.tr = disabledControl;\n bgRect.controls.mtr = disabledControl;\n bgRect.set(\"zIndex\", index - 0.5);\n\n canvas.add(bgRect);\n return bgRect;\n};\n","import { useRef, useState } from \"react\";\nimport { Canvas as FabricCanvas, FabricObject, Textbox } from \"fabric\";\nimport { Dimensions } from \"@twick/media-utils\";\nimport {\n CanvasMetadata,\n CanvasProps,\n CanvasElement,\n CaptionProps,\n} from \"../types\";\nimport {\n clearCanvas,\n convertToVideoPosition,\n createCanvas,\n getCanvasContext,\n getCurrentFrameEffect,\n reorderElementsByZIndex,\n} from \"../helpers/canvas.util\";\nimport { CANVAS_OPERATIONS, ELEMENT_TYPES } from \"../helpers/constants\";\nimport {\n addImageElement,\n addVideoElement,\n addRectElement,\n addTextElement,\n addCaptionElement,\n addBackgroundColor,\n addCircleElement,\n} from \"../components/elements\";\n\n/**\n * Custom hook to manage a Fabric.js canvas and associated operations.\n * Provides functionality for canvas initialization, element management,\n * and event handling for interactive canvas operations.\n *\n * @param onCanvasReady - Callback executed when the canvas is ready\n * @param onCanvasOperation - Callback executed on canvas operations such as item selection or updates\n * @returns Object containing canvas-related functions and state\n *\n * @example\n * ```js\n * const { twickCanvas, buildCanvas, addElementToCanvas } = useTwickCanvas({\n * onCanvasReady: (canvas) => console.log('Canvas ready:', canvas),\n * onCanvasOperation: (operation, data) => console.log('Operation:', operation, data)\n * });\n * ```\n */\nexport const useTwickCanvas = ({\n onCanvasReady,\n onCanvasOperation,\n}: {\n onCanvasReady?: (canvas: FabricCanvas) => void;\n onCanvasOperation?: (operation: string, data: any) => void;\n}) => {\n const [twickCanvas, setTwickCanvas] = useState<FabricCanvas | null>(null); // Canvas instance\n const elementMap = useRef<Record<string, any>>({}); // Maps element IDs to their data\n const elementFrameMap = useRef<Record<string, any>>({}); // Maps element IDs to their frame effects\n const twickCanvasRef = useRef<FabricCanvas | null>(null);\n const videoSizeRef = useRef<Dimensions>({ width: 1, height: 1 }); // Stores the video dimensions\n const canvasResolutionRef = useRef<Dimensions>({ width: 1, height: 1 }); // Stores the canvas dimensions\n const captionPropsRef = useRef<CaptionProps | null>(null);\n const canvasMetadataRef = useRef<CanvasMetadata>({\n width: 0,\n height: 0,\n aspectRatio: 0,\n scaleX: 1,\n scaleY: 1,\n }); // Metadata for the canvas\n\n /**\n * Updates canvas metadata when the video size changes.\n * Recalculates scale factors based on the new video dimensions\n * to maintain proper coordinate mapping between canvas and video.\n *\n * @param videoSize - New video dimensions\n *\n * @example\n * ```js\n * onVideoSizeChange({ width: 1920, height: 1080 });\n * ```\n */\n const onVideoSizeChange = (videoSize: Dimensions) => {\n if (videoSize) {\n videoSizeRef.current = videoSize;\n canvasMetadataRef.current.scaleX =\n canvasMetadataRef.current.width / videoSize.width;\n canvasMetadataRef.current.scaleY =\n canvasMetadataRef.current.height / videoSize.height;\n }\n };\n\n /**\n * Initializes the Fabric.js canvas with the provided configuration.\n * Creates a new canvas instance with the specified properties and sets up\n * event listeners for interactive operations.\n *\n * @param props - Canvas configuration properties including size, colors, and behavior settings\n *\n * @example\n * ```js\n * buildCanvas({\n * videoSize: { width: 1920, height: 1080 },\n * canvasSize: { width: 800, height: 600 },\n * canvasRef: canvasElement,\n * backgroundColor: \"#000000\",\n * selectionBorderColor: \"#2563eb\"\n * });\n * ```\n */\n const buildCanvas = ({\n videoSize,\n canvasSize,\n canvasRef,\n backgroundColor = \"#000000\",\n selectionBorderColor = \"#2563eb\",\n selectionLineWidth = 2,\n uniScaleTransform = true,\n enableRetinaScaling = true,\n touchZoomThreshold = 10,\n forceBuild = false,\n }: CanvasProps & { forceBuild?: boolean }) => {\n if (!canvasRef) return;\n\n if (\n !forceBuild &&\n canvasResolutionRef.current.width === canvasSize.width &&\n canvasResolutionRef.current.height === canvasSize.height\n ) {\n return;\n }\n\n // Dispose of the old canvas if it exists\n if (twickCanvasRef.current) {\n console.log(\"Destroying twickCanvas\");\n twickCanvasRef.current.off(\"mouse:up\", handleMouseUp);\n twickCanvasRef.current.off(\"text:editing:exited\", onTextEdit);\n twickCanvasRef.current.dispose();\n }\n\n // Create a new canvas and update metadata\n const { canvas, canvasMetadata } = createCanvas({\n videoSize,\n canvasSize,\n canvasRef,\n backgroundColor,\n selectionBorderColor,\n selectionLineWidth,\n uniScaleTransform,\n enableRetinaScaling,\n touchZoomThreshold,\n });\n canvasMetadataRef.current = canvasMetadata;\n videoSizeRef.current = videoSize;\n // Attach event listeners\n canvas?.on(\"mouse:up\", handleMouseUp);\n canvas?.on(\"text:editing:exited\", onTextEdit);\n canvasResolutionRef.current = canvasSize;\n setTwickCanvas(canvas);\n twickCanvasRef.current = canvas;\n // Notify when canvas is ready\n if (onCanvasReady) {\n onCanvasReady(canvas);\n }\n };\n\n const onTextEdit = (event: any) => {\n if (event.target) {\n const object: FabricObject = event.target;\n const elementId = object.get(\"id\");\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n text:\n (object as Textbox).text ??\n elementMap.current[elementId].props.text,\n },\n };\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_UPDATED,\n elementMap.current[elementId]\n );\n }\n };\n\n /**\n * Handles mouse up events on the canvas.\n * Processes user interactions like dragging, scaling, and rotating elements,\n * updating element properties and triggering appropriate callbacks.\n *\n * @param event - Mouse event object containing interaction details\n */\n const handleMouseUp = (event: any) => {\n if (event.target) {\n const object: FabricObject = event.target;\n const elementId = object.get(\"id\");\n if (event.transform?.action === \"drag\") {\n const original = event.transform.original;\n if (object.left === original.left && object.top === original.top) {\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_SELECTED,\n elementMap.current[elementId]\n );\n return;\n }\n }\n switch (event.transform?.action) {\n case \"drag\":\n case \"scale\":\n case \"scaleX\":\n case \"scaleY\":\n case \"rotate\":\n const { x, y } = convertToVideoPosition(\n object.left,\n object.top,\n canvasMetadataRef.current,\n videoSizeRef.current\n );\n if (elementMap.current[elementId].type === \"caption\") {\n if (captionPropsRef.current?.applyToAll) {\n onCanvasOperation?.(CANVAS_OPERATIONS.CAPTION_PROPS_UPDATED, {\n element: elementMap.current[elementId],\n props: {\n ...captionPropsRef.current,\n x,\n y,\n },\n });\n } else {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n x,\n y,\n },\n };\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_UPDATED,\n elementMap.current[elementId]\n );\n }\n } else {\n if (object?.type === \"group\") {\n const currentFrameEffect = elementFrameMap.current[elementId];\n let updatedFrameSize;\n if (currentFrameEffect) {\n updatedFrameSize = [\n currentFrameEffect.props.frameSize[0] * object.scaleX,\n currentFrameEffect.props.frameSize[1] * object.scaleY,\n ];\n } else {\n updatedFrameSize = [\n elementMap.current[elementId].frame.size[0] * object.scaleX,\n elementMap.current[elementId].frame.size[1] * object.scaleY,\n ];\n }\n\n if (currentFrameEffect) {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n frameEffects: (\n elementMap.current[elementId].frameEffects || []\n ).map((frameEffect: any) =>\n frameEffect.id === currentFrameEffect?.id\n ? {\n ...frameEffect,\n props: {\n ...frameEffect.props,\n framePosition: {\n x,\n y,\n },\n frameSize: updatedFrameSize,\n },\n }\n : frameEffect\n ),\n };\n elementFrameMap.current[elementId] = {\n ...elementFrameMap.current[elementId],\n framePosition: {\n x,\n y,\n },\n frameSize: updatedFrameSize,\n };\n } else {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n frame: {\n ...elementMap.current[elementId].frame,\n rotation: object.angle,\n size: updatedFrameSize,\n x,\n y,\n },\n };\n }\n } else {\n if (object?.type === \"text\") {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n rotation: object.angle,\n x,\n y,\n },\n };\n } else if (object?.type === \"circle\") {\n const radius = Number(\n (\n elementMap.current[elementId].props.radius * object.scaleX\n ).toFixed(2)\n );\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n rotation: object.angle,\n radius: radius,\n height: radius * 2,\n width: radius * 2,\n x,\n y,\n },\n };\n } else {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n rotation: object.angle,\n width:\n elementMap.current[elementId].props.width * object.scaleX,\n height:\n elementMap.current[elementId].props.height *\n object.scaleY,\n x,\n y,\n },\n };\n }\n }\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_UPDATED,\n elementMap.current[elementId]\n );\n }\n break;\n }\n }\n };\n\n /**\n * Sets elements to the canvas.\n * Adds multiple elements to the canvas with optional cleanup and ordering.\n * Supports batch operations for efficient element management.\n *\n * @param options - Object containing elements, seek time, and additional options\n *\n * @example\n * ```js\n * await setCanvasElements({\n * elements: [element1, element2, element3],\n * seekTime: 5.0,\n * cleanAndAdd: true\n * });\n * ```\n */\n const setCanvasElements = async ({\n elements,\n seekTime = 0,\n captionProps,\n cleanAndAdd = false,\n }: {\n elements: CanvasElement[];\n seekTime?: number;\n captionProps?: any;\n cleanAndAdd?: boolean;\n }) => {\n if (!twickCanvas || !getCanvasContext(twickCanvas)) {\n console.warn(\"Canvas not properly initialized\");\n return;\n }\n\n try {\n if (cleanAndAdd && getCanvasContext(twickCanvas)) {\n // Store background color before clearing\n const backgroundColor = twickCanvas.backgroundColor;\n\n // Clear canvas before adding new elements\n clearCanvas(twickCanvas);\n\n // Restore background color\n if (backgroundColor) {\n twickCanvas.backgroundColor = backgroundColor;\n twickCanvas.renderAll();\n }\n }\n\n captionPropsRef.current = captionProps;\n await Promise.all(\n elements.map(async (element, index) => {\n try {\n if (!element) {\n console.warn(\"Element not found\");\n return;\n }\n await addElementToCanvas({\n element,\n index,\n reorder: false,\n seekTime,\n captionProps,\n });\n } catch (error) {\n console.error(`Error adding element ${element.id}:`, error);\n }\n })\n );\n reorderElementsByZIndex(twickCanvas);\n } catch (error) {\n console.error(\"Error in setCanvasElements:\", error);\n }\n };\n\n /**\n * Add element to the canvas.\n * Adds a single element to the canvas based on its type and properties.\n * Handles different element types (video, image, text, etc.) with appropriate rendering.\n *\n * @param options - Object containing element data, index, and rendering options\n *\n * @example\n * ```js\n * await addElementToCanvas({\n * element: videoElement,\n * index: 0,\n * reorder: true,\n * seekTime: 2.5\n * });\n * ```\n */\n const addElementToCanvas = async ({\n element,\n index,\n reorder = true,\n seekTime,\n captionProps,\n }: {\n element: CanvasElement;\n index: number;\n reorder: boolean;\n seekTime?: number;\n captionProps?: any;\n }) => {\n if (!twickCanvas) {\n console.warn(\"Canvas not initialized\");\n return;\n }\n // Add element based on type\n switch (element.type) {\n case ELEMENT_TYPES.VIDEO:\n const currentFrameEffect = getCurrentFrameEffect(\n element,\n seekTime || 0\n );\n elementFrameMap.current[element.id] = currentFrameEffect;\n const snapTime =\n ((seekTime || 0) - (element?.s || 0)) *\n (element?.props?.playbackRate || 1) +\n (element?.props?.time || 0);\n await addVideoElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n currentFrameEffect,\n snapTime,\n });\n if (element.timelineType === \"scene\") {\n await addBackgroundColor({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n }\n break;\n case ELEMENT_TYPES.IMAGE:\n await addImageElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n if (element.timelineType === \"scene\") {\n await addBackgroundColor({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n }\n break;\n case ELEMENT_TYPES.RECT:\n await addRectElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n case ELEMENT_TYPES.CIRCLE:\n await addCircleElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n case ELEMENT_TYPES.TEXT:\n await addTextElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n case ELEMENT_TYPES.CAPTION:\n await addCaptionElement({\n element,\n index,\n canvas: twickCanvas,\n captionProps,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n default:\n break;\n }\n elementMap.current[element.id] = element;\n if (reorder) {\n reorderElementsByZIndex(twickCanvas);\n }\n };\n\n return {\n twickCanvas,\n buildCanvas,\n onVideoSizeChange,\n addElementToCanvas,\n setCanvasElements,\n };\n};\n"],"names":["FabricCanvas"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/helpers/constants.ts","../src/helpers/browser.ts","../src/helpers/canvas.util.ts","../src/components/element-controls.tsx","../../media-utils/dist/index.mjs","../src/components/elements.tsx","../src/hooks/use-twick-canvas.ts"],"sourcesContent":["/**\n * Default text properties for canvas text elements.\n * Provides consistent styling defaults for text elements added to the canvas.\n * \n * @example\n * ```js\n * import { DEFAULT_TEXT_PROPS } from '@twick/canvas';\n * \n * const textElement = {\n * ...DEFAULT_TEXT_PROPS,\n * text: \"Hello World\",\n * x: 100,\n * y: 100\n * };\n * ```\n */\nexport const DEFAULT_TEXT_PROPS = {\n /** Font family for text elements */\n family: \"Poppins\",\n /** Font size in pixels */\n size: 48,\n /** Text fill color */\n fill: \"#FFFFFF\",\n /** Text stroke color */\n stroke: \"#000000\",\n /** Stroke line width */\n lineWidth: 0,\n }\n \n/**\n * Default caption properties for canvas caption elements.\n * Provides enhanced styling defaults specifically for caption elements\n * with background colors, shadows, and highlight options.\n * \n * @example\n * ```js\n * import { DEFAULT_CAPTION_PROPS } from '@twick/canvas';\n * \n * const captionElement = {\n * ...DEFAULT_CAPTION_PROPS,\n * text: \"Video Caption\",\n * x: 50,\n * y: 50\n * };\n * ```\n */\nexport const DEFAULT_CAPTION_PROPS = {\n /** Font family for caption elements */\n family: \"Poppins\",\n /** Font size in pixels */\n size: 48,\n /** Text fill color */\n fill: \"#FFFFFF\",\n /** Font weight */\n fontWeight: 600,\n /** Color configuration object */\n color: {\n /** Text color */\n text: \"#FFFFFF\",\n /** Background color */\n background: \"#000000\",\n /** Highlight color */\n highlight: \"#FFFFFF\",\n },\n /** Text stroke color */\n stroke: \"#000000\",\n /** Stroke line width */\n lineWidth: 0.2,\n /** Shadow color */\n shadowColor: \"#000000\",\n /** Shadow blur radius */\n shadowBlur: 2,\n /** Shadow offset [x, y] */\n shadowOffset: [0, 0],\n}\n\n/**\n * Canvas operation constants for tracking canvas state changes.\n * Defines the different types of operations that can occur on canvas elements.\n * \n * @example\n * ```js\n * import { CANVAS_OPERATIONS } from '@twick/canvas';\n * \n * function handleCanvasOperation(operation) {\n * switch (operation) {\n * case CANVAS_OPERATIONS.ITEM_ADDED:\n * console.log('New item added to canvas');\n * break;\n * case CANVAS_OPERATIONS.ITEM_UPDATED:\n * console.log('Item updated on canvas');\n * break;\n * }\n * }\n * ```\n */\nexport const CANVAS_OPERATIONS = {\n /** An item has been selected on the canvas */\n ITEM_SELECTED: \"ITEM_SELECTED\",\n /** An item has been updated/modified on the canvas */\n ITEM_UPDATED: \"ITEM_UPDATED\",\n /** An item has been deleted from the canvas */\n ITEM_DELETED: \"ITEM_DELETED\",\n /** A new item has been added to the canvas */\n ITEM_ADDED: \"ITEM_ADDED\",\n /** Items have been grouped together */\n ITEM_GROUPED: \"ITEM_GROUPED\",\n /** Items have been ungrouped */\n ITEM_UNGROUPED: \"ITEM_UNGROUPED\",\n /** Caption properties have been updated */\n CAPTION_PROPS_UPDATED: \"CAPTION_PROPS_UPDATED\"\n}\n\n/**\n * Element type constants for canvas elements.\n * Defines the different types of elements that can be added to the canvas.\n * \n * @example\n * ```js\n * import { ELEMENT_TYPES } from '@twick/canvas';\n * \n * if (element.type === ELEMENT_TYPES.TEXT) {\n * // Handle text element\n * } else if (element.type === ELEMENT_TYPES.IMAGE) {\n * // Handle image element\n * }\n * ```\n */\nexport const ELEMENT_TYPES = {\n /** Text element type */\n TEXT: \"text\",\n /** Caption element type */\n CAPTION: \"caption\",\n /** Image element type */\n IMAGE: \"image\",\n /** Video element type */\n VIDEO: \"video\",\n /** Rectangle element type */\n RECT: \"rect\",\n /** Circle element type */\n CIRCLE: \"circle\",\n /** Background color element type */\n BACKGROUND_COLOR: \"backgroundColor\",\n}\n","/**\n * Checks if the code is running in a browser environment.\n * Returns true if window object is available, false otherwise.\n * \n * @example\n * ```js\n * import { isBrowser } from '@twick/canvas';\n * \n * if (isBrowser) {\n * // Browser-specific code\n * console.log('Running in browser');\n * }\n * ```\n */\nexport const isBrowser = typeof window !== 'undefined';\n\n/**\n * Checks if the Canvas API is supported in the current environment.\n * Returns true if HTMLCanvasElement is available, false otherwise.\n * \n * @example\n * ```js\n * import { isCanvasSupported } from '@twick/canvas';\n * \n * if (isCanvasSupported) {\n * // Canvas operations are safe\n * createCanvas();\n * }\n * ```\n */\nexport const isCanvasSupported = isBrowser && !!window.HTMLCanvasElement;\n\n/**\n * Checks if the Video API is supported in the current environment.\n * Returns true if HTMLVideoElement is available, false otherwise.\n * \n * @example\n * ```js\n * import { isVideoSupported } from '@twick/canvas';\n * \n * if (isVideoSupported) {\n * // Video operations are safe\n * addVideoElement();\n * }\n * ```\n */\nexport const isVideoSupported = isBrowser && !!window.HTMLVideoElement;\n\n/**\n * Asserts that the code is running in a browser environment.\n * Throws an error if the code is executed in a non-browser context\n * such as Node.js server-side rendering.\n * \n * @throws Error when not running in a browser environment\n * \n * @example\n * ```js\n * assertBrowser();\n * // Code continues if in browser, throws error if not\n * ```\n */\nexport function assertBrowser() {\n if (!isBrowser) {\n throw new Error('This code can only run in a browser environment');\n }\n}\n\n/**\n * Asserts that the Canvas API is supported in the current environment.\n * Checks for HTMLCanvasElement support and throws an error if canvas\n * functionality is not available.\n * \n * @throws Error when Canvas API is not supported\n * \n * @example\n * ```js\n * assertCanvasSupport();\n * // Code continues if canvas is supported, throws error if not\n * ```\n */\nexport function assertCanvasSupport() {\n if (!isCanvasSupported) {\n throw new Error('Canvas is not supported in this environment');\n }\n}\n\n/**\n * Asserts that the Video API is supported in the current environment.\n * Checks for HTMLVideoElement support and throws an error if video\n * functionality is not available.\n * \n * @throws Error when Video API is not supported\n * \n * @example\n * ```js\n * assertVideoSupport();\n * // Code continues if video is supported, throws error if not\n * ```\n */\nexport function assertVideoSupport() {\n if (!isVideoSupported) {\n throw new Error('Video is not supported in this environment');\n }\n} ","import { Canvas as FabricCanvas } from \"fabric\";\nimport { CanvasMetadata, CanvasProps } from \"../types\";\nimport { Dimensions, Position } from \"@twick/media-utils\";\nimport { assertBrowser, assertCanvasSupport } from \"./browser\";\n\n/**\n * Creates and initializes a Fabric.js canvas with specified configurations.\n * Sets up a canvas with proper scaling, background, and interaction settings\n * based on the provided video and canvas dimensions.\n *\n * @param videoSize - The dimensions of the video\n * @param canvasSize - The dimensions of the canvas\n * @param canvasContainer - The HTML container for the canvas\n * @param backgroundColor - Background color of the canvas\n * @param selectionBorderColor - Border color for selected objects\n * @param selectionLineWidth - Width of the selection border\n * @param uniScaleTransform - Ensures uniform scaling of objects\n * @param enableRetinaScaling - Enables retina scaling for higher DPI\n * @param touchZoomThreshold - Threshold for touch zoom interactions\n * @returns Object containing the initialized canvas and its metadata\n *\n * @example\n * ```js\n * const { canvas, canvasMetadata } = createCanvas({\n * videoSize: { width: 1920, height: 1080 },\n * canvasSize: { width: 800, height: 600 },\n * canvasRef: canvasElement,\n * backgroundColor: \"#000000\",\n * selectionBorderColor: \"#2563eb\"\n * });\n * ```\n */\nexport const createCanvas = ({\n videoSize,\n canvasSize,\n canvasRef,\n backgroundColor = \"#000000\",\n selectionBorderColor = \"#2563eb\",\n selectionLineWidth = 2,\n uniScaleTransform = true,\n enableRetinaScaling = true,\n touchZoomThreshold = 10,\n}: CanvasProps): { canvas: FabricCanvas; canvasMetadata: CanvasMetadata } => {\n assertBrowser();\n assertCanvasSupport();\n\n // Metadata for scaling and positioning on the canvas\n const canvasMetadata = {\n width: canvasSize.width,\n height: canvasSize.height,\n aspectRatio: canvasSize.width / canvasSize.height,\n scaleX: Number((canvasSize.width / videoSize.width).toFixed(2)) ,\n scaleY: Number((canvasSize.height / videoSize.height).toFixed(2)),\n };\n\n // Create and configure the Fabric.js canvas\n const canvas = new FabricCanvas(canvasRef, {\n backgroundColor,\n width: canvasSize.width,\n height: canvasSize.height,\n preserveObjectStacking: true,\n enableRetinaScaling,\n selectionBorderColor,\n selectionLineWidth,\n uniScaleTransform,\n touchZoomThreshold,\n renderOnAddRemove: false,\n stateful: false,\n selection: true,\n skipTargetFind: false,\n controlsAboveOverlay: true,\n });\n\n // Set dimensions and render canvas\n if (canvasRef) {\n canvas.setDimensions({\n width: canvasMetadata.width,\n height: canvasMetadata.height,\n });\n canvas.renderAll();\n }\n\n return {\n canvas,\n canvasMetadata,\n };\n};\n\n/**\n * Reorders elements on the canvas based on their zIndex property.\n * Sorts all canvas objects by their zIndex and re-adds them to maintain\n * proper layering order for visual elements.\n *\n * @param canvas - The Fabric.js canvas instance\n *\n * @example\n * ```js\n * reorderElementsByZIndex(canvas);\n * // Elements are now properly layered based on zIndex\n * ```\n */\nexport const reorderElementsByZIndex = (canvas: FabricCanvas) => {\n if (!canvas) return;\n const backgroundColor = canvas.backgroundColor;\n\n const objects = canvas.getObjects();\n console.log(\"objects\", objects);\n // Sort objects by zIndex and re-add to the canvas in order\n objects.sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0));\n\n canvas.clear();\n canvas.backgroundColor = backgroundColor;\n\n objects.forEach((obj) => canvas.add(obj));\n canvas.renderAll();\n};\n\n/**\n * Retrieves the context of a Fabric.js canvas.\n * \n * @param canvas - The Fabric.js canvas instance\n * @returns The context of the canvas\n */\nexport const getCanvasContext = (canvas: FabricCanvas | null | undefined) => {\n if (!canvas || !canvas.elements?.lower?.ctx) return;\n return canvas.elements?.lower?.ctx;\n};\n\n/**\n * Clears all elements from the canvas and re-renders it.\n * Removes all objects from the canvas while preserving the background\n * and triggers a re-render to update the display.\n *\n * @param canvas - The Fabric.js canvas instance\n *\n * @example\n * ```js\n * clearCanvas(canvas);\n * // Canvas is now empty and ready for new elements\n * ```\n */\nexport const clearCanvas = (canvas: FabricCanvas | null | undefined) => {\n try {\n if (!canvas || !getCanvasContext(canvas)) return;\n canvas.clear();\n canvas.renderAll();\n } catch (error) {\n console.warn(\"Error clearing canvas:\", error);\n }\n};\n\n/**\n * Converts a position from the video coordinate space to the canvas coordinate space.\n * Applies scaling and centering transformations to map video coordinates\n * to the corresponding canvas pixel positions.\n *\n * @param x - X-coordinate in video space\n * @param y - Y-coordinate in video space\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @returns Object containing the corresponding position in canvas space\n *\n * @example\n * ```js\n * const canvasPos = convertToCanvasPosition(100, 200, canvasMetadata);\n * // canvasPos = { x: 450, y: 500 }\n * ```\n */\nexport const convertToCanvasPosition = (\n x: number,\n y: number,\n canvasMetadata: CanvasMetadata\n): Position => {\n return {\n x: x * canvasMetadata.scaleX + canvasMetadata.width / 2,\n y: y * canvasMetadata.scaleY + canvasMetadata.height / 2,\n };\n};\n\n/**\n * Converts a position from the canvas coordinate space to the video coordinate space.\n * Applies inverse scaling and centering transformations to map canvas coordinates\n * back to the corresponding video coordinate positions.\n *\n * @param x - X-coordinate in canvas space\n * @param y - Y-coordinate in canvas space\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @param videoSize - Dimensions of the video\n * @returns Object containing the corresponding position in video space\n *\n * @example\n * ```js\n * const videoPos = convertToVideoPosition(450, 500, canvasMetadata, videoSize);\n * // videoPos = { x: 100, y: 200 }\n * ```\n */\nexport const convertToVideoPosition = (\n x: number,\n y: number,\n canvasMetadata: CanvasMetadata,\n videoSize: Dimensions\n): Position => {\n return {\n x: Number((x / canvasMetadata.scaleX - videoSize.width / 2).toFixed(2)),\n y: Number((y / canvasMetadata.scaleY - videoSize.height / 2).toFixed(2)),\n };\n};\n\n/**\n * Retrieves the current frame effect for a given seek time.\n * Searches through the item's frame effects to find the one that is active\n * at the specified seek time based on start and end time ranges.\n *\n * @param item - The item containing frame effects\n * @param seekTime - The current time to match against frame effects\n * @returns The current frame effect active at the given seek time, or undefined if none found\n *\n * @example\n * ```js\n * const currentEffect = getCurrentFrameEffect(videoElement, 5.5);\n * // Returns the frame effect active at 5.5 seconds, if any\n * ```\n */\nexport const getCurrentFrameEffect = (item: any, seekTime: number) => {\n let currentFrameEffect;\n for (let i = 0; i < item?.frameEffects?.length; i++) {\n if (\n item.frameEffects[i].s <= seekTime &&\n item.frameEffects[i].e >= seekTime\n ) {\n currentFrameEffect = item.frameEffects[i];\n break;\n }\n }\n return currentFrameEffect;\n};\n","import { Control, controlsUtils } from \"fabric\";\n\n/**\n * Disabled control for canvas elements.\n * Creates a control that appears disabled and doesn't perform any actions\n * when interacted with. Useful for showing non-interactive control points.\n * \n * @example\n * ```js\n * import { disabledControl } from '@twick/canvas';\n * \n * // Apply to a canvas object\n * object.setControlsVisibility({\n * mt: false, // Disable top control\n * mb: false, // Disable bottom control\n * ml: false, // Disable left control\n * mr: false, // Disable right control\n * bl: disabledControl, // Use disabled control for bottom-left\n * });\n * ```\n */\nexport const disabledControl = new Control({\n /** X position offset */\n x: 0,\n /** Y position offset */\n y: -0.5,\n /** Additional Y offset */\n offsetY: 0,\n /** Cursor style when hovering */\n cursorStyle: \"pointer\",\n /** Action handler that does nothing */\n actionHandler: () => {\n return true;\n },\n /** Name of the action */\n actionName: \"scale\",\n /** Render function for the control */\n render: function (ctx: CanvasRenderingContext2D,\n left: number,\n top: number) {\n const size = 0;\n ctx.save();\n ctx.translate(left, top);\n ctx.fillStyle = \"#red\";\n ctx.fillRect(-size / 2, -size / 2, size, size);\n ctx.restore();\n },\n });\n\n/**\n * Rotation control for canvas elements.\n * Creates a control that allows rotation of canvas objects with snapping\n * functionality for precise angle adjustments.\n * \n * @example\n * ```js\n * import { rotateControl } from '@twick/canvas';\n * \n * // Apply to a canvas object\n * object.setControlsVisibility({\n * mtr: rotateControl, // Use custom rotate control for top-right\n * });\n * \n * // Enable rotation\n * object.set({\n * hasRotatingPoint: true,\n * lockRotation: false\n * });\n * ```\n */\nexport const rotateControl = new Control({\n /** X position offset */\n x: 0,\n /** Y position offset */\n y: -0.5,\n /** Additional Y offset for positioning */\n offsetY: -25,\n /** Cursor style when hovering */\n cursorStyle: \"crosshair\",\n /** Action handler with rotation and snapping */\n actionHandler: controlsUtils.rotationWithSnapping,\n /** Name of the action */\n actionName: \"rotate\",\n /** Whether to show connection line */\n withConnection: true,\n });","const imageDimensionsCache = {};\nconst videoMetaCache = {};\nconst audioDurationCache = {};\n\nconst getAudioDuration = (audioSrc) => {\n if (audioDurationCache[audioSrc]) {\n return Promise.resolve(audioDurationCache[audioSrc]);\n }\n return new Promise((resolve, reject) => {\n const audio = document.createElement(\"audio\");\n audio.preload = \"metadata\";\n const isSafeUrl = /^(https?:|blob:|data:audio\\/)/i.test(audioSrc);\n if (!isSafeUrl) {\n throw new Error(\"Unsafe audio source URL\");\n }\n audio.src = audioSrc;\n audio.onloadedmetadata = () => {\n const duration = audio.duration;\n audioDurationCache[audioSrc] = duration;\n resolve(duration);\n };\n audio.onerror = () => {\n reject(new Error(\"Failed to load audio metadata\"));\n };\n });\n};\n\nconst concurrencyLimit = 5;\nlet activeCount = 0;\nconst queue = [];\nfunction runNext() {\n if (queue.length === 0 || activeCount >= concurrencyLimit) return;\n const next = queue.shift();\n if (next) {\n activeCount++;\n next();\n }\n}\nfunction limit(fn) {\n return new Promise((resolve, reject) => {\n const task = () => {\n fn().then(resolve).catch(reject).finally(() => {\n activeCount--;\n runNext();\n });\n };\n if (activeCount < concurrencyLimit) {\n activeCount++;\n task();\n } else {\n queue.push(task);\n }\n });\n}\n\nconst loadImageDimensions = (url) => {\n return new Promise((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(new Error(\"getImageDimensions() is only available in the browser.\"));\n return;\n }\n const img = new Image();\n img.onload = () => {\n resolve({ width: img.naturalWidth, height: img.naturalHeight });\n };\n img.onerror = reject;\n img.src = url;\n });\n};\nconst getImageDimensions = (url) => {\n if (imageDimensionsCache[url]) {\n return Promise.resolve(imageDimensionsCache[url]);\n }\n return limit(() => loadImageDimensions(url)).then((dimensions) => {\n imageDimensionsCache[url] = dimensions;\n return dimensions;\n });\n};\n\nconst getVideoMeta = (videoSrc) => {\n if (videoMetaCache[videoSrc]) {\n return Promise.resolve(videoMetaCache[videoSrc]);\n }\n return new Promise((resolve, reject) => {\n const video = document.createElement(\"video\");\n video.preload = \"metadata\";\n const isSafeUrl = /^(https?:|blob:|data:video\\/)/i.test(videoSrc);\n if (!isSafeUrl) {\n reject(new Error(\"Unsafe video source URL\"));\n return;\n }\n video.src = videoSrc;\n video.onloadedmetadata = () => {\n const meta = {\n width: video.videoWidth,\n height: video.videoHeight,\n duration: video.duration\n };\n videoMetaCache[videoSrc] = meta;\n resolve(meta);\n };\n video.onerror = () => reject(new Error(\"Failed to load video metadata\"));\n });\n};\n\nconst getThumbnail = async (videoUrl, seekTime = 0.1, playbackRate = 1) => {\n return new Promise((resolve, reject) => {\n const video = document.createElement(\"video\");\n video.crossOrigin = \"anonymous\";\n video.muted = true;\n video.playsInline = true;\n video.autoplay = false;\n video.preload = \"auto\";\n video.playbackRate = playbackRate;\n video.style.position = \"absolute\";\n video.style.left = \"-9999px\";\n video.style.top = \"-9999px\";\n video.style.width = \"1px\";\n video.style.height = \"1px\";\n video.style.opacity = \"0\";\n video.style.pointerEvents = \"none\";\n video.style.zIndex = \"-1\";\n let timeoutId;\n const cleanup = () => {\n if (video.parentNode) video.remove();\n if (timeoutId) clearTimeout(timeoutId);\n };\n const handleError = () => {\n cleanup();\n reject(new Error(`Failed to load video: ${video.error?.message || \"Unknown error\"}`));\n };\n const handleSeeked = () => {\n try {\n video.pause();\n const canvas = document.createElement(\"canvas\");\n const width = video.videoWidth || 640;\n const height = video.videoHeight || 360;\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n cleanup();\n reject(new Error(\"Failed to get canvas context\"));\n return;\n }\n ctx.drawImage(video, 0, 0, width, height);\n try {\n const dataUrl = canvas.toDataURL(\"image/jpeg\", 0.8);\n cleanup();\n resolve(dataUrl);\n } catch {\n canvas.toBlob((blob) => {\n if (!blob) {\n cleanup();\n reject(new Error(\"Failed to create Blob\"));\n return;\n }\n const blobUrl = URL.createObjectURL(blob);\n cleanup();\n resolve(blobUrl);\n }, \"image/jpeg\", 0.8);\n }\n } catch (err) {\n cleanup();\n reject(new Error(`Error creating thumbnail: ${err}`));\n }\n };\n video.addEventListener(\"error\", handleError, { once: true });\n video.addEventListener(\"seeked\", handleSeeked, { once: true });\n video.addEventListener(\"loadedmetadata\", () => {\n const playPromise = video.play();\n if (playPromise !== void 0) {\n playPromise.then(() => {\n video.currentTime = seekTime;\n }).catch(() => {\n video.currentTime = seekTime;\n });\n } else {\n video.currentTime = seekTime;\n }\n }, { once: true });\n timeoutId = window.setTimeout(() => {\n cleanup();\n reject(new Error(\"Video loading timed out\"));\n }, 15e3);\n video.src = videoUrl;\n document.body.appendChild(video);\n });\n};\n\nconst extractAudio = async ({\n src,\n playbackRate = 1,\n start = 0,\n end\n}) => {\n if (!src) throw new Error(\"src is required\");\n if (playbackRate <= 0) throw new Error(\"playbackRate must be > 0\");\n const isSafeUrl = /^(https?:|blob:|data:)/i.test(src);\n if (!isSafeUrl) throw new Error(\"Unsafe media source URL\");\n const audioBuffer = await fetchAndDecodeAudio(src);\n if (audioBuffer.duration === 0 || audioBuffer.length === 0) {\n throw new Error(\"No audio track found in the media source\");\n }\n if (isAudioSilent(audioBuffer)) {\n throw new Error(\"Audio track is silent (no audio content detected)\");\n }\n const clampedStart = Math.max(0, start || 0);\n const fullDuration = audioBuffer.duration;\n const clampedEnd = Math.min(\n typeof end === \"number\" ? end : fullDuration,\n fullDuration\n );\n if (clampedEnd <= clampedStart)\n throw new Error(\"Invalid range: end must be greater than start\");\n const renderedBuffer = await renderAudioSegment(\n audioBuffer,\n clampedStart,\n clampedEnd,\n playbackRate\n );\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\nconst hasAudio = async (src) => {\n if (!src) return false;\n const isSafeUrl = /^(https?:|blob:|data:)/i.test(src);\n if (!isSafeUrl) return false;\n try {\n const audioBuffer = await fetchAndDecodeAudio(src);\n if (audioBuffer.duration === 0 || audioBuffer.length === 0) {\n return false;\n }\n if (isAudioSilent(audioBuffer)) {\n return false;\n }\n return true;\n } catch (error) {\n return false;\n }\n};\nconst stitchAudio = async (segments, totalDuration) => {\n if (!segments || segments.length === 0) {\n throw new Error(\"At least one audio segment is required\");\n }\n const duration = totalDuration || Math.max(...segments.map((s) => s.e));\n const renderedBuffer = await createAudioTimeline(segments, duration);\n const mp3Blob = await audioBufferToMp3(renderedBuffer);\n return URL.createObjectURL(mp3Blob);\n};\nconst fetchAndDecodeAudio = async (src) => {\n const response = await fetch(src);\n if (!response.ok) throw new Error(`Failed to fetch source: ${response.status}`);\n const arrayBuffer = await response.arrayBuffer();\n return decodeAudioData(arrayBuffer);\n};\nconst decodeAudioData = async (arrayBuffer) => {\n const AudioContextCtor = window.AudioContext || window.webkitAudioContext;\n if (!AudioContextCtor) throw new Error(\"Web Audio API not supported\");\n const audioContext = new AudioContextCtor();\n try {\n return await new Promise((resolve, reject) => {\n audioContext.decodeAudioData(\n arrayBuffer.slice(0),\n (buf) => resolve(buf),\n (err) => reject(err || new Error(\"Failed to decode audio: no audio track found or unsupported format\"))\n );\n });\n } finally {\n audioContext.close();\n }\n};\nconst isAudioSilent = (buffer, threshold = 1e-3) => {\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const channelData = buffer.getChannelData(channel);\n for (let i = 0; i < channelData.length; i += 100) {\n if (Math.abs(channelData[i]) > threshold) {\n return false;\n }\n }\n }\n return true;\n};\nconst renderAudioSegment = async (audioBuffer, start, end, playbackRate) => {\n const OfflineAudioContextCtor = window.OfflineAudioContext || window.webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n const sampleRate = audioBuffer.sampleRate;\n const numChannels = audioBuffer.numberOfChannels;\n const sourceDuration = end - start;\n const renderedFrames = Math.max(\n 1,\n Math.ceil(sourceDuration / playbackRate * sampleRate)\n );\n const offline = new OfflineAudioContextCtor(numChannels, renderedFrames, sampleRate);\n const sourceNode = offline.createBufferSource();\n sourceNode.buffer = audioBuffer;\n sourceNode.playbackRate.value = playbackRate;\n sourceNode.connect(offline.destination);\n sourceNode.start(0, start, sourceDuration);\n return await offline.startRendering();\n};\nconst createAudioTimeline = async (segments, duration) => {\n const OfflineAudioContextCtor = window.OfflineAudioContext || window.webkitOfflineAudioContext;\n if (!OfflineAudioContextCtor) throw new Error(\"OfflineAudioContext not supported\");\n const sampleRate = 44100;\n const totalFrames = Math.ceil(duration * sampleRate);\n const offline = new OfflineAudioContextCtor(2, totalFrames, sampleRate);\n for (const segment of segments) {\n if (segment.s >= segment.e) {\n console.warn(`Invalid segment: start (${segment.s}) >= end (${segment.e})`);\n continue;\n }\n const volume = segment.volume ?? 1;\n if (volume <= 0) {\n console.warn(`Skipping muted segment: ${segment.src}`);\n continue;\n }\n try {\n const audioBuffer = await fetchAndDecodeAudio(segment.src);\n const segmentDuration = segment.e - segment.s;\n const sourceDuration = Math.min(segmentDuration, audioBuffer.duration);\n const source = offline.createBufferSource();\n source.buffer = audioBuffer;\n if (volume !== 1) {\n const gainNode = offline.createGain();\n gainNode.gain.value = volume;\n source.connect(gainNode);\n gainNode.connect(offline.destination);\n } else {\n source.connect(offline.destination);\n }\n source.start(segment.s, 0, sourceDuration);\n } catch (error) {\n console.warn(`Failed to process segment: ${segment.src}`, error);\n }\n }\n return await offline.startRendering();\n};\nconst audioBufferToMp3 = async (buffer) => {\n try {\n const wavArrayBuffer = audioBufferToWavArrayBuffer(buffer);\n const pcmBuffer = await decodeAudioData(wavArrayBuffer);\n return await encodePcmToMp3(pcmBuffer);\n } catch (error) {\n return audioBufferToWavBlob(buffer);\n }\n};\nconst audioBufferToWavArrayBuffer = (buffer) => {\n const numChannels = buffer.numberOfChannels;\n const sampleRate = buffer.sampleRate;\n const numFrames = buffer.length;\n const interleaved = interleave(buffer, numChannels, numFrames);\n const bytesPerSample = 2;\n const blockAlign = numChannels * bytesPerSample;\n const byteRate = sampleRate * blockAlign;\n const dataSize = interleaved.length * bytesPerSample;\n const bufferSize = 44 + dataSize;\n const arrayBuffer = new ArrayBuffer(bufferSize);\n const view = new DataView(arrayBuffer);\n writeString(view, 0, \"RIFF\");\n view.setUint32(4, 36 + dataSize, true);\n writeString(view, 8, \"WAVE\");\n writeString(view, 12, \"fmt \");\n view.setUint32(16, 16, true);\n view.setUint16(20, 1, true);\n view.setUint16(22, numChannels, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, blockAlign, true);\n view.setUint16(34, 16, true);\n writeString(view, 36, \"data\");\n view.setUint32(40, dataSize, true);\n floatTo16BitPCM(view, 44, interleaved);\n return arrayBuffer;\n};\nconst encodePcmToMp3 = async (buffer) => {\n const lamejs = await import('./index-CXhwwSX-.mjs').then(n => n.i);\n const channels = buffer.numberOfChannels >= 2 ? 2 : 1;\n const targetSampleRate = 22050;\n const downsampledBuffer = downsampleAudioBuffer(buffer, targetSampleRate);\n const kbps = 48;\n const mp3encoder = new lamejs.default.Mp3Encoder(channels, targetSampleRate, kbps);\n const samplesPerFrame = 1152;\n const leftFloat = downsampledBuffer.getChannelData(0);\n const left = floatTo16(leftFloat);\n let right;\n if (channels === 2) {\n const rightFloat = downsampledBuffer.getChannelData(1);\n right = floatTo16(rightFloat);\n }\n const mp3Chunks = [];\n for (let i = 0; i < left.length; i += samplesPerFrame) {\n const leftChunk = left.subarray(i, Math.min(i + samplesPerFrame, left.length));\n let mp3buf;\n if (channels === 2 && right) {\n const rightChunk = right.subarray(i, Math.min(i + samplesPerFrame, right.length));\n mp3buf = mp3encoder.encodeBuffer(leftChunk, rightChunk);\n } else {\n mp3buf = mp3encoder.encodeBuffer(leftChunk);\n }\n if (mp3buf.length > 0) mp3Chunks.push(mp3buf);\n }\n const end = mp3encoder.flush();\n if (end.length > 0) mp3Chunks.push(end);\n return new Blob(mp3Chunks, { type: \"audio/mpeg\" });\n};\nconst audioBufferToWavBlob = (buffer) => {\n const arrayBuffer = audioBufferToWavArrayBuffer(buffer);\n return new Blob([arrayBuffer], { type: \"audio/wav\" });\n};\nconst downsampleAudioBuffer = (buffer, targetSampleRate) => {\n if (buffer.sampleRate === targetSampleRate) {\n return buffer;\n }\n const ratio = buffer.sampleRate / targetSampleRate;\n const newLength = Math.round(buffer.length / ratio);\n const newBuffer = new AudioContext().createBuffer(\n buffer.numberOfChannels,\n newLength,\n targetSampleRate\n );\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const oldData = buffer.getChannelData(channel);\n const newData = newBuffer.getChannelData(channel);\n for (let i = 0; i < newLength; i++) {\n const oldIndex = Math.floor(i * ratio);\n newData[i] = oldData[oldIndex];\n }\n }\n return newBuffer;\n};\nconst interleave = (buffer, numChannels, numFrames) => {\n if (numChannels === 1) {\n return buffer.getChannelData(0).slice(0, numFrames);\n }\n const result = new Float32Array(numFrames * numChannels);\n const channelData = [];\n for (let ch = 0; ch < numChannels; ch++) {\n channelData[ch] = buffer.getChannelData(ch);\n }\n let writeIndex = 0;\n for (let i = 0; i < numFrames; i++) {\n for (let ch = 0; ch < numChannels; ch++) {\n result[writeIndex++] = channelData[ch][i];\n }\n }\n return result;\n};\nconst floatTo16BitPCM = (view, offset, input) => {\n let pos = offset;\n for (let i = 0; i < input.length; i++, pos += 2) {\n let s = Math.max(-1, Math.min(1, input[i]));\n view.setInt16(pos, s < 0 ? s * 32768 : s * 32767, true);\n }\n};\nconst floatTo16 = (input) => {\n const output = new Int16Array(input.length);\n for (let i = 0; i < input.length; i++) {\n const s = Math.max(-1, Math.min(1, input[i]));\n output[i] = s < 0 ? s * 32768 : s * 32767;\n }\n return output;\n};\nconst writeString = (view, offset, str) => {\n for (let i = 0; i < str.length; i++) {\n view.setUint8(offset + i, str.charCodeAt(i));\n }\n};\n\nconst getScaledDimensions = (width, height, maxWidth, maxHeight) => {\n if (width <= maxWidth && height <= maxHeight) {\n return {\n width: width % 2 === 0 ? width : width - 1,\n height: height % 2 === 0 ? height : height - 1\n };\n }\n const widthRatio = maxWidth / width;\n const heightRatio = maxHeight / height;\n const scale = Math.min(widthRatio, heightRatio);\n let scaledWidth = Math.round(width * scale);\n let scaledHeight = Math.round(height * scale);\n if (scaledWidth % 2 !== 0) {\n scaledWidth -= 1;\n }\n if (scaledHeight % 2 !== 0) {\n scaledHeight -= 1;\n }\n return {\n width: Math.min(scaledWidth, maxWidth),\n height: Math.min(scaledHeight, maxHeight)\n };\n};\nconst getObjectFitSize = (objectFit, elementSize, containerSize) => {\n const elementAspectRatio = elementSize.width / elementSize.height;\n const containerAspectRatio = containerSize.width / containerSize.height;\n switch (objectFit) {\n case \"contain\":\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio\n };\n } else {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height\n };\n }\n case \"cover\":\n if (elementAspectRatio > containerAspectRatio) {\n return {\n width: containerSize.height * elementAspectRatio,\n height: containerSize.height\n };\n } else {\n return {\n width: containerSize.width,\n height: containerSize.width / elementAspectRatio\n };\n }\n case \"fill\":\n return {\n width: containerSize.width,\n height: containerSize.height\n };\n default:\n return {\n width: elementSize.width,\n height: elementSize.height\n };\n }\n};\n\nconst blobUrlToFile = async (blobUrl, fileName) => {\n const response = await fetch(blobUrl);\n const blob = await response.blob();\n return new File([blob], fileName, { type: blob.type });\n};\nconst loadFile = (accept) => {\n return new Promise((resolve, reject) => {\n try {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.accept = accept;\n input.style.display = \"none\";\n document.body.appendChild(input);\n const cleanup = () => {\n input.value = \"\";\n document.body.removeChild(input);\n };\n input.onchange = () => {\n const file = input.files && input.files[0];\n cleanup();\n if (!file) {\n reject(new Error(\"No file selected\"));\n return;\n }\n resolve(file);\n };\n input.click();\n } catch (error) {\n reject(error);\n }\n });\n};\nconst saveAsFile = (content, type, name) => {\n const blob = typeof content === \"string\" ? new Blob([content], { type }) : content;\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = name;\n a.click();\n URL.revokeObjectURL(url);\n};\nconst downloadFile = async (url, filename) => {\n try {\n const response = await fetch(url);\n const blob = await response.blob();\n const downloadUrl = window.URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = downloadUrl;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n window.URL.revokeObjectURL(downloadUrl);\n } catch (error) {\n console.error(\"Error downloading file:\", error);\n throw error;\n }\n};\n\nconst detectMediaTypeFromUrl = async (url) => {\n try {\n const response = await fetch(url, { method: \"HEAD\" });\n const contentType = response.headers.get(\"Content-Type\");\n if (!contentType) return null;\n if (contentType.startsWith(\"image/\")) return \"image\";\n if (contentType.startsWith(\"video/\")) return \"video\";\n if (contentType.startsWith(\"audio/\")) return \"audio\";\n return null;\n } catch (error) {\n console.error(\"Fetch failed:\", error);\n return null;\n }\n};\n\nexport { blobUrlToFile, detectMediaTypeFromUrl, downloadFile, extractAudio, getAudioDuration, getImageDimensions, getObjectFitSize, getScaledDimensions, getThumbnail, getVideoMeta, hasAudio, limit, loadFile, saveAsFile, stitchAudio };\n//# sourceMappingURL=index.mjs.map\n","import {\n Canvas as FabricCanvas,\n FabricText,\n Textbox,\n Group,\n FabricImage,\n Rect,\n Circle,\n Shadow,\n} from \"fabric\";\nimport { convertToCanvasPosition } from \"../helpers/canvas.util\";\nimport {\n CanvasElement,\n CanvasMetadata,\n CaptionProps,\n FrameEffect,\n} from \"../types\";\nimport {\n DEFAULT_CAPTION_PROPS,\n DEFAULT_TEXT_PROPS,\n} from \"../helpers/constants\";\nimport { disabledControl, rotateControl } from \"./element-controls\";\nimport { getObjectFitSize, getThumbnail } from \"@twick/media-utils\";\n\n/**\n * Add a text element to the canvas.\n * Creates and configures a Fabric.js Textbox object with specified properties\n * including position, styling, interactive controls, and text wrapping support.\n *\n * @param element - The canvas element configuration\n * @param index - The z-index of the element\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata about the canvas including scale and dimensions\n * @returns The configured Fabric.js Textbox object with text wrapping enabled\n *\n * @example\n * ```js\n * const textElement = addTextElement({\n * element: { id: \"text1\", props: { text: \"Hello\", x: 100, y: 100, width: 200 } },\n * index: 1,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addTextElement = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n\n let width = element.props?.width ? element.props.width * canvasMetadata.scaleX : canvasMetadata.width;\n if (element.props?.maxWidth) {\n width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);\n }\n const text = new Textbox(element.props?.text || element.t || \"\", {\n left: x,\n top: y,\n originX: \"center\",\n originY: \"center\",\n angle: element.props?.rotation || 0,\n fontSize: Math.floor(\n (element.props?.fontSize || DEFAULT_TEXT_PROPS.size) *\n canvasMetadata.scaleX\n ),\n fontFamily: element.props?.fontFamily || DEFAULT_TEXT_PROPS.family,\n fontStyle: element.props?.fontStyle || \"normal\",\n fontWeight: element.props?.fontWeight || \"normal\",\n fill: element.props?.fill || DEFAULT_TEXT_PROPS.fill,\n opacity: element.props?.opacity ?? 1,\n width: width,\n splitByGrapheme: false,\n textAlign: element.props?.textAlign || \"center\",\n stroke: element.props?.stroke || DEFAULT_TEXT_PROPS.stroke,\n strokeWidth: element.props?.lineWidth || DEFAULT_TEXT_PROPS.lineWidth,\n shadow: element.props?.shadowColor\n ? new Shadow({\n offsetX:\n element.props?.shadowOffset?.length &&\n element.props?.shadowOffset?.length > 1\n ? element.props.shadowOffset[0] / 2\n : 1,\n offsetY:\n element.props?.shadowOffset?.length &&\n element.props?.shadowOffset.length > 1\n ? element.props.shadowOffset[1] / 2\n : 1,\n blur: (element.props?.shadowBlur || 2) / 2,\n color: element.props?.shadowColor,\n })\n : undefined,\n });\n\n // Assign metadata and custom controls\n text.set(\"id\", element.id);\n text.set(\"zIndex\", index);\n\n // Disable unwanted control points\n text.controls.mt = disabledControl;\n text.controls.mb = disabledControl;\n text.controls.ml = disabledControl;\n text.controls.mr = disabledControl;\n text.controls.bl = disabledControl;\n text.controls.br = disabledControl;\n text.controls.tl = disabledControl;\n text.controls.tr = disabledControl;\n text.controls.mtr = rotateControl;\n\n canvas.add(text);\n return text;\n};\n\n/**\n * Sets image properties for a Fabric.js image object.\n * Configures position, size, and metadata for image elements\n * on the canvas with proper scaling and positioning.\n *\n * @param img - The Fabric.js image object to configure\n * @param element - The canvas element configuration\n * @param index - The z-index of the element\n * @param canvasMetadata - Metadata about the canvas including scale and dimensions\n *\n * @example\n * ```js\n * setImageProps({\n * img: fabricImage,\n * element: { id: \"img1\", props: { width: 200, height: 150, x: 50, y: 50 } },\n * index: 2,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nconst setImageProps = ({\n img,\n element,\n index,\n canvasMetadata,\n}: {\n img: FabricImage;\n element: CanvasElement;\n index: number;\n canvasMetadata: CanvasMetadata;\n}) => {\n const width =\n (element.props?.width || 0) * canvasMetadata.scaleX || canvasMetadata.width;\n const height =\n (element.props?.height || 0) * canvasMetadata.scaleY ||\n canvasMetadata.height;\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n console.log(width, height, x, y);\n img.set(\"id\", element.id);\n img.set(\"zIndex\", index);\n img.set(\"width\", width);\n img.set(\"height\", height);\n img.set(\"left\", x);\n img.set(\"top\", y);\n img.set(\"opacity\", element.props?.opacity ?? 1);\n img.set(\"selectable\", true);\n img.set(\"hasControls\", true);\n img.set(\"touchAction\", \"all\");\n};\n\n/**\n * Add a caption element to the canvas based on provided props.\n * Creates a text element with caption-specific styling including\n * shadows, positioning, and font properties.\n *\n * @param element - The canvas element configuration\n * @param index - The z-index of the element\n * @param canvas - The Fabric.js canvas instance\n * @param captionProps - Default and user-defined caption properties\n * @param canvasMetadata - Metadata about the canvas including scale and dimensions\n * @returns The configured Fabric.js caption object\n *\n * @example\n * ```js\n * const captionElement = addCaptionElement({\n * element: { id: \"caption1\", props: { text: \"Caption\", pos: { x: 100, y: 100 } } },\n * index: 3,\n * canvas: fabricCanvas,\n * captionProps: { font: { size: 24, family: \"Arial\" } },\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addCaptionElement = ({\n element,\n index,\n canvas,\n captionProps,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n captionProps: CaptionProps;\n canvasMetadata: CanvasMetadata;\n}) => {\n const { x, y } = convertToCanvasPosition(\n (captionProps?.applyToAll ? captionProps?.x : element.props?.x) ?? 0,\n (captionProps?.applyToAll ? captionProps?.y : element.props?.y) ?? 0,\n canvasMetadata\n );\n\n const caption = new FabricText(element.props?.text || element.t || \"\", {\n left: x,\n top: y,\n originX: \"center\",\n originY: \"center\",\n angle: element.props?.rotation || 0,\n fontSize: Math.round(\n ((captionProps?.applyToAll\n ? captionProps?.font?.size\n : element.props?.font?.size ?? captionProps?.font?.size) ??\n DEFAULT_CAPTION_PROPS.size) * canvasMetadata.scaleX\n ),\n fontFamily:\n (captionProps?.applyToAll\n ? captionProps?.font?.family\n : element.props?.font?.family ?? captionProps?.font?.family) ??\n DEFAULT_CAPTION_PROPS.family,\n fill:\n (captionProps?.applyToAll\n ? captionProps.color?.text\n : element.props?.fill ?? captionProps.color?.text) ??\n DEFAULT_CAPTION_PROPS.fill,\n fontWeight: (captionProps?.applyToAll\n ? captionProps?.font?.weight\n : element.props?.fontWeight ?? captionProps?.font?.weight) ??\n DEFAULT_CAPTION_PROPS.fontWeight,\n stroke: (captionProps?.applyToAll\n ? captionProps?.stroke\n : element.props?.stroke ?? captionProps?.stroke) ??\n DEFAULT_CAPTION_PROPS.stroke,\n opacity: (captionProps?.applyToAll\n ? captionProps?.opacity\n : element.props?.opacity ?? captionProps?.opacity) ?? 1,\n shadow: new Shadow({\n offsetX:\n (captionProps?.applyToAll\n ? captionProps?.shadowOffset?.[0]\n : element.props?.shadowOffset?.[0] ?? captionProps?.shadowOffset?.[0]) ?? \n DEFAULT_CAPTION_PROPS.shadowOffset?.[0],\n offsetY:\n (captionProps?.applyToAll\n ? captionProps?.shadowOffset?.[1]\n : element.props?.shadowOffset?.[1] ?? captionProps?.shadowOffset?.[1]) ??\n DEFAULT_CAPTION_PROPS.shadowOffset?.[1],\n blur: (captionProps?.applyToAll\n ? captionProps?.shadowBlur\n : element.props?.shadowBlur ?? captionProps?.shadowBlur) ?? DEFAULT_CAPTION_PROPS.shadowBlur,\n color: (captionProps?.applyToAll\n ? captionProps?.shadowColor\n : element.props?.shadowColor ?? captionProps?.shadowColor) ?? DEFAULT_CAPTION_PROPS.shadowColor,\n }),\n strokeWidth: (captionProps?.applyToAll \n ? captionProps?.lineWidth\n : element.props?.lineWidth ?? captionProps?.lineWidth) ?? DEFAULT_CAPTION_PROPS.lineWidth,\n });\n\n // Assign metadata and custom controls\n caption.set(\"id\", element.id);\n caption.set(\"zIndex\", index);\n\n // Disable unwanted control points\n caption.controls.mt = disabledControl;\n caption.controls.mb = disabledControl;\n caption.controls.ml = disabledControl;\n caption.controls.mr = disabledControl;\n caption.controls.bl = disabledControl;\n caption.controls.br = disabledControl;\n caption.controls.tl = disabledControl;\n caption.controls.tr = disabledControl;\n caption.controls.mtr = disabledControl;\n\n canvas.add(caption);\n return caption;\n};\n\n/**\n * Add a video frame as element into a Fabric.js image object and optionally groups it with a frame.\n * Creates a video element by extracting a frame at the specified time and applying\n * optional frame effects for enhanced visual presentation.\n *\n * @param element - The video element containing properties like source and frame information\n * @param index - The z-index for ordering the element on the canvas\n * @param canvas - The Fabric.js canvas instance\n * @param snapTime - The time to snap the video frame with respect to full video duration\n * @param canvasMetadata - Metadata of the canvas, including dimensions and scale factors\n * @param currentFrameEffect - Optional frame effect to apply to the image\n * @returns A Fabric.js image object or a group with an image and frame\n *\n * @example\n * ```js\n * const videoElement = await addVideoElement({\n * element: {\n * id: \"video1\",\n * props: { src: \"video.mp4\", x: 100, y: 100 }\n * },\n * index: 2,\n * canvas: fabricCanvas,\n * snapTime: 5.0,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 },\n * currentFrameEffect: { shape: \"circle\", radius: 50 }\n * });\n * ```\n */\nexport const addVideoElement = async ({\n element,\n index,\n canvas,\n snapTime,\n canvasMetadata,\n currentFrameEffect,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n snapTime: number;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n}) => {\n try {\n const thumbnailUrl = await getThumbnail(\n element?.props?.src || \"\",\n snapTime\n );\n if (!thumbnailUrl) {\n console.error(\"Failed to get thumbnail\");\n return;\n }\n\n return addImageElement({\n imageUrl: thumbnailUrl,\n element,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n });\n } catch (error) {\n console.error(\"Error loading image:\", error);\n }\n};\n\n/**\n * Add an image element to the canvas and optionally group it with a frame.\n * Loads an image from URL and creates a Fabric.js image object with proper\n * positioning, scaling, and optional frame effects.\n *\n * @param imageUrl - Optional URL of the image to be added to the canvas\n * @param element - The image element containing properties like source and frame information\n * @param index - The z-index for ordering the element on the canvas\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata of the canvas including dimensions and scale factors\n * @param currentFrameEffect - Optional frame effect to apply to the image\n * @returns A Fabric.js image object or a group with an image and frame\n *\n * @example\n * ```js\n * const imageElement = await addImageElement({\n * imageUrl: \"https://example.com/image.jpg\",\n * element: { id: \"img1\", props: { src: \"image.jpg\", width: 200, height: 150 } },\n * index: 4,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addImageElement = async ({\n imageUrl,\n element,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n}: {\n imageUrl?: string;\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n}) => {\n try {\n // Load the image from the provided source URL\n const img = await FabricImage.fromURL(imageUrl || element.props.src || \"\");\n img.set({\n originX: \"center\",\n originY: \"center\",\n lockMovementX: false,\n lockMovementY: false,\n lockUniScaling: true,\n hasControls: false,\n selectable: false,\n });\n\n // Return the group if a frame is defined, otherwise return the image\n if (element.frame) {\n return addMediaGroup({\n element,\n img,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n });\n } else {\n setImageProps({ img, element, index, canvasMetadata });\n canvas.add(img);\n return img;\n }\n } catch (error) {\n console.error(\"Error loading image:\", error);\n }\n};\n\n/**\n * Add a Fabric.js group combining an image and its associated frame.\n * Applies styling, positioning, and scaling based on the given properties\n * and creates a grouped element for complex visual effects.\n *\n * @param element - The image element containing properties like frame, position, and styling\n * @param img - The Fabric.js image object to be included in the group\n * @param index - The z-index for ordering the group on the canvas\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata of the canvas including dimensions and scale factors\n * @param currentFrameEffect - Optional current frame effect to override default frame properties\n * @returns A Fabric.js group containing the image and frame with configured properties\n *\n * @example\n * ```js\n * const mediaGroup = addMediaGroup({\n * element: { id: \"group1\", frame: { size: [200, 150], x: 100, y: 100 } },\n * img: fabricImage,\n * index: 5,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nconst addMediaGroup = ({\n element,\n img,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n}: {\n element: CanvasElement;\n img: FabricImage;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n}) => {\n let frameSize;\n let angle;\n let framePosition;\n let frameRadius = 0;\n if (currentFrameEffect) {\n frameSize = {\n width:\n (currentFrameEffect.props.frameSize?.[0] || 0) *\n canvasMetadata.scaleX || canvasMetadata.width,\n height:\n (currentFrameEffect.props.frameSize?.[1] || 0) *\n canvasMetadata.scaleY || canvasMetadata.height,\n };\n angle = currentFrameEffect.props.rotation || 0;\n framePosition = currentFrameEffect.props.framePosition;\n if (currentFrameEffect.props.shape === \"circle\") {\n frameRadius = frameSize.width / 2;\n } else {\n frameRadius = currentFrameEffect?.props?.radius || 0;\n }\n } else {\n frameRadius = element?.frame?.radius || 0;\n frameSize = {\n width:\n (element?.frame?.size?.[0] || 0) * canvasMetadata.scaleX ||\n canvasMetadata.width,\n height:\n (element?.frame?.size?.[1] || 0) * canvasMetadata.scaleY ||\n canvasMetadata.height,\n };\n angle = element?.frame?.rotation || 0;\n framePosition = {\n x: element?.frame?.x || 0,\n y: element?.frame?.y || 0,\n };\n }\n\n const newSize = getObjectFitSize(\n element.objectFit,\n { width: img.width!, height: img.height! },\n frameSize\n );\n\n const frameRect = new Rect({\n originX: \"center\",\n originY: \"center\",\n lockMovementX: false,\n lockMovementY: false,\n lockUniScaling: true,\n hasControls: false,\n selectable: false,\n width: frameSize.width,\n height: frameSize.height,\n stroke: element?.frame?.stroke || \"#ffffff\",\n strokeWidth: element?.frame?.lineWidth || 0,\n hasRotatingPoint: true,\n rx: frameRadius || 0,\n ry: frameRadius || 0,\n });\n\n img.set({\n lockUniScaling: true,\n originX: \"center\",\n originY: \"center\",\n scaleX: newSize.width / img.width,\n scaleY: newSize.height / img.height,\n opacity: element.props?.opacity ?? 1,\n });\n\n const { x, y } = convertToCanvasPosition(\n framePosition?.x || 0,\n framePosition?.y || 0,\n canvasMetadata\n );\n\n const groupProps = {\n left: x,\n top: y,\n width: frameSize.width,\n height: frameSize.height,\n angle: angle,\n };\n\n // Customize the control points for the group\n // Change only the top control to a different style, keep others as circles\n\n const group = new Group([frameRect, img], {\n ...groupProps,\n originX: \"center\",\n originY: \"center\",\n angle: groupProps.angle,\n selectable: true,\n hasControls: true,\n hasBorders: true,\n clipPath: frameRect,\n });\n\n group.controls.mt = disabledControl;\n group.controls.mb = disabledControl;\n group.controls.ml = disabledControl;\n group.controls.mr = disabledControl;\n group.controls.mtr = rotateControl;\n\n group.set(\"id\", element.id);\n group.set(\"zIndex\", index);\n canvas.add(group);\n return group;\n};\n\n/**\n * Add a rectangular element to the canvas.\n * Creates a Fabric.js rectangle with specified properties including\n * position, size, styling, and interactive controls.\n *\n * @param element - The canvas element containing properties for the rectangle\n * @param index - The zIndex value used to determine the rendering order\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @returns A Fabric.js Rect object configured with the specified properties\n *\n * @example\n * ```js\n * const rectElement = addRectElement({\n * element: { id: \"rect1\", props: { width: 100, height: 50, x: 200, y: 150 } },\n * index: 6,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addRectElement = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n // Convert element's position to canvas coordinates\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n\n // Create a new rectangular Fabric.js object\n const rect = new Rect({\n left: x, // X-coordinate on the canvas\n top: y, // Y-coordinate on the canvas\n originX: \"center\", // Center the rectangle based on its position\n originY: \"center\", // Center the rectangle based on its position\n angle: element.props?.rotation || 0, // Rotation angle\n rx: (element.props?.radius || 0) * canvasMetadata.scaleX, // Horizontal radius for rounded corners\n ry: (element.props?.radius || 0) * canvasMetadata.scaleY, // Vertical radius for rounded corners\n stroke: element.props?.stroke || \"#000000\", // Stroke color\n strokeWidth: (element.props?.lineWidth || 0) * canvasMetadata.scaleX, // Scaled stroke width\n fill: element.props?.fill || \"#000000\", // Fill color\n opacity: element.props?.opacity || 1, // Opacity level\n width: (element.props?.width || 0) * canvasMetadata.scaleX, // Scaled width\n height: (element.props?.height || 0) * canvasMetadata.scaleY, // Scaled height\n });\n\n // Set custom properties for the rectangle\n rect.set(\"id\", element.id); // Unique identifier for the rectangle\n rect.set(\"zIndex\", index); // zIndex determines rendering order\n\n // Set custom control for rotation\n rect.controls.mtr = rotateControl;\n\n canvas.add(rect);\n return rect;\n};\n\nexport const addCircleElement = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n // Convert element's position to canvas coordinates\n const { x, y } = convertToCanvasPosition(\n element.props?.x || 0,\n element.props?.y || 0,\n canvasMetadata\n );\n\n const circle = new Circle({\n left: x, // X-coordinate on the canvas\n top: y, // Y-coordinate on the canvas\n radius: (element.props?.radius || 0) * canvasMetadata.scaleX,\n fill: element.props?.fill || \"#000000\",\n stroke: element.props?.stroke || \"#000000\",\n strokeWidth: (element.props?.lineWidth || 0) * canvasMetadata.scaleX,\n originX: \"center\",\n originY: \"center\",\n });\n\n // Set custom control for rotation\n circle.controls.mt = disabledControl;\n circle.controls.mb = disabledControl;\n circle.controls.ml = disabledControl;\n circle.controls.mr = disabledControl;\n circle.controls.mtr = disabledControl;\n\n circle.set(\"id\", element.id);\n circle.set(\"zIndex\", index);\n canvas.add(circle);\n return circle;\n};\n\n/**\n * Add a background color to the canvas.\n * Creates a full-canvas rectangle with the specified background color\n * that serves as the base layer for other elements.\n *\n * @param element - The canvas element containing properties for the background\n * @param index - The zIndex value used to determine the rendering order\n * @param canvas - The Fabric.js canvas instance\n * @param canvasMetadata - Metadata containing canvas scaling and dimensions\n * @returns A Fabric.js Rect object configured with the specified properties\n *\n * @example\n * ```js\n * const bgElement = addBackgroundColor({\n * element: { id: \"bg1\", backgoundColor: \"#ffffff\" },\n * index: 0,\n * canvas: fabricCanvas,\n * canvasMetadata: { scaleX: 1, scaleY: 1, width: 800, height: 600 }\n * });\n * ```\n */\nexport const addBackgroundColor = ({\n element,\n index,\n canvas,\n canvasMetadata,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n}) => {\n const bgRect = new Rect({\n width: canvasMetadata.width,\n height: canvasMetadata.height,\n left: canvasMetadata.width / 2,\n top: canvasMetadata.height / 2,\n fill: element.backgoundColor ?? \"#000000\",\n originX: \"center\",\n originY: \"center\",\n hasControls: false,\n hasBorders: false,\n selectable: false,\n });\n\n bgRect.controls.mt = disabledControl;\n bgRect.controls.mb = disabledControl;\n bgRect.controls.ml = disabledControl;\n bgRect.controls.mr = disabledControl;\n bgRect.controls.bl = disabledControl;\n bgRect.controls.br = disabledControl;\n bgRect.controls.tl = disabledControl;\n bgRect.controls.tr = disabledControl;\n bgRect.controls.mtr = disabledControl;\n bgRect.set(\"zIndex\", index - 0.5);\n\n canvas.add(bgRect);\n return bgRect;\n};\n","import { useRef, useState } from \"react\";\nimport { Canvas as FabricCanvas, FabricObject, Textbox } from \"fabric\";\nimport { Dimensions } from \"@twick/media-utils\";\nimport {\n CanvasMetadata,\n CanvasProps,\n CanvasElement,\n CaptionProps,\n} from \"../types\";\nimport {\n clearCanvas,\n convertToVideoPosition,\n createCanvas,\n getCanvasContext,\n getCurrentFrameEffect,\n reorderElementsByZIndex,\n} from \"../helpers/canvas.util\";\nimport { CANVAS_OPERATIONS, ELEMENT_TYPES } from \"../helpers/constants\";\nimport {\n addImageElement,\n addVideoElement,\n addRectElement,\n addTextElement,\n addCaptionElement,\n addBackgroundColor,\n addCircleElement,\n} from \"../components/elements\";\n\n/**\n * Custom hook to manage a Fabric.js canvas and associated operations.\n * Provides functionality for canvas initialization, element management,\n * and event handling for interactive canvas operations.\n *\n * @param onCanvasReady - Callback executed when the canvas is ready\n * @param onCanvasOperation - Callback executed on canvas operations such as item selection or updates\n * @returns Object containing canvas-related functions and state\n *\n * @example\n * ```js\n * const { twickCanvas, buildCanvas, addElementToCanvas } = useTwickCanvas({\n * onCanvasReady: (canvas) => console.log('Canvas ready:', canvas),\n * onCanvasOperation: (operation, data) => console.log('Operation:', operation, data)\n * });\n * ```\n */\nexport const useTwickCanvas = ({\n onCanvasReady,\n onCanvasOperation,\n}: {\n onCanvasReady?: (canvas: FabricCanvas) => void;\n onCanvasOperation?: (operation: string, data: any) => void;\n}) => {\n const [twickCanvas, setTwickCanvas] = useState<FabricCanvas | null>(null); // Canvas instance\n const elementMap = useRef<Record<string, any>>({}); // Maps element IDs to their data\n const elementFrameMap = useRef<Record<string, any>>({}); // Maps element IDs to their frame effects\n const twickCanvasRef = useRef<FabricCanvas | null>(null);\n const videoSizeRef = useRef<Dimensions>({ width: 1, height: 1 }); // Stores the video dimensions\n const canvasResolutionRef = useRef<Dimensions>({ width: 1, height: 1 }); // Stores the canvas dimensions\n const captionPropsRef = useRef<CaptionProps | null>(null);\n const canvasMetadataRef = useRef<CanvasMetadata>({\n width: 0,\n height: 0,\n aspectRatio: 0,\n scaleX: 1,\n scaleY: 1,\n }); // Metadata for the canvas\n\n /**\n * Updates canvas metadata when the video size changes.\n * Recalculates scale factors based on the new video dimensions\n * to maintain proper coordinate mapping between canvas and video.\n *\n * @param videoSize - New video dimensions\n *\n * @example\n * ```js\n * onVideoSizeChange({ width: 1920, height: 1080 });\n * ```\n */\n const onVideoSizeChange = (videoSize: Dimensions) => {\n if (videoSize) {\n videoSizeRef.current = videoSize;\n canvasMetadataRef.current.scaleX =\n canvasMetadataRef.current.width / videoSize.width;\n canvasMetadataRef.current.scaleY =\n canvasMetadataRef.current.height / videoSize.height;\n }\n };\n\n /**\n * Initializes the Fabric.js canvas with the provided configuration.\n * Creates a new canvas instance with the specified properties and sets up\n * event listeners for interactive operations.\n *\n * @param props - Canvas configuration properties including size, colors, and behavior settings\n *\n * @example\n * ```js\n * buildCanvas({\n * videoSize: { width: 1920, height: 1080 },\n * canvasSize: { width: 800, height: 600 },\n * canvasRef: canvasElement,\n * backgroundColor: \"#000000\",\n * selectionBorderColor: \"#2563eb\"\n * });\n * ```\n */\n const buildCanvas = ({\n videoSize,\n canvasSize,\n canvasRef,\n backgroundColor = \"#000000\",\n selectionBorderColor = \"#2563eb\",\n selectionLineWidth = 2,\n uniScaleTransform = true,\n enableRetinaScaling = true,\n touchZoomThreshold = 10,\n forceBuild = false,\n }: CanvasProps & { forceBuild?: boolean }) => {\n if (!canvasRef) return;\n\n if (\n !forceBuild &&\n canvasResolutionRef.current.width === canvasSize.width &&\n canvasResolutionRef.current.height === canvasSize.height\n ) {\n return;\n }\n\n // Dispose of the old canvas if it exists\n if (twickCanvasRef.current) {\n console.log(\"Destroying twickCanvas\");\n twickCanvasRef.current.off(\"mouse:up\", handleMouseUp);\n twickCanvasRef.current.off(\"text:editing:exited\", onTextEdit);\n twickCanvasRef.current.dispose();\n }\n\n // Create a new canvas and update metadata\n const { canvas, canvasMetadata } = createCanvas({\n videoSize,\n canvasSize,\n canvasRef,\n backgroundColor,\n selectionBorderColor,\n selectionLineWidth,\n uniScaleTransform,\n enableRetinaScaling,\n touchZoomThreshold,\n });\n canvasMetadataRef.current = canvasMetadata;\n videoSizeRef.current = videoSize;\n // Attach event listeners\n canvas?.on(\"mouse:up\", handleMouseUp);\n canvas?.on(\"text:editing:exited\", onTextEdit);\n canvasResolutionRef.current = canvasSize;\n setTwickCanvas(canvas);\n twickCanvasRef.current = canvas;\n // Notify when canvas is ready\n if (onCanvasReady) {\n onCanvasReady(canvas);\n }\n };\n\n const onTextEdit = (event: any) => {\n if (event.target) {\n const object: FabricObject = event.target;\n const elementId = object.get(\"id\");\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n text:\n (object as Textbox).text ??\n elementMap.current[elementId].props.text,\n },\n };\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_UPDATED,\n elementMap.current[elementId]\n );\n }\n };\n\n /**\n * Handles mouse up events on the canvas.\n * Processes user interactions like dragging, scaling, and rotating elements,\n * updating element properties and triggering appropriate callbacks.\n *\n * @param event - Mouse event object containing interaction details\n */\n const handleMouseUp = (event: any) => {\n if (event.target) {\n const object: FabricObject = event.target;\n const elementId = object.get(\"id\");\n if (event.transform?.action === \"drag\") {\n const original = event.transform.original;\n if (object.left === original.left && object.top === original.top) {\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_SELECTED,\n elementMap.current[elementId]\n );\n return;\n }\n }\n switch (event.transform?.action) {\n case \"drag\":\n case \"scale\":\n case \"scaleX\":\n case \"scaleY\":\n case \"rotate\":\n const { x, y } = convertToVideoPosition(\n object.left,\n object.top,\n canvasMetadataRef.current,\n videoSizeRef.current\n );\n if (elementMap.current[elementId].type === \"caption\") {\n if (captionPropsRef.current?.applyToAll) {\n onCanvasOperation?.(CANVAS_OPERATIONS.CAPTION_PROPS_UPDATED, {\n element: elementMap.current[elementId],\n props: {\n ...captionPropsRef.current,\n x,\n y,\n },\n });\n } else {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n x,\n y,\n },\n };\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_UPDATED,\n elementMap.current[elementId]\n );\n }\n } else {\n if (object?.type === \"group\") {\n const currentFrameEffect = elementFrameMap.current[elementId];\n let updatedFrameSize;\n if (currentFrameEffect) {\n updatedFrameSize = [\n currentFrameEffect.props.frameSize[0] * object.scaleX,\n currentFrameEffect.props.frameSize[1] * object.scaleY,\n ];\n } else {\n updatedFrameSize = [\n elementMap.current[elementId].frame.size[0] * object.scaleX,\n elementMap.current[elementId].frame.size[1] * object.scaleY,\n ];\n }\n\n if (currentFrameEffect) {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n frameEffects: (\n elementMap.current[elementId].frameEffects || []\n ).map((frameEffect: any) =>\n frameEffect.id === currentFrameEffect?.id\n ? {\n ...frameEffect,\n props: {\n ...frameEffect.props,\n framePosition: {\n x,\n y,\n },\n frameSize: updatedFrameSize,\n },\n }\n : frameEffect\n ),\n };\n elementFrameMap.current[elementId] = {\n ...elementFrameMap.current[elementId],\n framePosition: {\n x,\n y,\n },\n frameSize: updatedFrameSize,\n };\n } else {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n frame: {\n ...elementMap.current[elementId].frame,\n rotation: object.angle,\n size: updatedFrameSize,\n x,\n y,\n },\n };\n }\n } else {\n if (object?.type === \"text\") {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n rotation: object.angle,\n x,\n y,\n },\n };\n } else if (object?.type === \"circle\") {\n const radius = Number(\n (\n elementMap.current[elementId].props.radius * object.scaleX\n ).toFixed(2)\n );\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n rotation: object.angle,\n radius: radius,\n height: radius * 2,\n width: radius * 2,\n x,\n y,\n },\n };\n } else {\n elementMap.current[elementId] = {\n ...elementMap.current[elementId],\n props: {\n ...elementMap.current[elementId].props,\n rotation: object.angle,\n width:\n elementMap.current[elementId].props.width * object.scaleX,\n height:\n elementMap.current[elementId].props.height *\n object.scaleY,\n x,\n y,\n },\n };\n }\n }\n onCanvasOperation?.(\n CANVAS_OPERATIONS.ITEM_UPDATED,\n elementMap.current[elementId]\n );\n }\n break;\n }\n }\n };\n\n /**\n * Sets elements to the canvas.\n * Adds multiple elements to the canvas with optional cleanup and ordering.\n * Supports batch operations for efficient element management.\n *\n * @param options - Object containing elements, seek time, and additional options\n *\n * @example\n * ```js\n * await setCanvasElements({\n * elements: [element1, element2, element3],\n * seekTime: 5.0,\n * cleanAndAdd: true\n * });\n * ```\n */\n const setCanvasElements = async ({\n elements,\n seekTime = 0,\n captionProps,\n cleanAndAdd = false,\n }: {\n elements: CanvasElement[];\n seekTime?: number;\n captionProps?: any;\n cleanAndAdd?: boolean;\n }) => {\n if (!twickCanvas || !getCanvasContext(twickCanvas)) {\n console.warn(\"Canvas not properly initialized\");\n return;\n }\n\n try {\n if (cleanAndAdd && getCanvasContext(twickCanvas)) {\n // Store background color before clearing\n const backgroundColor = twickCanvas.backgroundColor;\n\n // Clear canvas before adding new elements\n clearCanvas(twickCanvas);\n\n // Restore background color\n if (backgroundColor) {\n twickCanvas.backgroundColor = backgroundColor;\n twickCanvas.renderAll();\n }\n }\n\n captionPropsRef.current = captionProps;\n await Promise.all(\n elements.map(async (element, index) => {\n try {\n if (!element) {\n console.warn(\"Element not found\");\n return;\n }\n await addElementToCanvas({\n element,\n index,\n reorder: false,\n seekTime,\n captionProps,\n });\n } catch (error) {\n console.error(`Error adding element ${element.id}:`, error);\n }\n })\n );\n reorderElementsByZIndex(twickCanvas);\n } catch (error) {\n console.error(\"Error in setCanvasElements:\", error);\n }\n };\n\n /**\n * Add element to the canvas.\n * Adds a single element to the canvas based on its type and properties.\n * Handles different element types (video, image, text, etc.) with appropriate rendering.\n *\n * @param options - Object containing element data, index, and rendering options\n *\n * @example\n * ```js\n * await addElementToCanvas({\n * element: videoElement,\n * index: 0,\n * reorder: true,\n * seekTime: 2.5\n * });\n * ```\n */\n const addElementToCanvas = async ({\n element,\n index,\n reorder = true,\n seekTime,\n captionProps,\n }: {\n element: CanvasElement;\n index: number;\n reorder: boolean;\n seekTime?: number;\n captionProps?: any;\n }) => {\n if (!twickCanvas) {\n console.warn(\"Canvas not initialized\");\n return;\n }\n // Add element based on type\n switch (element.type) {\n case ELEMENT_TYPES.VIDEO:\n const currentFrameEffect = getCurrentFrameEffect(\n element,\n seekTime || 0\n );\n elementFrameMap.current[element.id] = currentFrameEffect;\n const snapTime =\n ((seekTime || 0) - (element?.s || 0)) *\n (element?.props?.playbackRate || 1) +\n (element?.props?.time || 0);\n await addVideoElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n currentFrameEffect,\n snapTime,\n });\n if (element.timelineType === \"scene\") {\n await addBackgroundColor({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n }\n break;\n case ELEMENT_TYPES.IMAGE:\n await addImageElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n if (element.timelineType === \"scene\") {\n await addBackgroundColor({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n }\n break;\n case ELEMENT_TYPES.RECT:\n await addRectElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n case ELEMENT_TYPES.CIRCLE:\n await addCircleElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n case ELEMENT_TYPES.TEXT:\n await addTextElement({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n case ELEMENT_TYPES.CAPTION:\n await addCaptionElement({\n element,\n index,\n canvas: twickCanvas,\n captionProps,\n canvasMetadata: canvasMetadataRef.current,\n });\n break;\n default:\n break;\n }\n elementMap.current[element.id] = element;\n if (reorder) {\n reorderElementsByZIndex(twickCanvas);\n }\n };\n\n return {\n twickCanvas,\n buildCanvas,\n onVideoSizeChange,\n addElementToCanvas,\n setCanvasElements,\n };\n};\n"],"names":["FabricCanvas"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twick/canvas",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "0.15.0",
|
|
5
4
|
"main": "./dist/index.js",
|
|
6
5
|
"module": "./dist/index.mjs",
|
|
7
6
|
"types": "./dist/index.d.ts",
|
|
8
7
|
"exports": {
|
|
9
8
|
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
10
|
"import": "./dist/index.mjs",
|
|
11
|
-
"require": "./dist/index.js"
|
|
12
|
-
"types": "./dist/index.d.ts"
|
|
11
|
+
"require": "./dist/index.js"
|
|
13
12
|
}
|
|
14
13
|
},
|
|
15
14
|
"sideEffects": false,
|
|
@@ -20,6 +19,9 @@
|
|
|
20
19
|
"publishConfig": {
|
|
21
20
|
"access": "public"
|
|
22
21
|
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20.0.0"
|
|
24
|
+
},
|
|
23
25
|
"scripts": {
|
|
24
26
|
"build": "tsc --noEmit && vite build",
|
|
25
27
|
"dev": "vite build --watch",
|
|
@@ -28,15 +30,17 @@
|
|
|
28
30
|
"docs": "typedoc"
|
|
29
31
|
},
|
|
30
32
|
"dependencies": {
|
|
31
|
-
"@twick/media-utils": "0.
|
|
32
|
-
"fabric": "^6.6.2"
|
|
33
|
-
|
|
33
|
+
"@twick/media-utils": "^0.15.0",
|
|
34
|
+
"fabric": "^6.6.2"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
34
38
|
},
|
|
35
39
|
"devDependencies": {
|
|
36
40
|
"@types/fabric": "^5.3.7",
|
|
37
41
|
"@types/node": "^20.11.24",
|
|
38
|
-
"@types/react": "^18.0.0",
|
|
39
|
-
"@types/react-dom": "^18.0.0",
|
|
42
|
+
"@types/react": "^18.0.0 || ^19.0.0",
|
|
43
|
+
"@types/react-dom": "^18.0.0 || ^19.0.0",
|
|
40
44
|
"typescript": "^5.4.2",
|
|
41
45
|
"typedoc": "^0.25.8",
|
|
42
46
|
"typedoc-plugin-markdown": "^3.17.1",
|