@twick/canvas 0.15.20 → 0.15.21
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.d.ts +6 -0
- package/dist/index.js +170 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +171 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
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/elements/video.element.ts","../src/elements/image.element.ts","../src/elements/rect.element.ts","../src/elements/circle.element.ts","../src/elements/text.element.ts","../src/elements/caption.element.ts","../src/elements/watermark.element.ts","../src/controllers/element.controller.ts","../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 (matches highlight_bg default) */\n family: \"Bangers\",\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 /** Watermark has been updated */\n WATERMARK_UPDATED: \"WATERMARK_UPDATED\",\n /** A new element was added via drop on canvas; payload is { element } */\n ADDED_NEW_ELEMENT: \"ADDED_NEW_ELEMENT\",\n /** Z-order changed (bring to front / send to back). Payload is { elementId, direction }. Timeline should reorder tracks. */\n Z_ORDER_CHANGED: \"Z_ORDER_CHANGED\",\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 /** Icon element type */\n ICON: \"icon\",\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 * Measure the width of text using Canvas 2D measureText with the given font.\n * For multi-line text (with \\n), returns the width of the longest line.\n * Used to size Textbox to content when no explicit width is provided.\n *\n * @param text - The text to measure\n * @param options - Font options matching the text element (fontSize, fontFamily, etc.)\n * @returns The width in pixels of the longest line\n */\nexport function measureTextWidth(\n text: string,\n options: {\n fontSize: number;\n fontFamily: string;\n fontStyle?: string;\n fontWeight?: string | number;\n }\n): number {\n if (typeof document === \"undefined\" || !text) return 0;\n\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return 0;\n\n const {\n fontSize,\n fontFamily,\n fontStyle = \"normal\",\n fontWeight = \"normal\",\n } = options;\n ctx.font = `${fontStyle} ${String(fontWeight)} ${fontSize}px ${fontFamily}`;\n\n const lines = text.split(\"\\n\");\n let maxWidth = 0;\n for (const line of lines) {\n const { width } = ctx.measureText(line);\n if (width > maxWidth) maxWidth = width;\n }\n return Math.ceil(maxWidth);\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 // 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 * Finds a Fabric object on the canvas by its element id (stored as custom \"id\" on the object).\n */\nexport const getCanvasObjectById = (\n canvas: FabricCanvas | null | undefined,\n elementId: string\n): import(\"fabric\").FabricObject | undefined => {\n if (!canvas) return undefined;\n const objects = canvas.getObjects();\n return objects.find((obj) => (obj as any).get?.(\"id\") === elementId);\n};\n\nexport type ZOrderDirection = \"front\" | \"back\" | \"forward\" | \"backward\";\n\n/**\n * Changes the z-order of the object with the given element id and reorders the canvas.\n * Returns the new zIndex for the moved object, or null if not found.\n */\nexport const changeZOrder = (\n canvas: FabricCanvas | null | undefined,\n elementId: string,\n direction: ZOrderDirection\n): number | null => {\n if (!canvas) return null;\n const objects = canvas.getObjects();\n const sorted = [...objects].sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0));\n const idx = sorted.findIndex((obj) => (obj as any).get?.(\"id\") === elementId);\n if (idx < 0) return null;\n\n const minZ = sorted[0]?.zIndex ?? 0;\n const maxZ = sorted[sorted.length - 1]?.zIndex ?? 0;\n const obj = sorted[idx] as any;\n\n if (direction === \"front\") {\n obj.set(\"zIndex\", maxZ + 1);\n reorderElementsByZIndex(canvas);\n return maxZ + 1;\n }\n if (direction === \"back\") {\n obj.set(\"zIndex\", minZ - 1);\n reorderElementsByZIndex(canvas);\n return minZ - 1;\n }\n if (direction === \"forward\" && idx < sorted.length - 1) {\n const next = sorted[idx + 1] as any;\n const myZ = obj.zIndex ?? idx;\n const nextZ = next.zIndex ?? idx + 1;\n obj.set(\"zIndex\", nextZ);\n next.set(\"zIndex\", myZ);\n reorderElementsByZIndex(canvas);\n return nextZ;\n }\n if (direction === \"backward\" && idx > 0) {\n const prev = sorted[idx - 1] as any;\n const myZ = obj.zIndex ?? idx;\n const prevZ = prev.zIndex ?? idx - 1;\n obj.set(\"zIndex\", prevZ);\n prev.set(\"zIndex\", myZ);\n reorderElementsByZIndex(canvas);\n return prevZ;\n }\n return obj.zIndex ?? idx;\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 * Returns the object's center position in canvas coordinates.\n * For objects inside a Group/ActiveSelection, left/top are group-relative; this uses\n * getCenterPoint() to get the correct absolute canvas position (same as individual items).\n *\n * @param obj - Fabric object (standalone or inside a group)\n * @returns { x, y } in canvas space\n */\nexport const getObjectCanvasCenter = (obj: { left?: number; top?: number; getCenterPoint?: () => { x: number; y: number } }): { x: number; y: number } => {\n if (obj.getCenterPoint) {\n const p = obj.getCenterPoint();\n return { x: p.x, y: p.y };\n }\n return { x: obj.left ?? 0, y: obj.top ?? 0 };\n};\n\n/**\n * Returns the object's angle in canvas space (degrees).\n * For objects inside a Group/ActiveSelection, object.angle is group-relative; this uses\n * getTotalAngle() to get the correct absolute canvas angle (same as individual items).\n *\n * @param obj - Fabric object (standalone or inside a group)\n * @returns angle in degrees\n */\nexport const getObjectCanvasAngle = (obj: { angle?: number; getTotalAngle?: () => number }): number => {\n if (typeof obj.getTotalAngle === \"function\") {\n return obj.getTotalAngle();\n }\n return obj.angle ?? 0;\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 * Converts dimensions from canvas (Fabric) space to video space.\n * Uses the object's actual displayed size (width*scaleX, height*scaleY) so the\n * result matches what's on canvas even after move or when element.props is stale.\n */\nexport const convertToVideoDimensions = (\n widthCanvas: number,\n heightCanvas: number,\n canvasMetadata: CanvasMetadata\n): Dimensions => {\n return {\n width: Number((widthCanvas / canvasMetadata.scaleX).toFixed(2)),\n height: Number((heightCanvas / canvasMetadata.scaleY).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\n/**\n * Converts a hex color string to rgba with the given alpha.\n * Handles both 3-digit and 6-digit hex color formats.\n *\n * @param hex - The hex color string (e.g. \"#ff0000\" or \"#f00\")\n * @param alpha - Opacity value between 0 and 1\n * @returns CSS rgba string (e.g. \"rgba(255, 0, 0, 0.8)\")\n */\nexport const hexToRgba = (hex: string, alpha: number): string => {\n const color = hex.replace(/^#/, \"\");\n const fullHex =\n color.length === 3\n ? color\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : color;\n if (fullHex.length !== 6) {\n return hex;\n }\n const r = parseInt(fullHex.slice(0, 2), 16);\n const g = parseInt(fullHex.slice(2, 4), 16);\n const b = parseInt(fullHex.slice(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\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 isSafeUrl = /^(https?:|blob:|data:video\\/)/i.test(videoSrc);\n if (!isSafeUrl) {\n reject(new Error(\"Unsafe video source URL\"));\n return;\n }\n const tryLoadVideo = (useCors) => {\n const video = document.createElement(\"video\");\n video.preload = \"metadata\";\n video.crossOrigin = useCors ? \"anonymous\" : null;\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 = () => {\n if (useCors) {\n video.src = \"\";\n tryLoadVideo(false);\n } else {\n reject(new Error(\"Failed to load video metadata. This may be due to CORS restrictions or an invalid video URL.\"));\n }\n };\n };\n tryLoadVideo(true);\n });\n};\n\nconst getThumbnail = async (videoUrl, seekTime = 0.1, playbackRate = 1) => {\n return new Promise((resolve, reject) => {\n const tryLoadThumbnail = (useCors) => {\n const video = document.createElement(\"video\");\n video.crossOrigin = useCors ? \"anonymous\" : null;\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 if (useCors) {\n tryLoadThumbnail(false);\n } else {\n reject(new Error(`Failed to load video: ${video.error?.message || \"Unknown error. This may be due to CORS restrictions or an invalid video URL.\"}`));\n }\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 || 0.1;\n }).catch(() => {\n video.currentTime = seekTime || 0.1;\n });\n } else {\n video.currentTime = seekTime || 0.1;\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 tryLoadThumbnail(true);\n });\n};\n\nclass LRUCache {\n constructor(maxSize = 100) {\n if (maxSize <= 0) {\n throw new Error(\"maxSize must be greater than 0\");\n }\n this.maxSize = maxSize;\n this.cache = /* @__PURE__ */ new Map();\n }\n /**\n * Get a value from the cache.\n * Moves the item to the end (most recently used).\n */\n get(key) {\n const value = this.cache.get(key);\n if (value === void 0) {\n return void 0;\n }\n this.cache.delete(key);\n this.cache.set(key, value);\n return value;\n }\n /**\n * Set a value in the cache.\n * If cache is full, removes the least recently used item.\n */\n set(key, value) {\n if (this.cache.has(key)) {\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxSize) {\n const firstKey = this.cache.keys().next().value;\n if (firstKey !== void 0) {\n this.cache.delete(firstKey);\n }\n }\n this.cache.set(key, value);\n }\n /**\n * Check if a key exists in the cache.\n */\n has(key) {\n return this.cache.has(key);\n }\n /**\n * Delete a key from the cache.\n */\n delete(key) {\n return this.cache.delete(key);\n }\n /**\n * Clear all entries from the cache.\n */\n clear() {\n this.cache.clear();\n }\n /**\n * Get the current size of the cache.\n */\n get size() {\n return this.cache.size;\n }\n}\n\nclass VideoFrameExtractor {\n constructor(options = {}) {\n this.frameCache = new LRUCache(\n options.maxCacheSize ?? 50\n );\n this.videoElements = /* @__PURE__ */ new Map();\n this.maxVideoElements = options.maxVideoElements ?? 5;\n this.loadTimeout = options.loadTimeout ?? 15e3;\n this.jpegQuality = options.jpegQuality ?? 0.8;\n this.playbackRate = options.playbackRate ?? 1;\n }\n /**\n * Get a frame thumbnail from a video at a specific time.\n * Uses caching and reuses video elements for optimal performance.\n * Uses 0.1s instead of 0 when seekTime is 0, since frames at t=0 are often blank.\n *\n * @param videoUrl - The URL of the video\n * @param seekTime - The time in seconds to extract the frame (0 is treated as 0.1)\n * @returns Promise resolving to a thumbnail image URL (data URL or blob URL)\n */\n async getFrame(videoUrl, seekTime = 0.1) {\n const effectiveSeekTime = seekTime === 0 ? 0.1 : seekTime;\n const cacheKey = this.getCacheKey(videoUrl, effectiveSeekTime);\n const cached = this.frameCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n const videoState = await this.getVideoElement(videoUrl);\n const thumbnail = await this.extractFrame(videoState.video, effectiveSeekTime);\n this.frameCache.set(cacheKey, thumbnail);\n return thumbnail;\n }\n /**\n * Get or create a video element for the given URL.\n * Reuses existing elements and manages cleanup.\n */\n async getVideoElement(videoUrl) {\n let videoState = this.videoElements.get(videoUrl);\n if (videoState && videoState.isReady) {\n videoState.lastUsed = Date.now();\n return videoState;\n }\n if (videoState && videoState.isLoading && videoState.loadPromise) {\n await videoState.loadPromise;\n if (videoState.isReady) {\n videoState.lastUsed = Date.now();\n return videoState;\n }\n }\n if (this.videoElements.size >= this.maxVideoElements) {\n this.cleanupOldVideoElements();\n }\n videoState = await this.createVideoElement(videoUrl);\n this.videoElements.set(videoUrl, videoState);\n videoState.lastUsed = Date.now();\n return videoState;\n }\n /**\n * Create and initialize a new video element.\n */\n async createVideoElement(videoUrl) {\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 = this.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 const state = {\n video,\n isReady: false,\n isLoading: true,\n loadPromise: null,\n lastUsed: Date.now()\n };\n state.loadPromise = new Promise((resolve, reject) => {\n let timeoutId;\n const cleanup = () => {\n if (timeoutId) clearTimeout(timeoutId);\n };\n const handleError = () => {\n cleanup();\n state.isLoading = false;\n reject(new Error(`Failed to load video: ${video.error?.message || \"Unknown error\"}`));\n };\n const handleLoadedMetadata = () => {\n cleanup();\n state.isReady = true;\n state.isLoading = false;\n resolve();\n };\n video.addEventListener(\"error\", handleError, { once: true });\n video.addEventListener(\"loadedmetadata\", handleLoadedMetadata, { once: true });\n timeoutId = window.setTimeout(() => {\n cleanup();\n state.isLoading = false;\n reject(new Error(\"Video loading timed out\"));\n }, this.loadTimeout);\n video.src = videoUrl;\n document.body.appendChild(video);\n });\n try {\n await state.loadPromise;\n } catch (error) {\n if (video.parentNode) {\n video.remove();\n }\n throw error;\n }\n return state;\n }\n /**\n * Extract a frame from a video at the specified time.\n */\n async extractFrame(video, seekTime) {\n return new Promise((resolve, reject) => {\n video.pause();\n const timeThreshold = 0.1;\n if (Math.abs(video.currentTime - seekTime) < timeThreshold) {\n try {\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 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\", this.jpegQuality);\n resolve(dataUrl);\n } catch {\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error(\"Failed to create Blob\"));\n return;\n }\n const blobUrl = URL.createObjectURL(blob);\n resolve(blobUrl);\n },\n \"image/jpeg\",\n this.jpegQuality\n );\n }\n return;\n } catch (err) {\n reject(new Error(`Error creating thumbnail: ${err}`));\n return;\n }\n }\n const handleSeeked = () => {\n try {\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 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\", this.jpegQuality);\n resolve(dataUrl);\n } catch {\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error(\"Failed to create Blob\"));\n return;\n }\n const blobUrl = URL.createObjectURL(blob);\n resolve(blobUrl);\n },\n \"image/jpeg\",\n this.jpegQuality\n );\n }\n } catch (err) {\n reject(new Error(`Error creating thumbnail: ${err}`));\n }\n };\n video.addEventListener(\"seeked\", handleSeeked, { once: true });\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 });\n }\n /**\n * Generate cache key for a video URL and seek time.\n */\n getCacheKey(videoUrl, seekTime) {\n const roundedTime = Math.round(seekTime * 100) / 100;\n return `${videoUrl}:${roundedTime}`;\n }\n /**\n * Cleanup least recently used video elements.\n */\n cleanupOldVideoElements() {\n if (this.videoElements.size < this.maxVideoElements) {\n return;\n }\n const entries = Array.from(this.videoElements.entries());\n entries.sort((a, b) => a[1].lastUsed - b[1].lastUsed);\n const toRemove = entries.slice(0, entries.length - this.maxVideoElements + 1);\n for (const [url, state] of toRemove) {\n if (state.video.parentNode) {\n state.video.remove();\n }\n this.videoElements.delete(url);\n }\n }\n /**\n * Clear the frame cache.\n */\n clearCache() {\n this.frameCache.clear();\n }\n /**\n * Remove a specific video element and clear its cached frames.\n */\n removeVideo(videoUrl) {\n const state = this.videoElements.get(videoUrl);\n if (state) {\n if (state.video.parentNode) {\n state.video.remove();\n }\n this.videoElements.delete(videoUrl);\n }\n this.frameCache.clear();\n }\n /**\n * Dispose of all video elements and clear caches.\n * Removes all video elements from the DOM and clears both the frame cache\n * and video element cache. Call this when the extractor is no longer needed\n * to prevent memory leaks.\n */\n dispose() {\n for (const state of this.videoElements.values()) {\n if (state.video.parentNode) {\n state.video.remove();\n }\n }\n this.videoElements.clear();\n this.frameCache.clear();\n }\n}\nlet defaultExtractor = null;\nfunction getDefaultVideoFrameExtractor(options) {\n if (!defaultExtractor) {\n defaultExtractor = new VideoFrameExtractor(options);\n }\n return defaultExtractor;\n}\nasync function getThumbnailCached(videoUrl, seekTime = 0.1, playbackRate) {\n const extractor = getDefaultVideoFrameExtractor(\n playbackRate !== void 0 ? { playbackRate } : void 0\n );\n return extractor.getFrame(videoUrl, seekTime);\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) continue;\n const volume = segment.volume ?? 1;\n if (volume <= 0) continue;\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 {\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 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 {\n return null;\n }\n};\n\nexport { VideoFrameExtractor, blobUrlToFile, detectMediaTypeFromUrl, downloadFile, extractAudio, getAudioDuration, getDefaultVideoFrameExtractor, getImageDimensions, getObjectFitSize, getScaledDimensions, getThumbnail, getThumbnailCached, getVideoMeta, hasAudio, limit, loadFile, saveAsFile, stitchAudio };\n//# sourceMappingURL=index.mjs.map\n","import {\n Canvas as FabricCanvas,\n Textbox,\n Group,\n FabricImage,\n Rect,\n Circle,\n Shadow,\n} from \"fabric\";\nimport {\n convertToCanvasPosition,\n hexToRgba,\n measureTextWidth,\n} 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, getThumbnailCached } from \"@twick/media-utils\";\n\nconst MARGIN = 10;\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 const fontSize = Math.floor(\n (element.props?.fontSize || DEFAULT_TEXT_PROPS.size) *\n canvasMetadata.scaleX\n );\n const fontFamily = element.props?.fontFamily || DEFAULT_TEXT_PROPS.family;\n const fontStyle = element.props?.fontStyle || \"normal\";\n const fontWeight = element.props?.fontWeight || \"normal\";\n\n let width: number;\n if (element.props?.width != null && element.props.width > 0) {\n width = element.props.width * canvasMetadata.scaleX;\n if (element.props?.maxWidth) {\n width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);\n }\n } else {\n const textContent = element.props?.text ?? element.t ?? \"\";\n width = measureTextWidth(textContent, {\n fontSize,\n fontFamily,\n fontStyle,\n fontWeight,\n });\n const padding = 4;\n width = width + padding * 2;\n if (element.props?.maxWidth) {\n width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);\n }\n if (width <= 0) width = 100;\n }\n\n const backgroundColor = element.props?.backgroundColor\n ? hexToRgba(\n element.props.backgroundColor,\n element.props?.backgroundOpacity ?? 1\n )\n : undefined;\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,\n fontFamily,\n fontStyle,\n fontWeight,\n fill: element.props?.fill || DEFAULT_TEXT_PROPS.fill,\n opacity: element.props?.opacity ?? 1,\n 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 ...(backgroundColor && { backgroundColor }),\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 (text is not resizable on canvas)\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 lockAspectRatio = true,\n}: {\n img: FabricImage;\n element: CanvasElement;\n index: number;\n canvasMetadata: CanvasMetadata;\n lockAspectRatio?: boolean;\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 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 img.set(\"lockUniScaling\", lockAspectRatio);\n};\n\n/**\n * Add a caption element to the canvas based on provided props.\n * Creates a Fabric.js Textbox with caption-specific styling including\n * shadows, positioning, font properties, and text wrapping (same as text element).\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 Textbox caption object with wrapping enabled\n *\n * @example\n * ```js\n * const captionElement = addCaptionElement({\n * element: { id: \"caption1\", props: { text: \"Caption\", x: 100, y: 100, width: 200 } },\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 lockAspectRatio = false,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n captionProps: CaptionProps;\n canvasMetadata: CanvasMetadata;\n lockAspectRatio?: boolean;\n}) => {\n const applyToAll = captionProps?.applyToAll ?? false;\n const captionTextColor =\n captionProps?.colors?.text ?? captionProps?.color?.text;\n\n const { x, y } = convertToCanvasPosition(\n (applyToAll ? captionProps?.x : element.props?.x ?? captionProps?.x) ?? 0,\n (applyToAll ? captionProps?.y : element.props?.y ?? captionProps?.y) ?? 0,\n canvasMetadata\n );\n\n let width = element.props?.width ? element.props.width * canvasMetadata.scaleX : canvasMetadata.width - (2 * MARGIN);\n if (element.props?.maxWidth) {\n width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);\n }\n\n const elementColors = (element.props as { colors?: { text?: string } })?.colors;\n const resolvedFill =\n (applyToAll\n ? captionTextColor\n : element.props?.fill ?? elementColors?.text ?? captionTextColor) ??\n DEFAULT_CAPTION_PROPS.fill;\n\n const caption = 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.round(\n ((applyToAll\n ? captionProps?.font?.size\n : element.props?.font?.size ?? captionProps?.font?.size) ??\n DEFAULT_CAPTION_PROPS.size) * canvasMetadata.scaleX\n ),\n fontFamily:\n (applyToAll\n ? captionProps?.font?.family\n : element.props?.font?.family ?? captionProps?.font?.family) ??\n DEFAULT_CAPTION_PROPS.family,\n fill: resolvedFill,\n fontWeight: (applyToAll\n ? captionProps?.font?.weight\n : element.props?.fontWeight ?? captionProps?.font?.weight) ??\n DEFAULT_CAPTION_PROPS.fontWeight,\n stroke: (applyToAll\n ? captionProps?.stroke\n : element.props?.stroke ?? captionProps?.stroke) ??\n DEFAULT_CAPTION_PROPS.stroke,\n opacity: (applyToAll\n ? captionProps?.opacity\n : element.props?.opacity ?? captionProps?.opacity) ?? 1,\n width,\n splitByGrapheme: false,\n textAlign: element.props?.textAlign ?? \"center\",\n shadow: new Shadow({\n offsetX:\n (applyToAll\n ? captionProps?.shadowOffset?.[0]\n : element.props?.shadowOffset?.[0] ?? captionProps?.shadowOffset?.[0]) ??\n DEFAULT_CAPTION_PROPS.shadowOffset?.[0],\n offsetY:\n (applyToAll\n ? captionProps?.shadowOffset?.[1]\n : element.props?.shadowOffset?.[1] ?? captionProps?.shadowOffset?.[1]) ??\n DEFAULT_CAPTION_PROPS.shadowOffset?.[1],\n blur: (applyToAll\n ? captionProps?.shadowBlur\n : element.props?.shadowBlur ?? captionProps?.shadowBlur) ?? DEFAULT_CAPTION_PROPS.shadowBlur,\n color: (applyToAll\n ? captionProps?.shadowColor\n : element.props?.shadowColor ?? captionProps?.shadowColor) ?? DEFAULT_CAPTION_PROPS.shadowColor,\n }),\n strokeWidth: (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 caption.set(\"lockUniScaling\", lockAspectRatio);\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 getThumbnailCached(\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 {\n // Skip element on load 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 lockAspectRatio = true,\n}: {\n imageUrl?: string;\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n /** When true, resize keeps aspect ratio (uniform scaling). Default true for images. */\n lockAspectRatio?: boolean;\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: lockAspectRatio,\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 lockAspectRatio,\n });\n } else {\n setImageProps({ img, element, index, canvasMetadata, lockAspectRatio });\n canvas.add(img);\n return img;\n }\n } catch {\n // Skip element on load 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 lockAspectRatio = true,\n}: {\n element: CanvasElement;\n img: FabricImage;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n lockAspectRatio?: boolean;\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 group.set(\"lockUniScaling\", lockAspectRatio);\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 lockAspectRatio = false,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n lockAspectRatio?: boolean;\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 rect.set(\"lockUniScaling\", lockAspectRatio);\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 lockAspectRatio = true,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n lockAspectRatio?: boolean;\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 // Respect element opacity (0–1). Defaults to fully opaque.\n opacity: element.props?.opacity ?? 1,\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 circle.set(\"lockUniScaling\", lockAspectRatio);\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 type { CanvasElementHandler } from \"../types\";\nimport { addVideoElement, addBackgroundColor } from \"../components/elements\";\nimport { convertToVideoPosition, convertToVideoDimensions, getObjectCanvasCenter, getObjectCanvasAngle } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\n\nexport const VideoElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.VIDEO,\n\n async add(params) {\n const {\n element,\n index,\n canvas,\n canvasMetadata,\n seekTime = 0,\n elementFrameMapRef,\n getCurrentFrameEffect: getFrameEffect,\n } = params;\n if (!getFrameEffect || !elementFrameMapRef) return;\n\n const currentFrameEffect = getFrameEffect(element, seekTime);\n elementFrameMapRef.current[element.id] = currentFrameEffect;\n\n const snapTime =\n (seekTime - (element?.s ?? 0)) * (element?.props?.playbackRate ?? 1) +\n (element?.props?.time ?? 0);\n\n await addVideoElement({\n element,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n snapTime,\n });\n\n if (element.timelineType === \"scene\") {\n await addBackgroundColor({\n element,\n index,\n canvas,\n canvasMetadata,\n });\n }\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n const scaledW = (object.width ?? 0) * (object.scaleX ?? 1);\n const scaledH = (object.height ?? 0) * (object.scaleY ?? 1);\n const { width: fw, height: fh } = convertToVideoDimensions(\n scaledW,\n scaledH,\n context.canvasMetadata\n );\n const updatedFrameSize: [number, number] = [fw, fh];\n const currentFrameEffect = context.elementFrameMapRef.current[element.id];\n\n if (currentFrameEffect) {\n context.elementFrameMapRef.current[element.id] = {\n ...currentFrameEffect,\n props: {\n ...currentFrameEffect.props,\n framePosition: { x, y },\n frameSize: updatedFrameSize,\n },\n };\n return {\n element: {\n ...element,\n frameEffects: (element.frameEffects || []).map((fe: any) =>\n (fe as { id?: string }).id === (currentFrameEffect as { id?: string })?.id\n ? {\n ...fe,\n props: {\n ...fe.props,\n framePosition: { x, y },\n frameSize: updatedFrameSize,\n },\n }\n : fe\n ),\n },\n };\n }\n\n const frame = element.frame!;\n return {\n element: {\n ...element,\n frame: {\n ...frame,\n rotation: getObjectCanvasAngle(object),\n size: updatedFrameSize,\n x,\n y,\n },\n },\n };\n },\n};\n","import type { CanvasElementHandler } from \"../types\";\nimport { addImageElement, addBackgroundColor } from \"../components/elements\";\nimport { convertToVideoPosition, convertToVideoDimensions, getObjectCanvasCenter, getObjectCanvasAngle } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\n\nexport const ImageElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.IMAGE,\n\n async add(params) {\n const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;\n await addImageElement({\n element,\n index,\n canvas,\n canvasMetadata,\n lockAspectRatio: lockAspectRatio ?? element.props?.lockAspectRatio,\n });\n if (element.timelineType === \"scene\") {\n await addBackgroundColor({\n element,\n index,\n canvas,\n canvasMetadata,\n });\n }\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n const currentFrameEffect = context.elementFrameMapRef.current[element.id];\n\n if (object.type === \"group\") {\n const scaledW = (object.width ?? 0) * (object.scaleX ?? 1);\n const scaledH = (object.height ?? 0) * (object.scaleY ?? 1);\n const { width: fw, height: fh } = convertToVideoDimensions(\n scaledW,\n scaledH,\n context.canvasMetadata\n );\n const updatedFrameSize: [number, number] = [fw, fh];\n if (currentFrameEffect) {\n context.elementFrameMapRef.current[element.id] = {\n ...currentFrameEffect,\n props: {\n ...currentFrameEffect.props,\n framePosition: { x, y },\n frameSize: updatedFrameSize,\n },\n };\n return {\n element: {\n ...element,\n // Keep the base frame in sync with the active frame effect\n // so visualizer `Rect {...element.frame}` reflects the same size/position.\n frame: element.frame\n ? {\n ...element.frame,\n rotation: getObjectCanvasAngle(object),\n size: updatedFrameSize,\n x,\n y,\n }\n : element.frame,\n frameEffects: (element.frameEffects || []).map((fe: any) =>\n (fe as { id?: string }).id === (currentFrameEffect as { id?: string })?.id\n ? {\n ...fe,\n props: {\n ...fe.props,\n framePosition: { x, y },\n frameSize: updatedFrameSize,\n },\n }\n : fe\n ),\n },\n };\n }\n const frame = element.frame!;\n return {\n element: {\n ...element,\n frame: {\n ...frame,\n rotation: getObjectCanvasAngle(object),\n size: updatedFrameSize,\n x,\n y,\n },\n },\n };\n }\n\n // Use Fabric's actual displayed size so live player matches canvas after resize+move\n const scaledW = (object.width ?? 0) * (object.scaleX ?? 1);\n const scaledH = (object.height ?? 0) * (object.scaleY ?? 1);\n const { width, height } = convertToVideoDimensions(\n scaledW,\n scaledH,\n context.canvasMetadata\n );\n return {\n element: {\n ...element,\n props: {\n ...element.props,\n rotation: getObjectCanvasAngle(object),\n width,\n height,\n x,\n y,\n },\n },\n };\n },\n};\n","import type { CanvasElementHandler } from \"../types\";\nimport { addRectElement } from \"../components/elements\";\nimport { convertToVideoPosition, getObjectCanvasCenter, getObjectCanvasAngle } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\n\nexport const RectElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.RECT,\n\n async add(params) {\n const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;\n await addRectElement({\n element,\n index,\n canvas,\n canvasMetadata,\n lockAspectRatio: lockAspectRatio ?? element.props?.lockAspectRatio,\n });\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n return {\n element: {\n ...element,\n props: {\n ...element.props,\n rotation: getObjectCanvasAngle(object),\n width: (element.props?.width ?? 0) * object.scaleX,\n height: (element.props?.height ?? 0) * object.scaleY,\n x,\n y,\n },\n },\n };\n },\n};\n","import type { CanvasElementHandler } from \"../types\";\nimport { addCircleElement } from \"../components/elements\";\nimport { convertToVideoPosition, getObjectCanvasCenter, getObjectCanvasAngle } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\n\nexport const CircleElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.CIRCLE,\n\n async add(params) {\n const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;\n await addCircleElement({\n element,\n index,\n canvas,\n canvasMetadata,\n lockAspectRatio: lockAspectRatio ?? element.props?.lockAspectRatio,\n });\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n const radius = Number(\n ((element.props?.radius ?? 0) * object.scaleX).toFixed(2)\n );\n const opacity = object.opacity != null ? object.opacity : element.props?.opacity;\n return {\n element: {\n ...element,\n props: {\n ...element.props,\n rotation: getObjectCanvasAngle(object),\n radius,\n height: radius * 2,\n width: radius * 2,\n x,\n y,\n ...(opacity != null && { opacity }),\n },\n },\n };\n },\n};\n","import type { CanvasElementHandler } from \"../types\";\nimport { addTextElement } from \"../components/elements\";\nimport { convertToVideoPosition, getObjectCanvasCenter, getObjectCanvasAngle } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\n\nexport const TextElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.TEXT,\n\n async add(params) {\n const { element, index, canvas, canvasMetadata } = params;\n await addTextElement({\n element,\n index,\n canvas,\n canvasMetadata,\n });\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n return {\n element: {\n ...element,\n props: {\n ...element.props,\n rotation: getObjectCanvasAngle(object),\n x,\n y,\n },\n },\n };\n },\n};\n","import type { CanvasElementHandler } from \"../types\";\nimport { addCaptionElement } from \"../components/elements\";\nimport { convertToVideoPosition, getObjectCanvasCenter } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\nimport { CANVAS_OPERATIONS } from \"../helpers/constants\";\n\nexport const CaptionElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.CAPTION,\n\n async add(params) {\n const { element, index, canvas, captionProps, canvasMetadata, lockAspectRatio } = params;\n await addCaptionElement({\n element,\n index,\n canvas,\n captionProps: (captionProps ?? {}) as import(\"../types\").CaptionProps,\n canvasMetadata,\n lockAspectRatio: lockAspectRatio ?? element.props?.lockAspectRatio,\n });\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n if (context.captionPropsRef.current?.applyToAll) {\n return {\n element,\n operation: CANVAS_OPERATIONS.CAPTION_PROPS_UPDATED,\n payload: {\n element,\n props: {\n ...context.captionPropsRef.current,\n x,\n y,\n },\n },\n };\n }\n return {\n element: {\n ...element,\n props: {\n ...element.props,\n x,\n y,\n },\n },\n };\n },\n};\n","import type { CanvasElementHandler, WatermarkUpdatePayload } from \"../types\";\nimport { addTextElement, addImageElement } from \"../components/elements\";\nimport { convertToVideoPosition } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\nimport { CANVAS_OPERATIONS } from \"../helpers/constants\";\n\n/**\n * Watermark handler: add() supports element.type \"text\" | \"image\".\n * updateFromFabricObject() returns canonical WatermarkUpdatePayload for\n * WATERMARK_UPDATED so video-editor can persist via setWatermark().\n */\nexport const WatermarkElement: CanvasElementHandler = {\n name: \"watermark\",\n\n async add(params) {\n const { element, index, canvas, canvasMetadata, watermarkPropsRef } = params;\n if (element.type === ELEMENT_TYPES.TEXT) {\n if (watermarkPropsRef) watermarkPropsRef.current = element.props;\n await addTextElement({\n element,\n index,\n canvas,\n canvasMetadata,\n });\n } else if (element.type === ELEMENT_TYPES.IMAGE) {\n await addImageElement({\n element,\n index,\n canvas,\n canvasMetadata,\n });\n }\n },\n\n updateFromFabricObject(object, element, context) {\n const { x, y } = convertToVideoPosition(\n object.left,\n object.top,\n context.canvasMetadata,\n context.videoSize\n );\n const rotation = object.angle != null ? object.angle : undefined;\n const opacity = object.opacity != null ? object.opacity : undefined;\n // Preserve existing props; text uses watermarkPropsRef, image uses element.props + scale\n const baseProps =\n element.type === ELEMENT_TYPES.TEXT\n ? context.watermarkPropsRef.current ?? element.props ?? {}\n : { ...element.props };\n const props =\n element.type === ELEMENT_TYPES.IMAGE && (object.scaleX != null || object.scaleY != null)\n ? {\n ...baseProps,\n width: baseProps.width != null && object.scaleX != null ? baseProps.width * object.scaleX : baseProps.width,\n height: baseProps.height != null && object.scaleY != null ? baseProps.height * object.scaleY : baseProps.height,\n }\n : baseProps;\n\n const payload: WatermarkUpdatePayload = {\n position: { x, y },\n ...(rotation != null && { rotation }),\n ...(opacity != null && { opacity }),\n ...(Object.keys(props).length > 0 && { props }),\n };\n\n return {\n element: { ...element, props: { ...element.props, x, y, rotation, opacity, ...props } },\n operation: CANVAS_OPERATIONS.WATERMARK_UPDATED,\n payload,\n };\n },\n};\n","import type { CanvasElementHandler } from \"../types\";\nimport { VideoElement } from \"../elements/video.element\";\nimport { ImageElement } from \"../elements/image.element\";\nimport { RectElement } from \"../elements/rect.element\";\nimport { CircleElement } from \"../elements/circle.element\";\nimport { TextElement } from \"../elements/text.element\";\nimport { CaptionElement } from \"../elements/caption.element\";\nimport { WatermarkElement } from \"../elements/watermark.element\";\n\n/**\n * Registry for canvas element handlers. Enables scalable dispatch by type:\n * elementController.get(element.type)?.add(params) and updateFromFabricObject(...).\n */\nexport class ElementController {\n private elements = new Map<string, CanvasElementHandler>();\n\n register(handler: CanvasElementHandler) {\n this.elements.set(handler.name, handler);\n }\n\n get(name: string): CanvasElementHandler | undefined {\n return this.elements.get(name);\n }\n\n list(): string[] {\n return Array.from(this.elements.keys());\n }\n}\n\nconst elementController = new ElementController();\n\nfunction registerElements() {\n elementController.register(VideoElement);\n elementController.register(ImageElement);\n elementController.register(RectElement);\n elementController.register(CircleElement);\n elementController.register(TextElement);\n elementController.register(CaptionElement);\n elementController.register(WatermarkElement);\n}\n\nregisterElements();\n\nexport default elementController;\n","import { useCallback, useRef, useState } from \"react\";\nimport { Canvas as FabricCanvas, FabricObject, Textbox, ActiveSelection } from \"fabric\";\nimport { Dimensions } from \"@twick/media-utils\";\nimport {\n CanvasMetadata,\n CanvasElement,\n CaptionProps,\n BuildCanvasOptions,\n AddElementToCanvasOptions,\n SetCanvasElementsOptions,\n AddWatermarkToCanvasOptions,\n ResizeCanvasOptions,\n} from \"../types\";\nimport {\n changeZOrder,\n clearCanvas,\n createCanvas,\n getCanvasContext,\n getCurrentFrameEffect,\n reorderElementsByZIndex,\n} from \"../helpers/canvas.util\";\nimport { CANVAS_OPERATIONS } from \"../helpers/constants\";\nimport elementController from \"../controllers/element.controller\";\nimport { disabledControl, rotateControl } from \"../components/element-controls\";\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 * When true, holding Shift while dragging an object will lock movement to\n * the dominant axis (horizontal or vertical). This mirrors behavior in\n * professional editors and improves precise alignment.\n *\n * Default: false (opt‑in to avoid surprising existing consumers).\n */\n enableShiftAxisLock = false,\n}: {\n onCanvasReady?: (canvas: FabricCanvas) => void;\n onCanvasOperation?: (operation: string, data: any) => void;\n enableShiftAxisLock?: boolean;\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 watermarkPropsRef = useRef<any | null>(null);\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 axisLockStateRef = useRef<\n | null\n | {\n /** \"x\" when movement is locked horizontally, \"y\" when locked vertically */\n axis: \"x\" | \"y\";\n }\n >(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 * Resizes the canvas to match new container dimensions.\n * Updates Fabric canvas dimensions and metadata (scaleX, scaleY) without\n * recreating the canvas. Caller should refresh elements (e.g. setCanvasElements\n * with cleanAndAdd) after resize so they are re-positioned with the new scale.\n *\n * @param canvasSize - New canvas dimensions (e.g. from ResizeObserver)\n *\n * @example\n * ```js\n * resizeCanvas({ width: 800, height: 600 });\n * setCanvasElements({ elements, cleanAndAdd: true });\n * ```\n */\n const resizeCanvas = ({\n canvasSize,\n videoSize = videoSizeRef.current,\n }: ResizeCanvasOptions) => {\n const canvas = twickCanvasRef.current;\n if (!canvas || !getCanvasContext(canvas)) return;\n if (!videoSize?.width || !videoSize?.height) return;\n if (\n canvasResolutionRef.current.width === canvasSize.width &&\n canvasResolutionRef.current.height === canvasSize.height\n ) {\n return;\n }\n\n canvasMetadataRef.current = {\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 canvas.setDimensions({\n width: canvasSize.width,\n height: canvasSize.height,\n });\n canvasResolutionRef.current = canvasSize;\n canvas.requestRenderAll();\n };\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 * Handles object moving events on the canvas.\n * When Shift is held (and axis lock is enabled), restricts movement to the\n * dominant axis based on the initial drag direction.\n */\n const handleObjectMoving = (event: any) => {\n if (!enableShiftAxisLock) return;\n const target: FabricObject | undefined = event?.target;\n const transform = event?.transform;\n const pointerEvent = event?.e as MouseEvent | PointerEvent | undefined;\n\n if (!target || !transform || !pointerEvent) {\n axisLockStateRef.current = null;\n return;\n }\n\n // If Shift is not pressed, do not constrain movement.\n if (!pointerEvent.shiftKey) {\n axisLockStateRef.current = null;\n return;\n }\n\n const original = transform.original;\n if (!original || typeof target.left !== \"number\" || typeof target.top !== \"number\") {\n axisLockStateRef.current = null;\n return;\n }\n\n // Decide the dominant axis once for the drag operation.\n if (!axisLockStateRef.current) {\n const dx = Math.abs(target.left - original.left);\n const dy = Math.abs(target.top - original.top);\n axisLockStateRef.current = {\n axis: dx >= dy ? \"x\" : \"y\",\n };\n }\n\n if (axisLockStateRef.current.axis === \"x\") {\n // Lock vertical movement.\n target.top = original.top;\n } else {\n // Lock horizontal movement.\n target.left = original.left;\n }\n\n // Ensure the canvas reflects the updated coordinates.\n target.canvas?.requestRenderAll();\n };\n\n /**\n * Applies drag-only controls to marquee selection (ActiveSelection).\n * Restricts resize/scale while allowing drag and rotate, matching text element behavior.\n * Does not affect Media Groups (image+frame) which are Fabric Groups, not ActiveSelection.\n */\n const applyMarqueeSelectionControls = () => {\n const canvasInstance = twickCanvasRef.current;\n if (!canvasInstance) return;\n\n const activeObject = canvasInstance.getActiveObject();\n if (!activeObject) return;\n\n // Only modify ActiveSelection (marquee multi-select), not Media Groups or single objects\n if (activeObject instanceof ActiveSelection) {\n activeObject.controls.mt = disabledControl;\n activeObject.controls.mb = disabledControl;\n activeObject.controls.ml = disabledControl;\n activeObject.controls.mr = disabledControl;\n activeObject.controls.bl = disabledControl;\n activeObject.controls.br = disabledControl;\n activeObject.controls.tl = disabledControl;\n activeObject.controls.tr = disabledControl;\n activeObject.controls.mtr = rotateControl;\n canvasInstance.requestRenderAll();\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 }: BuildCanvasOptions) => {\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 if (twickCanvasRef.current) {\n twickCanvasRef.current.off(\"mouse:up\", handleMouseUp);\n twickCanvasRef.current.off(\"text:editing:exited\", onTextEdit);\n twickCanvasRef.current.off(\"object:moving\", handleObjectMoving);\n twickCanvasRef.current.off(\"selection:created\", applyMarqueeSelectionControls);\n twickCanvasRef.current.off(\"selection:updated\", applyMarqueeSelectionControls);\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 canvas?.on(\"object:moving\", handleObjectMoving);\n canvas?.on(\"selection:created\", applyMarqueeSelectionControls);\n canvas?.on(\"selection:updated\", applyMarqueeSelectionControls);\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 * When a marquee selection (ActiveSelection) is dragged or rotated, persists\n * each selected element's new position/rotation to the timeline.\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 const action = event.transform?.action;\n\n if (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\n const context = {\n canvasMetadata: canvasMetadataRef.current,\n videoSize: videoSizeRef.current,\n elementFrameMapRef: elementFrameMap,\n captionPropsRef,\n watermarkPropsRef,\n };\n\n // Marquee selection (ActiveSelection): persist each selected element to timeline\n if (object instanceof ActiveSelection && (action === \"drag\" || action === \"rotate\")) {\n const objects = object.getObjects();\n for (const fabricObj of objects) {\n const id = fabricObj.get(\"id\");\n if (!id || id === \"e-watermark\") continue;\n const currentElement = elementMap.current[id];\n if (!currentElement) continue;\n const handler = elementController.get(currentElement.type);\n const result = handler?.updateFromFabricObject?.(\n fabricObj,\n currentElement,\n context\n );\n if (result) {\n elementMap.current[id] = result.element;\n onCanvasOperation?.(\n result.operation ?? CANVAS_OPERATIONS.ITEM_UPDATED,\n result.payload ?? result.element\n );\n }\n }\n return;\n }\n\n // Single object\n switch (action) {\n case \"drag\":\n case \"scale\":\n case \"scaleX\":\n case \"scaleY\":\n case \"rotate\": {\n const currentElement = elementMap.current[elementId];\n const handler = elementController.get(\n elementId === \"e-watermark\" ? \"watermark\" : currentElement?.type\n );\n const result = handler?.updateFromFabricObject?.(object, currentElement ?? { id: elementId, type: \"text\", props: {} } as CanvasElement, context);\n if (result) {\n elementMap.current[elementId] = result.element;\n onCanvasOperation?.(\n result.operation ?? CANVAS_OPERATIONS.ITEM_UPDATED,\n result.payload ?? result.element\n );\n }\n break;\n }\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 watermark,\n seekTime = 0,\n captionProps,\n cleanAndAdd = false,\n lockAspectRatio,\n }: SetCanvasElementsOptions) => {\n if (!twickCanvas || !getCanvasContext(twickCanvas)) return;\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\n // Deduplicate elements by id to avoid rendering the same logical\n // element multiple times on the canvas (which could happen if the\n // caller accidentally passes duplicates).\n const uniqueElements: CanvasElement[] = [];\n const seenIds = new Set<string>();\n for (const el of elements) {\n if (!el || !el.id) continue;\n if (seenIds.has(el.id)) continue;\n seenIds.add(el.id);\n uniqueElements.push(el);\n }\n\n await Promise.all(\n uniqueElements.map(async (element, index) => {\n try {\n if (!element) return;\n const zOrder = element.zIndex ?? index;\n await addElementToCanvas({\n element,\n index: zOrder,\n reorder: false,\n seekTime,\n captionProps,\n lockAspectRatio,\n });\n } catch {\n // Skip element on add error\n }\n })\n );\n if (watermark) {\n addWatermarkToCanvas({\n element: watermark,\n });\n }\n reorderElementsByZIndex(twickCanvas);\n } catch {\n // Skip on 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 lockAspectRatio,\n }: AddElementToCanvasOptions) => {\n if (!twickCanvas) return;\n const handler = elementController.get(element.type);\n if (handler) {\n await handler.add({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n seekTime,\n captionProps: captionProps ?? null,\n elementFrameMapRef: elementFrameMap,\n getCurrentFrameEffect,\n lockAspectRatio: lockAspectRatio ?? element.props?.lockAspectRatio,\n });\n }\n elementMap.current[element.id] = { ...element, zIndex: element.zIndex ?? index };\n if (reorder) {\n reorderElementsByZIndex(twickCanvas);\n }\n };\n\n const addWatermarkToCanvas = ({ element }: AddWatermarkToCanvasOptions) => {\n if (!twickCanvas) return;\n const handler = elementController.get(\"watermark\");\n if (handler) {\n handler.add({\n element,\n index: Object.keys(elementMap.current).length,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n watermarkPropsRef,\n });\n elementMap.current[element.id] = element;\n }\n };\n\n /**\n * Changes the canvas z-order of the element (Fabric display) and notifies timeline to reorder tracks.\n * Z-order is determined by track order; this emits Z_ORDER_CHANGED so the editor can move the element's track.\n */\n const applyZOrder = (elementId: string, direction: \"front\" | \"back\" | \"forward\" | \"backward\"): boolean => {\n if (!twickCanvas) return false;\n const newZIndex = changeZOrder(twickCanvas, elementId, direction);\n if (newZIndex == null) return false;\n const element = elementMap.current[elementId];\n if (element) elementMap.current[elementId] = { ...element, zIndex: newZIndex };\n onCanvasOperation?.(CANVAS_OPERATIONS.Z_ORDER_CHANGED, { elementId, direction });\n return true;\n };\n\n const bringToFront = (elementId: string) => applyZOrder(elementId, \"front\");\n const sendToBack = (elementId: string) => applyZOrder(elementId, \"back\");\n const bringForward = (elementId: string) => applyZOrder(elementId, \"forward\");\n const sendBackward = (elementId: string) => applyZOrder(elementId, \"backward\");\n\n /**\n * Updates the canvas background color (e.g. when project backgroundColor changes).\n * Keeps the editor canvas in sync with project/visualizer background.\n */\n const setBackgroundColor = useCallback((color: string) => {\n const canvas = twickCanvasRef.current;\n if (canvas) {\n canvas.backgroundColor = color;\n canvas.requestRenderAll();\n }\n }, []);\n\n return {\n twickCanvas,\n buildCanvas,\n resizeCanvas,\n setBackgroundColor,\n onVideoSizeChange,\n addWatermarkToCanvas,\n addElementToCanvas,\n setCanvasElements,\n bringToFront,\n sendToBack,\n bringForward,\n sendBackward,\n };\n};\n"],"names":["FabricCanvas","obj","Control","controlsUtils","Textbox","Shadow","FabricImage","Rect","Group","Circle","scaledW","scaledH","useState","useRef","ActiveSelection","useCallback"],"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/elements/video.element.ts","../src/elements/image.element.ts","../src/elements/rect.element.ts","../src/elements/circle.element.ts","../src/elements/text.element.ts","../src/elements/caption.element.ts","../src/elements/watermark.element.ts","../src/elements/arrow.element.ts","../src/elements/line.element.ts","../src/elements/effect.element.ts","../src/controllers/element.controller.ts","../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 (matches highlight_bg default) */\n family: \"Bangers\",\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 /** Watermark has been updated */\n WATERMARK_UPDATED: \"WATERMARK_UPDATED\",\n /** A new element was added via drop on canvas; payload is { element } */\n ADDED_NEW_ELEMENT: \"ADDED_NEW_ELEMENT\",\n /** Z-order changed (bring to front / send to back). Payload is { elementId, direction }. Timeline should reorder tracks. */\n Z_ORDER_CHANGED: \"Z_ORDER_CHANGED\",\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 /** Icon element type */\n ICON: \"icon\",\n /** Arrow annotation element type */\n ARROW: \"arrow\",\n /** Line annotation / shape element type */\n LINE: \"line\",\n /** Background color element type */\n BACKGROUND_COLOR: \"backgroundColor\",\n /** Global / adjustment-layer style effect element */\n EFFECT: \"effect\",\n} as const;\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 * Measure the width of text using Canvas 2D measureText with the given font.\n * For multi-line text (with \\n), returns the width of the longest line.\n * Used to size Textbox to content when no explicit width is provided.\n *\n * @param text - The text to measure\n * @param options - Font options matching the text element (fontSize, fontFamily, etc.)\n * @returns The width in pixels of the longest line\n */\nexport function measureTextWidth(\n text: string,\n options: {\n fontSize: number;\n fontFamily: string;\n fontStyle?: string;\n fontWeight?: string | number;\n }\n): number {\n if (typeof document === \"undefined\" || !text) return 0;\n\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return 0;\n\n const {\n fontSize,\n fontFamily,\n fontStyle = \"normal\",\n fontWeight = \"normal\",\n } = options;\n ctx.font = `${fontStyle} ${String(fontWeight)} ${fontSize}px ${fontFamily}`;\n\n const lines = text.split(\"\\n\");\n let maxWidth = 0;\n for (const line of lines) {\n const { width } = ctx.measureText(line);\n if (width > maxWidth) maxWidth = width;\n }\n return Math.ceil(maxWidth);\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 // 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 * Finds a Fabric object on the canvas by its element id (stored as custom \"id\" on the object).\n */\nexport const getCanvasObjectById = (\n canvas: FabricCanvas | null | undefined,\n elementId: string\n): import(\"fabric\").FabricObject | undefined => {\n if (!canvas) return undefined;\n const objects = canvas.getObjects();\n return objects.find((obj) => (obj as any).get?.(\"id\") === elementId);\n};\n\nexport type ZOrderDirection = \"front\" | \"back\" | \"forward\" | \"backward\";\n\n/**\n * Changes the z-order of the object with the given element id and reorders the canvas.\n * Returns the new zIndex for the moved object, or null if not found.\n */\nexport const changeZOrder = (\n canvas: FabricCanvas | null | undefined,\n elementId: string,\n direction: ZOrderDirection\n): number | null => {\n if (!canvas) return null;\n const objects = canvas.getObjects();\n const sorted = [...objects].sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0));\n const idx = sorted.findIndex((obj) => (obj as any).get?.(\"id\") === elementId);\n if (idx < 0) return null;\n\n const minZ = sorted[0]?.zIndex ?? 0;\n const maxZ = sorted[sorted.length - 1]?.zIndex ?? 0;\n const obj = sorted[idx] as any;\n\n if (direction === \"front\") {\n obj.set(\"zIndex\", maxZ + 1);\n reorderElementsByZIndex(canvas);\n return maxZ + 1;\n }\n if (direction === \"back\") {\n obj.set(\"zIndex\", minZ - 1);\n reorderElementsByZIndex(canvas);\n return minZ - 1;\n }\n if (direction === \"forward\" && idx < sorted.length - 1) {\n const next = sorted[idx + 1] as any;\n const myZ = obj.zIndex ?? idx;\n const nextZ = next.zIndex ?? idx + 1;\n obj.set(\"zIndex\", nextZ);\n next.set(\"zIndex\", myZ);\n reorderElementsByZIndex(canvas);\n return nextZ;\n }\n if (direction === \"backward\" && idx > 0) {\n const prev = sorted[idx - 1] as any;\n const myZ = obj.zIndex ?? idx;\n const prevZ = prev.zIndex ?? idx - 1;\n obj.set(\"zIndex\", prevZ);\n prev.set(\"zIndex\", myZ);\n reorderElementsByZIndex(canvas);\n return prevZ;\n }\n return obj.zIndex ?? idx;\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 * Returns the object's center position in canvas coordinates.\n * For objects inside a Group/ActiveSelection, left/top are group-relative; this uses\n * getCenterPoint() to get the correct absolute canvas position (same as individual items).\n *\n * @param obj - Fabric object (standalone or inside a group)\n * @returns { x, y } in canvas space\n */\nexport const getObjectCanvasCenter = (obj: { left?: number; top?: number; getCenterPoint?: () => { x: number; y: number } }): { x: number; y: number } => {\n if (obj.getCenterPoint) {\n const p = obj.getCenterPoint();\n return { x: p.x, y: p.y };\n }\n return { x: obj.left ?? 0, y: obj.top ?? 0 };\n};\n\n/**\n * Returns the object's angle in canvas space (degrees).\n * For objects inside a Group/ActiveSelection, object.angle is group-relative; this uses\n * getTotalAngle() to get the correct absolute canvas angle (same as individual items).\n *\n * @param obj - Fabric object (standalone or inside a group)\n * @returns angle in degrees\n */\nexport const getObjectCanvasAngle = (obj: { angle?: number; getTotalAngle?: () => number }): number => {\n if (typeof obj.getTotalAngle === \"function\") {\n return obj.getTotalAngle();\n }\n return obj.angle ?? 0;\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 * Converts dimensions from canvas (Fabric) space to video space.\n * Uses the object's actual displayed size (width*scaleX, height*scaleY) so the\n * result matches what's on canvas even after move or when element.props is stale.\n */\nexport const convertToVideoDimensions = (\n widthCanvas: number,\n heightCanvas: number,\n canvasMetadata: CanvasMetadata\n): Dimensions => {\n return {\n width: Number((widthCanvas / canvasMetadata.scaleX).toFixed(2)),\n height: Number((heightCanvas / canvasMetadata.scaleY).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\n/**\n * Converts a hex color string to rgba with the given alpha.\n * Handles both 3-digit and 6-digit hex color formats.\n *\n * @param hex - The hex color string (e.g. \"#ff0000\" or \"#f00\")\n * @param alpha - Opacity value between 0 and 1\n * @returns CSS rgba string (e.g. \"rgba(255, 0, 0, 0.8)\")\n */\nexport const hexToRgba = (hex: string, alpha: number): string => {\n const color = hex.replace(/^#/, \"\");\n const fullHex =\n color.length === 3\n ? color\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : color;\n if (fullHex.length !== 6) {\n return hex;\n }\n const r = parseInt(fullHex.slice(0, 2), 16);\n const g = parseInt(fullHex.slice(2, 4), 16);\n const b = parseInt(fullHex.slice(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\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 isSafeUrl = /^(https?:|blob:|data:video\\/)/i.test(videoSrc);\n if (!isSafeUrl) {\n reject(new Error(\"Unsafe video source URL\"));\n return;\n }\n const tryLoadVideo = (useCors) => {\n const video = document.createElement(\"video\");\n video.preload = \"metadata\";\n video.crossOrigin = useCors ? \"anonymous\" : null;\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 = () => {\n if (useCors) {\n video.src = \"\";\n tryLoadVideo(false);\n } else {\n reject(new Error(\"Failed to load video metadata. This may be due to CORS restrictions or an invalid video URL.\"));\n }\n };\n };\n tryLoadVideo(true);\n });\n};\n\nconst getThumbnail = async (videoUrl, seekTime = 0.1, playbackRate = 1) => {\n return new Promise((resolve, reject) => {\n const tryLoadThumbnail = (useCors) => {\n const video = document.createElement(\"video\");\n video.crossOrigin = useCors ? \"anonymous\" : null;\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 if (useCors) {\n tryLoadThumbnail(false);\n } else {\n reject(new Error(`Failed to load video: ${video.error?.message || \"Unknown error. This may be due to CORS restrictions or an invalid video URL.\"}`));\n }\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 || 0.1;\n }).catch(() => {\n video.currentTime = seekTime || 0.1;\n });\n } else {\n video.currentTime = seekTime || 0.1;\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 tryLoadThumbnail(true);\n });\n};\n\nclass LRUCache {\n constructor(maxSize = 100) {\n if (maxSize <= 0) {\n throw new Error(\"maxSize must be greater than 0\");\n }\n this.maxSize = maxSize;\n this.cache = /* @__PURE__ */ new Map();\n }\n /**\n * Get a value from the cache.\n * Moves the item to the end (most recently used).\n */\n get(key) {\n const value = this.cache.get(key);\n if (value === void 0) {\n return void 0;\n }\n this.cache.delete(key);\n this.cache.set(key, value);\n return value;\n }\n /**\n * Set a value in the cache.\n * If cache is full, removes the least recently used item.\n */\n set(key, value) {\n if (this.cache.has(key)) {\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxSize) {\n const firstKey = this.cache.keys().next().value;\n if (firstKey !== void 0) {\n this.cache.delete(firstKey);\n }\n }\n this.cache.set(key, value);\n }\n /**\n * Check if a key exists in the cache.\n */\n has(key) {\n return this.cache.has(key);\n }\n /**\n * Delete a key from the cache.\n */\n delete(key) {\n return this.cache.delete(key);\n }\n /**\n * Clear all entries from the cache.\n */\n clear() {\n this.cache.clear();\n }\n /**\n * Get the current size of the cache.\n */\n get size() {\n return this.cache.size;\n }\n}\n\nclass VideoFrameExtractor {\n constructor(options = {}) {\n this.frameCache = new LRUCache(\n options.maxCacheSize ?? 50\n );\n this.videoElements = /* @__PURE__ */ new Map();\n this.maxVideoElements = options.maxVideoElements ?? 5;\n this.loadTimeout = options.loadTimeout ?? 15e3;\n this.jpegQuality = options.jpegQuality ?? 0.8;\n this.playbackRate = options.playbackRate ?? 1;\n }\n /**\n * Get a frame thumbnail from a video at a specific time.\n * Uses caching and reuses video elements for optimal performance.\n * Uses 0.1s instead of 0 when seekTime is 0, since frames at t=0 are often blank.\n *\n * @param videoUrl - The URL of the video\n * @param seekTime - The time in seconds to extract the frame (0 is treated as 0.1)\n * @returns Promise resolving to a thumbnail image URL (data URL or blob URL)\n */\n async getFrame(videoUrl, seekTime = 0.1) {\n const effectiveSeekTime = seekTime === 0 ? 0.1 : seekTime;\n const cacheKey = this.getCacheKey(videoUrl, effectiveSeekTime);\n const cached = this.frameCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n const videoState = await this.getVideoElement(videoUrl);\n const thumbnail = await this.extractFrame(videoState.video, effectiveSeekTime);\n this.frameCache.set(cacheKey, thumbnail);\n return thumbnail;\n }\n /**\n * Get or create a video element for the given URL.\n * Reuses existing elements and manages cleanup.\n */\n async getVideoElement(videoUrl) {\n let videoState = this.videoElements.get(videoUrl);\n if (videoState && videoState.isReady) {\n videoState.lastUsed = Date.now();\n return videoState;\n }\n if (videoState && videoState.isLoading && videoState.loadPromise) {\n await videoState.loadPromise;\n if (videoState.isReady) {\n videoState.lastUsed = Date.now();\n return videoState;\n }\n }\n if (this.videoElements.size >= this.maxVideoElements) {\n this.cleanupOldVideoElements();\n }\n videoState = await this.createVideoElement(videoUrl);\n this.videoElements.set(videoUrl, videoState);\n videoState.lastUsed = Date.now();\n return videoState;\n }\n /**\n * Create and initialize a new video element.\n */\n async createVideoElement(videoUrl) {\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 = this.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 const state = {\n video,\n isReady: false,\n isLoading: true,\n loadPromise: null,\n lastUsed: Date.now()\n };\n state.loadPromise = new Promise((resolve, reject) => {\n let timeoutId;\n const cleanup = () => {\n if (timeoutId) clearTimeout(timeoutId);\n };\n const handleError = () => {\n cleanup();\n state.isLoading = false;\n reject(new Error(`Failed to load video: ${video.error?.message || \"Unknown error\"}`));\n };\n const handleLoadedMetadata = () => {\n cleanup();\n state.isReady = true;\n state.isLoading = false;\n resolve();\n };\n video.addEventListener(\"error\", handleError, { once: true });\n video.addEventListener(\"loadedmetadata\", handleLoadedMetadata, { once: true });\n timeoutId = window.setTimeout(() => {\n cleanup();\n state.isLoading = false;\n reject(new Error(\"Video loading timed out\"));\n }, this.loadTimeout);\n video.src = videoUrl;\n document.body.appendChild(video);\n });\n try {\n await state.loadPromise;\n } catch (error) {\n if (video.parentNode) {\n video.remove();\n }\n throw error;\n }\n return state;\n }\n /**\n * Extract a frame from a video at the specified time.\n */\n async extractFrame(video, seekTime) {\n return new Promise((resolve, reject) => {\n video.pause();\n const timeThreshold = 0.1;\n if (Math.abs(video.currentTime - seekTime) < timeThreshold) {\n try {\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 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\", this.jpegQuality);\n resolve(dataUrl);\n } catch {\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error(\"Failed to create Blob\"));\n return;\n }\n const blobUrl = URL.createObjectURL(blob);\n resolve(blobUrl);\n },\n \"image/jpeg\",\n this.jpegQuality\n );\n }\n return;\n } catch (err) {\n reject(new Error(`Error creating thumbnail: ${err}`));\n return;\n }\n }\n const handleSeeked = () => {\n try {\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 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\", this.jpegQuality);\n resolve(dataUrl);\n } catch {\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error(\"Failed to create Blob\"));\n return;\n }\n const blobUrl = URL.createObjectURL(blob);\n resolve(blobUrl);\n },\n \"image/jpeg\",\n this.jpegQuality\n );\n }\n } catch (err) {\n reject(new Error(`Error creating thumbnail: ${err}`));\n }\n };\n video.addEventListener(\"seeked\", handleSeeked, { once: true });\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 });\n }\n /**\n * Generate cache key for a video URL and seek time.\n */\n getCacheKey(videoUrl, seekTime) {\n const roundedTime = Math.round(seekTime * 100) / 100;\n return `${videoUrl}:${roundedTime}`;\n }\n /**\n * Cleanup least recently used video elements.\n */\n cleanupOldVideoElements() {\n if (this.videoElements.size < this.maxVideoElements) {\n return;\n }\n const entries = Array.from(this.videoElements.entries());\n entries.sort((a, b) => a[1].lastUsed - b[1].lastUsed);\n const toRemove = entries.slice(0, entries.length - this.maxVideoElements + 1);\n for (const [url, state] of toRemove) {\n if (state.video.parentNode) {\n state.video.remove();\n }\n this.videoElements.delete(url);\n }\n }\n /**\n * Clear the frame cache.\n */\n clearCache() {\n this.frameCache.clear();\n }\n /**\n * Remove a specific video element and clear its cached frames.\n */\n removeVideo(videoUrl) {\n const state = this.videoElements.get(videoUrl);\n if (state) {\n if (state.video.parentNode) {\n state.video.remove();\n }\n this.videoElements.delete(videoUrl);\n }\n this.frameCache.clear();\n }\n /**\n * Dispose of all video elements and clear caches.\n * Removes all video elements from the DOM and clears both the frame cache\n * and video element cache. Call this when the extractor is no longer needed\n * to prevent memory leaks.\n */\n dispose() {\n for (const state of this.videoElements.values()) {\n if (state.video.parentNode) {\n state.video.remove();\n }\n }\n this.videoElements.clear();\n this.frameCache.clear();\n }\n}\nlet defaultExtractor = null;\nfunction getDefaultVideoFrameExtractor(options) {\n if (!defaultExtractor) {\n defaultExtractor = new VideoFrameExtractor(options);\n }\n return defaultExtractor;\n}\nasync function getThumbnailCached(videoUrl, seekTime = 0.1, playbackRate) {\n const extractor = getDefaultVideoFrameExtractor(\n playbackRate !== void 0 ? { playbackRate } : void 0\n );\n return extractor.getFrame(videoUrl, seekTime);\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) continue;\n const volume = segment.volume ?? 1;\n if (volume <= 0) continue;\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 {\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 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 {\n return null;\n }\n};\n\nexport { VideoFrameExtractor, blobUrlToFile, detectMediaTypeFromUrl, downloadFile, extractAudio, getAudioDuration, getDefaultVideoFrameExtractor, getImageDimensions, getObjectFitSize, getScaledDimensions, getThumbnail, getThumbnailCached, getVideoMeta, hasAudio, limit, loadFile, saveAsFile, stitchAudio };\n//# sourceMappingURL=index.mjs.map\n","import {\n Canvas as FabricCanvas,\n Textbox,\n Group,\n FabricImage,\n Rect,\n Circle,\n Shadow,\n} from \"fabric\";\nimport {\n convertToCanvasPosition,\n hexToRgba,\n measureTextWidth,\n} 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, getThumbnailCached } from \"@twick/media-utils\";\n\nconst MARGIN = 10;\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 const fontSize = Math.floor(\n (element.props?.fontSize || DEFAULT_TEXT_PROPS.size) *\n canvasMetadata.scaleX\n );\n const fontFamily = element.props?.fontFamily || DEFAULT_TEXT_PROPS.family;\n const fontStyle = element.props?.fontStyle || \"normal\";\n const fontWeight = element.props?.fontWeight || \"normal\";\n\n let width: number;\n if (element.props?.width != null && element.props.width > 0) {\n width = element.props.width * canvasMetadata.scaleX;\n if (element.props?.maxWidth) {\n width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);\n }\n } else {\n const textContent = element.props?.text ?? element.t ?? \"\";\n width = measureTextWidth(textContent, {\n fontSize,\n fontFamily,\n fontStyle,\n fontWeight,\n });\n const padding = 4;\n width = width + padding * 2;\n if (element.props?.maxWidth) {\n width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);\n }\n if (width <= 0) width = 100;\n }\n\n const backgroundColor = element.props?.backgroundColor\n ? hexToRgba(\n element.props.backgroundColor,\n element.props?.backgroundOpacity ?? 1\n )\n : undefined;\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,\n fontFamily,\n fontStyle,\n fontWeight,\n fill: element.props?.fill || DEFAULT_TEXT_PROPS.fill,\n opacity: element.props?.opacity ?? 1,\n 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 ...(backgroundColor && { backgroundColor }),\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 (text is not resizable on canvas)\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 lockAspectRatio = true,\n}: {\n img: FabricImage;\n element: CanvasElement;\n index: number;\n canvasMetadata: CanvasMetadata;\n lockAspectRatio?: boolean;\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 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 img.set(\"lockUniScaling\", lockAspectRatio);\n};\n\n/**\n * Add a caption element to the canvas based on provided props.\n * Creates a Fabric.js Textbox with caption-specific styling including\n * shadows, positioning, font properties, and text wrapping (same as text element).\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 Textbox caption object with wrapping enabled\n *\n * @example\n * ```js\n * const captionElement = addCaptionElement({\n * element: { id: \"caption1\", props: { text: \"Caption\", x: 100, y: 100, width: 200 } },\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 lockAspectRatio = false,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n captionProps: CaptionProps;\n canvasMetadata: CanvasMetadata;\n lockAspectRatio?: boolean;\n}) => {\n const applyToAll = captionProps?.applyToAll ?? false;\n const captionTextColor =\n captionProps?.colors?.text ?? captionProps?.color?.text;\n\n const { x, y } = convertToCanvasPosition(\n (applyToAll ? captionProps?.x : element.props?.x ?? captionProps?.x) ?? 0,\n (applyToAll ? captionProps?.y : element.props?.y ?? captionProps?.y) ?? 0,\n canvasMetadata\n );\n\n let width = element.props?.width ? element.props.width * canvasMetadata.scaleX : canvasMetadata.width - (2 * MARGIN);\n if (element.props?.maxWidth) {\n width = Math.min(width, element.props.maxWidth * canvasMetadata.scaleX);\n }\n\n const elementColors = (element.props as { colors?: { text?: string } })?.colors;\n const resolvedFill =\n (applyToAll\n ? captionTextColor\n : element.props?.fill ?? elementColors?.text ?? captionTextColor) ??\n DEFAULT_CAPTION_PROPS.fill;\n\n const caption = 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.round(\n ((applyToAll\n ? captionProps?.font?.size\n : element.props?.font?.size ?? captionProps?.font?.size) ??\n DEFAULT_CAPTION_PROPS.size) * canvasMetadata.scaleX\n ),\n fontFamily:\n (applyToAll\n ? captionProps?.font?.family\n : element.props?.font?.family ?? captionProps?.font?.family) ??\n DEFAULT_CAPTION_PROPS.family,\n fill: resolvedFill,\n fontWeight:\n (applyToAll\n ? captionProps?.font?.weight\n : element.props?.font?.weight ?? captionProps?.font?.weight) ??\n DEFAULT_CAPTION_PROPS.fontWeight,\n stroke: (applyToAll\n ? captionProps?.stroke\n : element.props?.stroke ?? captionProps?.stroke) ??\n DEFAULT_CAPTION_PROPS.stroke,\n opacity: (applyToAll\n ? captionProps?.opacity\n : element.props?.opacity ?? captionProps?.opacity) ?? 1,\n width,\n splitByGrapheme: false,\n textAlign: element.props?.textAlign ?? \"center\",\n shadow: new Shadow({\n offsetX:\n (applyToAll\n ? captionProps?.shadowOffset?.[0]\n : element.props?.shadowOffset?.[0] ?? captionProps?.shadowOffset?.[0]) ??\n DEFAULT_CAPTION_PROPS.shadowOffset?.[0],\n offsetY:\n (applyToAll\n ? captionProps?.shadowOffset?.[1]\n : element.props?.shadowOffset?.[1] ?? captionProps?.shadowOffset?.[1]) ??\n DEFAULT_CAPTION_PROPS.shadowOffset?.[1],\n blur: (applyToAll\n ? captionProps?.shadowBlur\n : element.props?.shadowBlur ?? captionProps?.shadowBlur) ?? DEFAULT_CAPTION_PROPS.shadowBlur,\n color: (applyToAll\n ? captionProps?.shadowColor\n : element.props?.shadowColor ?? captionProps?.shadowColor) ?? DEFAULT_CAPTION_PROPS.shadowColor,\n }),\n strokeWidth: (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 caption.set(\"lockUniScaling\", lockAspectRatio);\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 getThumbnailCached(\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 {\n // Skip element on load 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 lockAspectRatio = true,\n}: {\n imageUrl?: string;\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n /** When true, resize keeps aspect ratio (uniform scaling). Default true for images. */\n lockAspectRatio?: boolean;\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: lockAspectRatio,\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 lockAspectRatio,\n });\n } else {\n setImageProps({ img, element, index, canvasMetadata, lockAspectRatio });\n canvas.add(img);\n return img;\n }\n } catch {\n // Skip element on load 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 lockAspectRatio = true,\n}: {\n element: CanvasElement;\n img: FabricImage;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n currentFrameEffect?: FrameEffect;\n lockAspectRatio?: boolean;\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 group.set(\"lockUniScaling\", lockAspectRatio);\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 lockAspectRatio = false,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n lockAspectRatio?: boolean;\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 rect.set(\"lockUniScaling\", lockAspectRatio);\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 lockAspectRatio = true,\n}: {\n element: CanvasElement;\n index: number;\n canvas: FabricCanvas;\n canvasMetadata: CanvasMetadata;\n lockAspectRatio?: boolean;\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 // Respect element opacity (0–1). Defaults to fully opaque.\n opacity: element.props?.opacity ?? 1,\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 circle.set(\"lockUniScaling\", lockAspectRatio);\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 type { CanvasElementHandler } from \"../types\";\nimport { addVideoElement, addBackgroundColor } from \"../components/elements\";\nimport { convertToVideoPosition, convertToVideoDimensions, getObjectCanvasCenter, getObjectCanvasAngle } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\n\nexport const VideoElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.VIDEO,\n\n async add(params) {\n const {\n element,\n index,\n canvas,\n canvasMetadata,\n seekTime = 0,\n elementFrameMapRef,\n getCurrentFrameEffect: getFrameEffect,\n } = params;\n if (!getFrameEffect || !elementFrameMapRef) return;\n\n const currentFrameEffect = getFrameEffect(element, seekTime);\n elementFrameMapRef.current[element.id] = currentFrameEffect;\n\n const snapTime =\n (seekTime - (element?.s ?? 0)) * (element?.props?.playbackRate ?? 1) +\n (element?.props?.time ?? 0);\n\n await addVideoElement({\n element,\n index,\n canvas,\n canvasMetadata,\n currentFrameEffect,\n snapTime,\n });\n\n if (element.timelineType === \"scene\") {\n await addBackgroundColor({\n element,\n index,\n canvas,\n canvasMetadata,\n });\n }\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n const scaledW = (object.width ?? 0) * (object.scaleX ?? 1);\n const scaledH = (object.height ?? 0) * (object.scaleY ?? 1);\n const { width: fw, height: fh } = convertToVideoDimensions(\n scaledW,\n scaledH,\n context.canvasMetadata\n );\n const updatedFrameSize: [number, number] = [fw, fh];\n const currentFrameEffect = context.elementFrameMapRef.current[element.id];\n\n if (currentFrameEffect) {\n context.elementFrameMapRef.current[element.id] = {\n ...currentFrameEffect,\n props: {\n ...currentFrameEffect.props,\n framePosition: { x, y },\n frameSize: updatedFrameSize,\n },\n };\n return {\n element: {\n ...element,\n frameEffects: (element.frameEffects || []).map((fe: any) =>\n (fe as { id?: string }).id === (currentFrameEffect as { id?: string })?.id\n ? {\n ...fe,\n props: {\n ...fe.props,\n framePosition: { x, y },\n frameSize: updatedFrameSize,\n },\n }\n : fe\n ),\n },\n };\n }\n\n const frame = element.frame!;\n return {\n element: {\n ...element,\n frame: {\n ...frame,\n rotation: getObjectCanvasAngle(object),\n size: updatedFrameSize,\n x,\n y,\n },\n },\n };\n },\n};\n","import type { CanvasElementHandler } from \"../types\";\nimport { addImageElement, addBackgroundColor } from \"../components/elements\";\nimport { convertToVideoPosition, convertToVideoDimensions, getObjectCanvasCenter, getObjectCanvasAngle } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\n\nexport const ImageElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.IMAGE,\n\n async add(params) {\n const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;\n await addImageElement({\n element,\n index,\n canvas,\n canvasMetadata,\n lockAspectRatio: lockAspectRatio ?? element.props?.lockAspectRatio,\n });\n if (element.timelineType === \"scene\") {\n await addBackgroundColor({\n element,\n index,\n canvas,\n canvasMetadata,\n });\n }\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n const currentFrameEffect = context.elementFrameMapRef.current[element.id];\n\n if (object.type === \"group\") {\n const scaledW = (object.width ?? 0) * (object.scaleX ?? 1);\n const scaledH = (object.height ?? 0) * (object.scaleY ?? 1);\n const { width: fw, height: fh } = convertToVideoDimensions(\n scaledW,\n scaledH,\n context.canvasMetadata\n );\n const updatedFrameSize: [number, number] = [fw, fh];\n if (currentFrameEffect) {\n context.elementFrameMapRef.current[element.id] = {\n ...currentFrameEffect,\n props: {\n ...currentFrameEffect.props,\n framePosition: { x, y },\n frameSize: updatedFrameSize,\n },\n };\n return {\n element: {\n ...element,\n // Keep the base frame in sync with the active frame effect\n // so visualizer `Rect {...element.frame}` reflects the same size/position.\n frame: element.frame\n ? {\n ...element.frame,\n rotation: getObjectCanvasAngle(object),\n size: updatedFrameSize,\n x,\n y,\n }\n : element.frame,\n frameEffects: (element.frameEffects || []).map((fe: any) =>\n (fe as { id?: string }).id === (currentFrameEffect as { id?: string })?.id\n ? {\n ...fe,\n props: {\n ...fe.props,\n framePosition: { x, y },\n frameSize: updatedFrameSize,\n },\n }\n : fe\n ),\n },\n };\n }\n const frame = element.frame!;\n return {\n element: {\n ...element,\n frame: {\n ...frame,\n rotation: getObjectCanvasAngle(object),\n size: updatedFrameSize,\n x,\n y,\n },\n },\n };\n }\n\n // Use Fabric's actual displayed size so live player matches canvas after resize+move\n const scaledW = (object.width ?? 0) * (object.scaleX ?? 1);\n const scaledH = (object.height ?? 0) * (object.scaleY ?? 1);\n const { width, height } = convertToVideoDimensions(\n scaledW,\n scaledH,\n context.canvasMetadata\n );\n return {\n element: {\n ...element,\n props: {\n ...element.props,\n rotation: getObjectCanvasAngle(object),\n width,\n height,\n x,\n y,\n },\n },\n };\n },\n};\n","import type { CanvasElementHandler } from \"../types\";\nimport { addRectElement } from \"../components/elements\";\nimport { convertToVideoPosition, getObjectCanvasCenter, getObjectCanvasAngle } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\n\nexport const RectElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.RECT,\n\n async add(params) {\n const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;\n await addRectElement({\n element,\n index,\n canvas,\n canvasMetadata,\n lockAspectRatio: lockAspectRatio ?? element.props?.lockAspectRatio,\n });\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n return {\n element: {\n ...element,\n props: {\n ...element.props,\n rotation: getObjectCanvasAngle(object),\n width: (element.props?.width ?? 0) * object.scaleX,\n height: (element.props?.height ?? 0) * object.scaleY,\n x,\n y,\n },\n },\n };\n },\n};\n","import type { CanvasElementHandler } from \"../types\";\nimport { addCircleElement } from \"../components/elements\";\nimport { convertToVideoPosition, getObjectCanvasCenter, getObjectCanvasAngle } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\n\nexport const CircleElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.CIRCLE,\n\n async add(params) {\n const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;\n await addCircleElement({\n element,\n index,\n canvas,\n canvasMetadata,\n lockAspectRatio: lockAspectRatio ?? element.props?.lockAspectRatio,\n });\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n const radius = Number(\n ((element.props?.radius ?? 0) * object.scaleX).toFixed(2)\n );\n const opacity = object.opacity != null ? object.opacity : element.props?.opacity;\n return {\n element: {\n ...element,\n props: {\n ...element.props,\n rotation: getObjectCanvasAngle(object),\n radius,\n height: radius * 2,\n width: radius * 2,\n x,\n y,\n ...(opacity != null && { opacity }),\n },\n },\n };\n },\n};\n","import type { CanvasElementHandler } from \"../types\";\nimport { addTextElement } from \"../components/elements\";\nimport { convertToVideoPosition, getObjectCanvasCenter, getObjectCanvasAngle } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\n\nexport const TextElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.TEXT,\n\n async add(params) {\n const { element, index, canvas, canvasMetadata } = params;\n await addTextElement({\n element,\n index,\n canvas,\n canvasMetadata,\n });\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n return {\n element: {\n ...element,\n props: {\n ...element.props,\n rotation: getObjectCanvasAngle(object),\n x,\n y,\n },\n },\n };\n },\n};\n","import type { CanvasElementHandler } from \"../types\";\nimport { addCaptionElement } from \"../components/elements\";\nimport { convertToVideoPosition, getObjectCanvasCenter } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\nimport { CANVAS_OPERATIONS } from \"../helpers/constants\";\n\nexport const CaptionElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.CAPTION,\n\n async add(params) {\n const { element, index, canvas, captionProps, canvasMetadata, lockAspectRatio } = params;\n await addCaptionElement({\n element,\n index,\n canvas,\n captionProps: (captionProps ?? {}) as import(\"../types\").CaptionProps,\n canvasMetadata,\n lockAspectRatio: lockAspectRatio ?? element.props?.lockAspectRatio,\n });\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n if (context.captionPropsRef.current?.applyToAll) {\n return {\n element,\n operation: CANVAS_OPERATIONS.CAPTION_PROPS_UPDATED,\n payload: {\n element,\n props: {\n ...context.captionPropsRef.current,\n x,\n y,\n },\n },\n };\n }\n return {\n element: {\n ...element,\n props: {\n ...element.props,\n x,\n y,\n },\n },\n };\n },\n};\n","import type { CanvasElementHandler, WatermarkUpdatePayload } from \"../types\";\nimport { addTextElement, addImageElement } from \"../components/elements\";\nimport { convertToVideoPosition } from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\nimport { CANVAS_OPERATIONS } from \"../helpers/constants\";\n\n/**\n * Watermark handler: add() supports element.type \"text\" | \"image\".\n * updateFromFabricObject() returns canonical WatermarkUpdatePayload for\n * WATERMARK_UPDATED so video-editor can persist via setWatermark().\n */\nexport const WatermarkElement: CanvasElementHandler = {\n name: \"watermark\",\n\n async add(params) {\n const { element, index, canvas, canvasMetadata, watermarkPropsRef } = params;\n if (element.type === ELEMENT_TYPES.TEXT) {\n if (watermarkPropsRef) watermarkPropsRef.current = element.props;\n await addTextElement({\n element,\n index,\n canvas,\n canvasMetadata,\n });\n } else if (element.type === ELEMENT_TYPES.IMAGE) {\n await addImageElement({\n element,\n index,\n canvas,\n canvasMetadata,\n });\n }\n },\n\n updateFromFabricObject(object, element, context) {\n const { x, y } = convertToVideoPosition(\n object.left,\n object.top,\n context.canvasMetadata,\n context.videoSize\n );\n const rotation = object.angle != null ? object.angle : undefined;\n const opacity = object.opacity != null ? object.opacity : undefined;\n // Preserve existing props; text uses watermarkPropsRef, image uses element.props + scale\n const baseProps =\n element.type === ELEMENT_TYPES.TEXT\n ? context.watermarkPropsRef.current ?? element.props ?? {}\n : { ...element.props };\n const props =\n element.type === ELEMENT_TYPES.IMAGE && (object.scaleX != null || object.scaleY != null)\n ? {\n ...baseProps,\n width: baseProps.width != null && object.scaleX != null ? baseProps.width * object.scaleX : baseProps.width,\n height: baseProps.height != null && object.scaleY != null ? baseProps.height * object.scaleY : baseProps.height,\n }\n : baseProps;\n\n const payload: WatermarkUpdatePayload = {\n position: { x, y },\n ...(rotation != null && { rotation }),\n ...(opacity != null && { opacity }),\n ...(Object.keys(props).length > 0 && { props }),\n };\n\n return {\n element: { ...element, props: { ...element.props, x, y, rotation, opacity, ...props } },\n operation: CANVAS_OPERATIONS.WATERMARK_UPDATED,\n payload,\n };\n },\n};\n","import type { CanvasElementHandler } from \"../types\";\nimport { Group, Rect, Triangle } from \"fabric\";\nimport {\n convertToCanvasPosition,\n convertToVideoPosition,\n getObjectCanvasAngle,\n getObjectCanvasCenter,\n} from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\n\nexport const ArrowElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.ARROW,\n\n async add(params) {\n const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;\n\n const baseWidth = element.props?.width ?? 220;\n const baseHeight = element.props?.height ?? 14;\n const { x, y } = convertToCanvasPosition(\n element.props?.x ?? 0,\n element.props?.y ?? 0,\n canvasMetadata\n );\n\n const fill = element.props?.fill || \"#f59e0b\";\n const radius = (element.props?.radius ?? 4) * canvasMetadata.scaleX;\n\n const barWidth = baseWidth * canvasMetadata.scaleX;\n const barHeight = baseHeight * canvasMetadata.scaleY;\n const headSize = barHeight * 1.8;\n\n // Bar: from left to just before the triangle, with overlap so no gap\n const barLength = barWidth - headSize * 0.5;\n const bar = new Rect({\n left: -barWidth / 2,\n top: -barHeight / 2,\n originX: \"left\",\n originY: \"top\",\n width: barLength,\n height: barHeight,\n rx: radius,\n ry: radius,\n fill,\n });\n\n // Triangle: tip points right; center overlaps the bar end for a clean join\n const arrowHead = new Triangle({\n left: barWidth / 2 - headSize * 0.25,\n top: 0,\n originX: \"center\",\n originY: \"center\",\n width: headSize,\n height: headSize,\n fill,\n angle: 90,\n });\n\n const opacity = element.props?.opacity ?? 1;\n\n const group = new Group([bar, arrowHead], {\n left: x,\n top: y,\n originX: \"center\",\n originY: \"center\",\n angle: element.props?.rotation || 0,\n opacity,\n selectable: true,\n hasControls: true,\n });\n\n // Keep arrow scaling uniform when resizing\n group.set(\n \"lockUniScaling\",\n (lockAspectRatio ?? element.props?.lockAspectRatio ?? true) as boolean\n );\n\n // Attach metadata for syncing back to the timeline\n group.set(\"id\" as any, element.id as any);\n group.set(\"zIndex\" as any, index as any);\n\n canvas.add(group);\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n\n const baseWidth = element.props?.width ?? 220;\n const baseHeight = element.props?.height ?? 14;\n\n const opacity =\n (object as { opacity?: number }).opacity ?? element.props?.opacity ?? 1;\n\n return {\n element: {\n ...element,\n props: {\n ...element.props,\n rotation: getObjectCanvasAngle(object),\n width: baseWidth * object.scaleX,\n height: baseHeight * object.scaleY,\n x,\n y,\n opacity,\n },\n },\n };\n },\n};\n","import type { CanvasElementHandler } from \"../types\";\nimport { addRectElement } from \"../components/elements\";\nimport {\n convertToVideoPosition,\n getObjectCanvasCenter,\n getObjectCanvasAngle,\n} from \"../helpers/canvas.util\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\n\n/**\n * LineElement: renders a simple line/segment using the rectangular helper.\n *\n * Semantics:\n * - props.width → visual length of the line\n * - props.height → visual thickness of the line\n * - props.fill → line color\n * - props.radius → roundedness of the ends (handled as rect corner radius)\n */\nexport const LineElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.LINE,\n\n async add(params) {\n const { element, index, canvas, canvasMetadata, lockAspectRatio } = params;\n\n // Ensure the visible stroke matches the logical fill color for lines.\n // By default, generic rect helpers fall back to a black stroke, which\n // makes thin lines look black even if fill is set. For LINE we want the\n // stroke (outline) either disabled or aligned with the fill color.\n const lineProps = element.props ?? {};\n const lineElement = {\n ...element,\n props: {\n ...lineProps,\n // Use fill as stroke color when a stroke is desired; otherwise rely\n // on fill-only rendering. Avoid the generic \"#000000\" fallback.\n stroke: lineProps.stroke ?? lineProps.fill,\n // If a specific lineWidth is provided, keep it; otherwise default to 0\n // so the stroke does not override the visual fill color.\n lineWidth: lineProps.lineWidth ?? 0,\n },\n };\n\n await addRectElement({\n element: lineElement,\n index,\n canvas,\n canvasMetadata,\n lockAspectRatio: lockAspectRatio ?? lineElement.props?.lockAspectRatio,\n });\n },\n\n updateFromFabricObject(object, element, context) {\n const canvasCenter = getObjectCanvasCenter(object);\n const { x, y } = convertToVideoPosition(\n canvasCenter.x,\n canvasCenter.y,\n context.canvasMetadata,\n context.videoSize\n );\n return {\n element: {\n ...element,\n props: {\n ...element.props,\n rotation: getObjectCanvasAngle(object),\n width: (element.props?.width ?? 0) * object.scaleX,\n height: (element.props?.height ?? 0) * object.scaleY,\n x,\n y,\n },\n },\n };\n },\n};\n\n","import type { CanvasElementHandler } from \"../types\";\nimport { ELEMENT_TYPES } from \"../helpers/constants\";\n\nexport const EffectElement: CanvasElementHandler = {\n name: ELEMENT_TYPES.EFFECT,\n\n async add() {\n // Effect elements are global/post-processing only and do not render\n // any Fabric objects on the editing canvas. Selection and editing\n // are handled via the timeline UI.\n return;\n },\n};\n\n","import type { CanvasElementHandler } from \"../types\";\nimport { VideoElement } from \"../elements/video.element\";\nimport { ImageElement } from \"../elements/image.element\";\nimport { RectElement } from \"../elements/rect.element\";\nimport { CircleElement } from \"../elements/circle.element\";\nimport { TextElement } from \"../elements/text.element\";\nimport { CaptionElement } from \"../elements/caption.element\";\nimport { WatermarkElement } from \"../elements/watermark.element\";\nimport { ArrowElement } from \"../elements/arrow.element\";\nimport { LineElement } from \"../elements/line.element\";\nimport { EffectElement } from \"../elements/effect.element\";\n\n/**\n * Registry for canvas element handlers. Enables scalable dispatch by type:\n * elementController.get(element.type)?.add(params) and updateFromFabricObject(...).\n */\nexport class ElementController {\n private elements = new Map<string, CanvasElementHandler>();\n\n register(handler: CanvasElementHandler) {\n this.elements.set(handler.name, handler);\n }\n\n get(name: string): CanvasElementHandler | undefined {\n return this.elements.get(name);\n }\n\n list(): string[] {\n return Array.from(this.elements.keys());\n }\n}\n\nconst elementController = new ElementController();\n\nfunction registerElements() {\n elementController.register(VideoElement);\n elementController.register(ImageElement);\n elementController.register(RectElement);\n elementController.register(CircleElement);\n elementController.register(TextElement);\n elementController.register(CaptionElement);\n elementController.register(WatermarkElement);\n elementController.register(ArrowElement);\n elementController.register(LineElement);\n elementController.register(EffectElement);\n}\n\nregisterElements();\n\nexport function registerCanvasHandler(handler: CanvasElementHandler): void {\n elementController.register(handler);\n}\n\nexport function getCanvasHandler(name: string): CanvasElementHandler | undefined {\n return elementController.get(name);\n}\n\nexport default elementController;\n","import { useCallback, useRef, useState } from \"react\";\nimport { Canvas as FabricCanvas, FabricObject, Textbox, ActiveSelection } from \"fabric\";\nimport { Dimensions } from \"@twick/media-utils\";\nimport {\n CanvasMetadata,\n CanvasElement,\n CaptionProps,\n BuildCanvasOptions,\n AddElementToCanvasOptions,\n SetCanvasElementsOptions,\n AddWatermarkToCanvasOptions,\n ResizeCanvasOptions,\n} from \"../types\";\nimport {\n changeZOrder,\n clearCanvas,\n createCanvas,\n getCanvasContext,\n getCurrentFrameEffect,\n reorderElementsByZIndex,\n} from \"../helpers/canvas.util\";\nimport { CANVAS_OPERATIONS } from \"../helpers/constants\";\nimport elementController from \"../controllers/element.controller\";\nimport { disabledControl, rotateControl } from \"../components/element-controls\";\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 * When true, holding Shift while dragging an object will lock movement to\n * the dominant axis (horizontal or vertical). This mirrors behavior in\n * professional editors and improves precise alignment.\n *\n * Default: false (opt‑in to avoid surprising existing consumers).\n */\n enableShiftAxisLock = false,\n}: {\n onCanvasReady?: (canvas: FabricCanvas) => void;\n onCanvasOperation?: (operation: string, data: any) => void;\n enableShiftAxisLock?: boolean;\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 watermarkPropsRef = useRef<any | null>(null);\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 axisLockStateRef = useRef<\n | null\n | {\n /** \"x\" when movement is locked horizontally, \"y\" when locked vertically */\n axis: \"x\" | \"y\";\n }\n >(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 * Resizes the canvas to match new container dimensions.\n * Updates Fabric canvas dimensions and metadata (scaleX, scaleY) without\n * recreating the canvas. Caller should refresh elements (e.g. setCanvasElements\n * with cleanAndAdd) after resize so they are re-positioned with the new scale.\n *\n * @param canvasSize - New canvas dimensions (e.g. from ResizeObserver)\n *\n * @example\n * ```js\n * resizeCanvas({ width: 800, height: 600 });\n * setCanvasElements({ elements, cleanAndAdd: true });\n * ```\n */\n const resizeCanvas = ({\n canvasSize,\n videoSize = videoSizeRef.current,\n }: ResizeCanvasOptions) => {\n const canvas = twickCanvasRef.current;\n if (!canvas || !getCanvasContext(canvas)) return;\n if (!videoSize?.width || !videoSize?.height) return;\n if (\n canvasResolutionRef.current.width === canvasSize.width &&\n canvasResolutionRef.current.height === canvasSize.height\n ) {\n return;\n }\n\n canvasMetadataRef.current = {\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 canvas.setDimensions({\n width: canvasSize.width,\n height: canvasSize.height,\n });\n canvasResolutionRef.current = canvasSize;\n canvas.requestRenderAll();\n };\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 * Handles object moving events on the canvas.\n * When Shift is held (and axis lock is enabled), restricts movement to the\n * dominant axis based on the initial drag direction.\n */\n const handleObjectMoving = (event: any) => {\n if (!enableShiftAxisLock) return;\n const target: FabricObject | undefined = event?.target;\n const transform = event?.transform;\n const pointerEvent = event?.e as MouseEvent | PointerEvent | undefined;\n\n if (!target || !transform || !pointerEvent) {\n axisLockStateRef.current = null;\n return;\n }\n\n // If Shift is not pressed, do not constrain movement.\n if (!pointerEvent.shiftKey) {\n axisLockStateRef.current = null;\n return;\n }\n\n const original = transform.original;\n if (!original || typeof target.left !== \"number\" || typeof target.top !== \"number\") {\n axisLockStateRef.current = null;\n return;\n }\n\n // Decide the dominant axis once for the drag operation.\n if (!axisLockStateRef.current) {\n const dx = Math.abs(target.left - original.left);\n const dy = Math.abs(target.top - original.top);\n axisLockStateRef.current = {\n axis: dx >= dy ? \"x\" : \"y\",\n };\n }\n\n if (axisLockStateRef.current.axis === \"x\") {\n // Lock vertical movement.\n target.top = original.top;\n } else {\n // Lock horizontal movement.\n target.left = original.left;\n }\n\n // Ensure the canvas reflects the updated coordinates.\n target.canvas?.requestRenderAll();\n };\n\n /**\n * Applies drag-only controls to marquee selection (ActiveSelection).\n * Restricts resize/scale while allowing drag and rotate, matching text element behavior.\n * Does not affect Media Groups (image+frame) which are Fabric Groups, not ActiveSelection.\n */\n const applyMarqueeSelectionControls = () => {\n const canvasInstance = twickCanvasRef.current;\n if (!canvasInstance) return;\n\n const activeObject = canvasInstance.getActiveObject();\n if (!activeObject) return;\n\n // Only modify ActiveSelection (marquee multi-select), not Media Groups or single objects\n if (activeObject instanceof ActiveSelection) {\n activeObject.controls.mt = disabledControl;\n activeObject.controls.mb = disabledControl;\n activeObject.controls.ml = disabledControl;\n activeObject.controls.mr = disabledControl;\n activeObject.controls.bl = disabledControl;\n activeObject.controls.br = disabledControl;\n activeObject.controls.tl = disabledControl;\n activeObject.controls.tr = disabledControl;\n activeObject.controls.mtr = rotateControl;\n canvasInstance.requestRenderAll();\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 }: BuildCanvasOptions) => {\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 if (twickCanvasRef.current) {\n twickCanvasRef.current.off(\"mouse:up\", handleMouseUp);\n twickCanvasRef.current.off(\"text:editing:exited\", onTextEdit);\n twickCanvasRef.current.off(\"object:moving\", handleObjectMoving);\n twickCanvasRef.current.off(\"selection:created\", applyMarqueeSelectionControls);\n twickCanvasRef.current.off(\"selection:updated\", applyMarqueeSelectionControls);\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 canvas?.on(\"object:moving\", handleObjectMoving);\n canvas?.on(\"selection:created\", applyMarqueeSelectionControls);\n canvas?.on(\"selection:updated\", applyMarqueeSelectionControls);\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 * When a marquee selection (ActiveSelection) is dragged or rotated, persists\n * each selected element's new position/rotation to the timeline.\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 const action = event.transform?.action;\n\n if (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\n const context = {\n canvasMetadata: canvasMetadataRef.current,\n videoSize: videoSizeRef.current,\n elementFrameMapRef: elementFrameMap,\n captionPropsRef,\n watermarkPropsRef,\n };\n\n // Marquee selection (ActiveSelection): persist each selected element to timeline\n if (object instanceof ActiveSelection && (action === \"drag\" || action === \"rotate\")) {\n const objects = object.getObjects();\n for (const fabricObj of objects) {\n const id = fabricObj.get(\"id\");\n if (!id || id === \"e-watermark\") continue;\n const currentElement = elementMap.current[id];\n if (!currentElement) continue;\n const handler = elementController.get(currentElement.type);\n const result = handler?.updateFromFabricObject?.(\n fabricObj,\n currentElement,\n context\n );\n if (result) {\n elementMap.current[id] = result.element;\n onCanvasOperation?.(\n result.operation ?? CANVAS_OPERATIONS.ITEM_UPDATED,\n result.payload ?? result.element\n );\n }\n }\n return;\n }\n\n // Single object\n switch (action) {\n case \"drag\":\n case \"scale\":\n case \"scaleX\":\n case \"scaleY\":\n case \"rotate\": {\n const currentElement = elementMap.current[elementId];\n const handler = elementController.get(\n elementId === \"e-watermark\" ? \"watermark\" : currentElement?.type\n );\n const result = handler?.updateFromFabricObject?.(object, currentElement ?? { id: elementId, type: \"text\", props: {} } as CanvasElement, context);\n if (result) {\n elementMap.current[elementId] = result.element;\n onCanvasOperation?.(\n result.operation ?? CANVAS_OPERATIONS.ITEM_UPDATED,\n result.payload ?? result.element\n );\n }\n break;\n }\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 watermark,\n seekTime = 0,\n captionProps,\n cleanAndAdd = false,\n lockAspectRatio,\n }: SetCanvasElementsOptions) => {\n if (!twickCanvas || !getCanvasContext(twickCanvas)) return;\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\n // Deduplicate elements by id to avoid rendering the same logical\n // element multiple times on the canvas (which could happen if the\n // caller accidentally passes duplicates).\n const uniqueElements: CanvasElement[] = [];\n const seenIds = new Set<string>();\n for (const el of elements) {\n if (!el || !el.id) continue;\n if (seenIds.has(el.id)) continue;\n seenIds.add(el.id);\n uniqueElements.push(el);\n }\n\n await Promise.all(\n uniqueElements.map(async (element, index) => {\n try {\n if (!element) return;\n const zOrder = element.zIndex ?? index;\n await addElementToCanvas({\n element,\n index: zOrder,\n reorder: false,\n seekTime,\n captionProps,\n lockAspectRatio,\n });\n } catch {\n // Skip element on add error\n }\n })\n );\n if (watermark) {\n addWatermarkToCanvas({\n element: watermark,\n });\n }\n reorderElementsByZIndex(twickCanvas);\n } catch {\n // Skip on 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 lockAspectRatio,\n }: AddElementToCanvasOptions) => {\n if (!twickCanvas) return;\n const handler = elementController.get(element.type);\n if (handler) {\n await handler.add({\n element,\n index,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n seekTime,\n captionProps: captionProps ?? null,\n elementFrameMapRef: elementFrameMap,\n getCurrentFrameEffect,\n lockAspectRatio: lockAspectRatio ?? element.props?.lockAspectRatio,\n });\n }\n elementMap.current[element.id] = { ...element, zIndex: element.zIndex ?? index };\n if (reorder) {\n reorderElementsByZIndex(twickCanvas);\n }\n };\n\n const addWatermarkToCanvas = ({ element }: AddWatermarkToCanvasOptions) => {\n if (!twickCanvas) return;\n const handler = elementController.get(\"watermark\");\n if (handler) {\n handler.add({\n element,\n index: Object.keys(elementMap.current).length,\n canvas: twickCanvas,\n canvasMetadata: canvasMetadataRef.current,\n watermarkPropsRef,\n });\n elementMap.current[element.id] = element;\n }\n };\n\n /**\n * Changes the canvas z-order of the element (Fabric display) and notifies timeline to reorder tracks.\n * Z-order is determined by track order; this emits Z_ORDER_CHANGED so the editor can move the element's track.\n */\n const applyZOrder = (elementId: string, direction: \"front\" | \"back\" | \"forward\" | \"backward\"): boolean => {\n if (!twickCanvas) return false;\n const newZIndex = changeZOrder(twickCanvas, elementId, direction);\n if (newZIndex == null) return false;\n const element = elementMap.current[elementId];\n if (element) elementMap.current[elementId] = { ...element, zIndex: newZIndex };\n onCanvasOperation?.(CANVAS_OPERATIONS.Z_ORDER_CHANGED, { elementId, direction });\n return true;\n };\n\n const bringToFront = (elementId: string) => applyZOrder(elementId, \"front\");\n const sendToBack = (elementId: string) => applyZOrder(elementId, \"back\");\n const bringForward = (elementId: string) => applyZOrder(elementId, \"forward\");\n const sendBackward = (elementId: string) => applyZOrder(elementId, \"backward\");\n\n /**\n * Updates the canvas background color (e.g. when project backgroundColor changes).\n * Keeps the editor canvas in sync with project/visualizer background.\n */\n const setBackgroundColor = useCallback((color: string) => {\n const canvas = twickCanvasRef.current;\n if (canvas) {\n canvas.backgroundColor = color;\n canvas.requestRenderAll();\n }\n }, []);\n\n return {\n twickCanvas,\n buildCanvas,\n resizeCanvas,\n setBackgroundColor,\n onVideoSizeChange,\n addWatermarkToCanvas,\n addElementToCanvas,\n setCanvasElements,\n bringToFront,\n sendToBack,\n bringForward,\n sendBackward,\n };\n};\n"],"names":["FabricCanvas","obj","Control","controlsUtils","Textbox","Shadow","FabricImage","Rect","Group","Circle","scaledW","scaledH","Triangle","useState","useRef","ActiveSelection","useCallback"],"mappings":"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"}
|