@pixerate/editor 0.14.3 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/agent/index.cjs +28 -220
  2. package/dist/agent/index.cjs.map +1 -1
  3. package/dist/agent/index.d.cts +1 -1
  4. package/dist/agent/index.d.ts +1 -1
  5. package/dist/agent/index.js +5 -154
  6. package/dist/agent/index.js.map +1 -1
  7. package/dist/canvas/index.cjs +113 -377
  8. package/dist/canvas/index.cjs.map +1 -1
  9. package/dist/canvas/index.d.cts +2 -2
  10. package/dist/canvas/index.d.ts +2 -2
  11. package/dist/canvas/index.js +18 -208
  12. package/dist/canvas/index.js.map +1 -1
  13. package/dist/chunk-33UVEDOS.cjs +233 -0
  14. package/dist/chunk-33UVEDOS.cjs.map +1 -0
  15. package/dist/chunk-7FWEWE6T.js +1888 -0
  16. package/dist/chunk-7FWEWE6T.js.map +1 -0
  17. package/dist/chunk-BYCOFZAA.js +514 -0
  18. package/dist/chunk-BYCOFZAA.js.map +1 -0
  19. package/dist/chunk-CYKQLDYH.cjs +1290 -0
  20. package/dist/chunk-CYKQLDYH.cjs.map +1 -0
  21. package/dist/chunk-GLSIZKOX.js +17 -0
  22. package/dist/chunk-GLSIZKOX.js.map +1 -0
  23. package/dist/chunk-GWHJVZPI.js +233 -0
  24. package/dist/chunk-GWHJVZPI.js.map +1 -0
  25. package/dist/chunk-LDES3EOS.cjs +1888 -0
  26. package/dist/chunk-LDES3EOS.cjs.map +1 -0
  27. package/dist/chunk-MPPXH7U5.cjs +336 -0
  28. package/dist/chunk-MPPXH7U5.cjs.map +1 -0
  29. package/dist/chunk-NNJRJJSC.js +1290 -0
  30. package/dist/chunk-NNJRJJSC.js.map +1 -0
  31. package/dist/chunk-O7LOPQMH.js +336 -0
  32. package/dist/chunk-O7LOPQMH.js.map +1 -0
  33. package/dist/chunk-SEDR75VH.cjs +17 -0
  34. package/dist/chunk-SEDR75VH.cjs.map +1 -0
  35. package/dist/chunk-SHWP2A43.cjs +514 -0
  36. package/dist/chunk-SHWP2A43.cjs.map +1 -0
  37. package/dist/chunk-VHZCXHS4.js +222 -0
  38. package/dist/chunk-VHZCXHS4.js.map +1 -0
  39. package/dist/chunk-WADCJ7A7.cjs +222 -0
  40. package/dist/chunk-WADCJ7A7.cjs.map +1 -0
  41. package/dist/extensions/index.cjs +25 -1920
  42. package/dist/extensions/index.cjs.map +1 -1
  43. package/dist/extensions/index.js +22 -1868
  44. package/dist/extensions/index.js.map +1 -1
  45. package/dist/grammar/index.cjs +44 -387
  46. package/dist/grammar/index.cjs.map +1 -1
  47. package/dist/grammar/index.js +23 -321
  48. package/dist/grammar/index.js.map +1 -1
  49. package/dist/history/index.cjs +6 -257
  50. package/dist/history/index.cjs.map +1 -1
  51. package/dist/history/index.js +4 -227
  52. package/dist/history/index.js.map +1 -1
  53. package/dist/image-editor/index.cjs +63 -106
  54. package/dist/image-editor/index.cjs.map +1 -1
  55. package/dist/index.cjs +324 -7232
  56. package/dist/index.cjs.map +1 -1
  57. package/dist/index.d.cts +0 -4
  58. package/dist/index.d.ts +0 -4
  59. package/dist/index.js +258 -7007
  60. package/dist/index.js.map +1 -1
  61. package/dist/serializers/index.cjs +28 -558
  62. package/dist/serializers/index.cjs.map +1 -1
  63. package/dist/serializers/index.js +16 -496
  64. package/dist/serializers/index.js.map +1 -1
  65. package/dist/spreadsheet/index.cjs +21 -1319
  66. package/dist/spreadsheet/index.cjs.map +1 -1
  67. package/dist/spreadsheet/index.js +14 -1274
  68. package/dist/spreadsheet/index.js.map +1 -1
  69. package/dist/{types-lGtRobRV.d.cts → types-DC8b2TsA.d.cts} +1 -1
  70. package/dist/{types-lGtRobRV.d.ts → types-DC8b2TsA.d.ts} +1 -1
  71. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/image-editor/index.ts","../../src/image-editor/filters.ts","../../src/image-editor/renderer.ts","../../src/image-editor/controller.ts","../../src/image-editor/geometry.ts"],"sourcesContent":["export * from './types';\nexport * from './filters';\nexport * from './renderer';\nexport * from './controller';\nexport * from './geometry';\n","import type { DepthMaskOptions, ImageAdjustments } from './types';\n\nfunction clamp(value: number, min = 0, max = 255): number {\n return Math.max(min, Math.min(max, value));\n}\n\n/**\n * Applies color and tone adjustments (brightness, contrast, saturation, exposure, temperature, opacity, blur)\n * directly to the pixel data of an ImageData buffer.\n */\nexport function applyAdjustments(imageData: ImageData, adjustments: ImageAdjustments): void {\n const { brightness, contrast, saturation, exposure, temperature, opacity, blur } = adjustments;\n const data = imageData.data;\n const len = data.length;\n\n const hasBrightness = brightness !== 0;\n const hasContrast = contrast !== 0;\n const hasSaturation = saturation !== 0;\n const hasExposure = exposure !== 0;\n const hasTemperature = temperature !== 0;\n const hasOpacity = opacity < 1;\n\n // Pre-calculate contrast factor\n const contrastFactor = hasContrast\n ? (259 * (contrast + 255)) / (255 * (259 - contrast))\n : 1;\n\n // Pre-calculate exposure multiplier\n const exposureMultiplier = hasExposure ? Math.pow(2, exposure / 50) : 1;\n\n // Pre-calculate saturation multiplier\n const satMultiplier = hasSaturation ? 1 + saturation / 100 : 1;\n\n // Pre-calculate temperature offsets\n const tempOffset = hasTemperature ? temperature * 0.8 : 0;\n\n if (hasBrightness || hasContrast || hasSaturation || hasExposure || hasTemperature || hasOpacity) {\n for (let i = 0; i < len; i += 4) {\n let r = data[i];\n let g = data[i + 1];\n let b = data[i + 2];\n let a = data[i + 3];\n\n // Exposure\n if (hasExposure) {\n r = clamp(r * exposureMultiplier);\n g = clamp(g * exposureMultiplier);\n b = clamp(b * exposureMultiplier);\n }\n\n // Brightness\n if (hasBrightness) {\n const bOffset = brightness * 2.55;\n r = clamp(r + bOffset);\n g = clamp(g + bOffset);\n b = clamp(b + bOffset);\n }\n\n // Contrast\n if (hasContrast) {\n r = clamp(contrastFactor * (r - 128) + 128);\n g = clamp(contrastFactor * (g - 128) + 128);\n b = clamp(contrastFactor * (b - 128) + 128);\n }\n\n // Saturation\n if (hasSaturation) {\n const luminance = 0.2989 * r + 0.587 * g + 0.114 * b;\n r = clamp(luminance + (r - luminance) * satMultiplier);\n g = clamp(luminance + (g - luminance) * satMultiplier);\n b = clamp(luminance + (b - luminance) * satMultiplier);\n }\n\n // Temperature / Warmth\n if (hasTemperature) {\n r = clamp(r + tempOffset);\n b = clamp(b - tempOffset);\n }\n\n // Opacity\n if (hasOpacity) {\n a = clamp(a * opacity);\n }\n\n data[i] = r;\n data[i + 1] = g;\n data[i + 2] = b;\n data[i + 3] = a;\n }\n }\n\n // Apply blur if requested\n if (blur > 0) {\n applyFastBoxBlur(imageData, Math.min(Math.round(blur), 40));\n }\n}\n\n/**\n * Fast separable box blur running horizontally and vertically.\n */\nfunction applyFastBoxBlur(imageData: ImageData, radius: number): void {\n if (radius < 1) return;\n const w = imageData.width;\n const h = imageData.height;\n const src = imageData.data;\n const temp = new Uint8ClampedArray(src.length);\n\n // Horizontal blur pass\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < w; x++) {\n let r = 0, g = 0, b = 0, a = 0;\n let count = 0;\n for (let k = -radius; k <= radius; k++) {\n const px = Math.min(w - 1, Math.max(0, x + k));\n const idx = (y * w + px) * 4;\n r += src[idx];\n g += src[idx + 1];\n b += src[idx + 2];\n a += src[idx + 3];\n count++;\n }\n const dstIdx = (y * w + x) * 4;\n temp[dstIdx] = Math.round(r / count);\n temp[dstIdx + 1] = Math.round(g / count);\n temp[dstIdx + 2] = Math.round(b / count);\n temp[dstIdx + 3] = Math.round(a / count);\n }\n }\n\n // Vertical blur pass back into src\n for (let x = 0; x < w; x++) {\n for (let y = 0; y < h; y++) {\n let r = 0, g = 0, b = 0, a = 0;\n let count = 0;\n for (let k = -radius; k <= radius; k++) {\n const py = Math.min(h - 1, Math.max(0, y + k));\n const idx = (py * w + x) * 4;\n r += temp[idx];\n g += temp[idx + 1];\n b += temp[idx + 2];\n a += temp[idx + 3];\n count++;\n }\n const dstIdx = (y * w + x) * 4;\n src[dstIdx] = Math.round(r / count);\n src[dstIdx + 1] = Math.round(g / count);\n src[dstIdx + 2] = Math.round(b / count);\n src[dstIdx + 3] = Math.round(a / count);\n }\n }\n}\n\n/**\n * Blends a depth/alpha mask into the main image data based on Gleamforge depth masking.\n * Supports threshold range with softness gradation, brightness mode, and optional inversion.\n */\nexport function applyDepthMask(\n mainImageData: ImageData,\n maskImageData: ImageData,\n options: DepthMaskOptions\n): void {\n const { mode, depthRange, softness, invert } = options;\n const mainData = mainImageData.data;\n const maskData = maskImageData.data;\n const len = Math.min(mainData.length, maskData.length);\n const effectiveSoftness = Math.max(1, softness || 10);\n const minDepth = depthRange[0];\n const maxDepth = depthRange[1];\n\n for (let i = 0; i < len; i += 4) {\n let maskValue = maskData[i]; // Use red channel of mask (or grayscale)\n if (invert) {\n maskValue = 255 - maskValue;\n }\n\n let alphaValue: number;\n\n if (mode === 'threshold') {\n if (maskValue >= maxDepth + effectiveSoftness) {\n alphaValue = 0;\n } else if (maskValue > maxDepth) {\n alphaValue = Math.round(((maxDepth + effectiveSoftness - maskValue) / effectiveSoftness) * 255);\n } else if (maskValue >= minDepth) {\n alphaValue = 255;\n } else if (maskValue < minDepth - effectiveSoftness) {\n alphaValue = 0;\n } else {\n alphaValue = Math.round(((maskValue - (minDepth - effectiveSoftness)) / effectiveSoftness) * 255);\n }\n } else {\n // Brightness mode\n alphaValue = clamp(maskValue + minDepth);\n }\n\n // Multiply existing alpha with mask alpha\n mainData[i + 3] = clamp((mainData[i + 3] * alphaValue) / 255);\n }\n}\n\n/**\n * Removes alpha channel by setting all alpha bytes to 255 (completely opaque).\n * From Gleamforge image-filters.ts.\n */\nexport function removeAlpha(imageData: ImageData): void {\n const data = imageData.data;\n const len = data.length;\n for (let i = 3; i < len; i += 4) {\n data[i] = 255;\n }\n}\n","import type {\n Annotation,\n CropBox,\n ExportOptions,\n ImageEditorState,\n ImageTransform,\n PenAnnotation,\n RectAnnotation,\n CircleAnnotation,\n ArrowAnnotation,\n LineAnnotation,\n TextAnnotation,\n ImageAnnotation,\n} from './types';\nimport { applyAdjustments, applyDepthMask } from './filters';\n\nexport interface RenderOptions {\n viewportWidth?: number;\n viewportHeight?: number;\n showCropOverlay?: boolean;\n showSelectionOverlay?: boolean;\n interactive?: boolean;\n draftAnnotation?: Annotation | null;\n draftCrop?: CropBox | null;\n}\n\n/**\n * Image Cache to avoid refetching HTMLImageElement objects repeatedly during re-renders.\n */\nclass ImageCache {\n private cache = new Map<string, HTMLImageElement>();\n\n public async get(src: string): Promise<HTMLImageElement> {\n const existing = this.cache.get(src);\n if (existing && existing.complete) {\n return existing;\n }\n\n return new Promise((resolve, reject) => {\n if (typeof window === 'undefined' || typeof Image === 'undefined') {\n reject(new Error('Image loading is only supported in browser/DOM environments'));\n return;\n }\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => {\n this.cache.set(src, img);\n resolve(img);\n };\n img.onerror = (e) => reject(new Error(`Failed to load image from: ${src}`));\n img.src = src;\n\n // In JSDOM or test environments where Image resource loading is inactive, resolve promptly\n if (typeof navigator !== 'undefined' && /jsdom/i.test(navigator.userAgent)) {\n setTimeout(() => {\n try {\n Object.defineProperty(img, 'naturalWidth', { value: 800, configurable: true });\n Object.defineProperty(img, 'naturalHeight', { value: 600, configurable: true });\n Object.defineProperty(img, 'complete', { value: true, configurable: true });\n } catch {\n // Ignore definition errors in restrictive environments\n }\n this.cache.set(src, img);\n resolve(img);\n }, 10);\n }\n });\n }\n\n public set(src: string, img: HTMLImageElement): void {\n this.cache.set(src, img);\n }\n\n public clear(): void {\n this.cache.clear();\n }\n}\n\nexport const globalImageCache = new ImageCache();\n\n/**\n * Renders an image with all its transformations, filters, depth masks, and annotations\n * to a target HTMLCanvasElement.\n */\nexport class ImageEditorRenderer {\n public imageCache = globalImageCache;\n private cachedBaseCanvas: HTMLCanvasElement | null = null;\n private cachedBaseKey = '';\n\n /**\n * Helper to create a canvas element safely in browser/JSDOM.\n */\n public createCanvas(width: number, height: number): HTMLCanvasElement {\n if (typeof document !== 'undefined' && document.createElement) {\n const c = document.createElement('canvas');\n c.width = Math.max(1, Math.round(width));\n c.height = Math.max(1, Math.round(height));\n return c;\n }\n // Fallback for non-DOM / headless\n return {\n width: Math.max(1, Math.round(width)),\n height: Math.max(1, Math.round(height)),\n getContext: () => null,\n toDataURL: () => '',\n toBlob: () => {},\n } as unknown as HTMLCanvasElement;\n }\n\n /**\n * Renders the editor state onto a canvas (viewport or preview).\n */\n public async renderToCanvas(\n targetCanvas: HTMLCanvasElement,\n imageSource: CanvasImageSource | string,\n state: ImageEditorState,\n options: RenderOptions = {}\n ): Promise<void> {\n const ctx = targetCanvas.getContext('2d');\n if (!ctx) return;\n\n let img: CanvasImageSource;\n if (typeof imageSource === 'string') {\n img = await this.imageCache.get(imageSource);\n } else {\n img = imageSource;\n }\n\n const naturalWidth = (img as any).naturalWidth || (img as any).videoWidth || (img as any).width || 800;\n const naturalHeight = (img as any).naturalHeight || (img as any).videoHeight || (img as any).height || 600;\n\n const { crop, transform, adjustments, depthMask, annotations, selectedAnnotationId, zoom, pan, isOriginalCompared } = state;\n\n // Viewport dimensions\n const vpWidth = options.viewportWidth || targetCanvas.width;\n const vpHeight = options.viewportHeight || targetCanvas.height;\n\n targetCanvas.width = vpWidth;\n targetCanvas.height = vpHeight;\n\n ctx.clearRect(0, 0, vpWidth, vpHeight);\n\n // If in compare mode, render original image directly and exit\n if (isOriginalCompared) {\n this.drawFittedImage(ctx, img, naturalWidth, naturalHeight, vpWidth, vpHeight, zoom, pan);\n return;\n }\n\n // Determine working base dimensions based on crop or natural\n // When actively in crop tool or overlay requested, show full image so user can adjust the crop box\n const isActivelyCropping = Boolean(options.showCropOverlay || state.activeTool === 'crop');\n const cropX = (!isActivelyCropping && crop) ? Math.max(0, crop.x) : 0;\n const cropY = (!isActivelyCropping && crop) ? Math.max(0, crop.y) : 0;\n const cropW = (!isActivelyCropping && crop) ? Math.min(naturalWidth - cropX, crop.width) : naturalWidth;\n const cropH = (!isActivelyCropping && crop) ? Math.min(naturalHeight - cropY, crop.height) : naturalHeight;\n\n const angle = ((transform.rotate % 360) + 360) % 360;\n const isSwapped = angle === 90 || angle === 270;\n const baseW = isSwapped ? cropH : cropW;\n const baseH = isSwapped ? cropW : cropH;\n\n // 1. Offscreen canvas for Base Image + Transform (Flip/Rotate) + Crop + Adjustments\n const baseKey = `${state.sourceUrl}-${naturalWidth}x${naturalHeight}-${cropX},${cropY},${cropW},${cropH}-${angle},${transform.flipH},${transform.flipV}-${adjustments.brightness},${adjustments.contrast},${adjustments.saturation},${adjustments.exposure},${adjustments.temperature},${adjustments.blur},${adjustments.opacity}-${depthMask.enabled},${depthMask.depthRange?.[0]}-${depthMask.depthRange?.[1]},${depthMask.softness},${depthMask.invert},${depthMask.mode}`;\n\n let baseCanvas = this.cachedBaseCanvas;\n if (!baseCanvas || this.cachedBaseKey !== baseKey) {\n baseCanvas = this.createCanvas(baseW, baseH);\n const baseCtx = baseCanvas.getContext('2d');\n\n if (baseCtx) {\n baseCtx.save();\n\n // Handle Flip & Rotate\n const rad = (angle * Math.PI) / 180;\n baseCtx.translate(baseW / 2, baseH / 2);\n baseCtx.rotate(rad);\n baseCtx.scale(transform.flipH ? -1 : 1, transform.flipV ? -1 : 1);\n\n // Draw the cropped portion centered\n baseCtx.drawImage(\n img,\n cropX, cropY, cropW, cropH,\n -cropW / 2, -cropH / 2, cropW, cropH\n );\n\n baseCtx.restore();\n\n // 2. Pixel adjustments & Depth Masking\n const imageData = baseCtx.getImageData(0, 0, baseW, baseH);\n if (imageData) {\n applyAdjustments(imageData, adjustments);\n\n if (depthMask.enabled && depthMask.maskSource) {\n try {\n const maskImg = await this.imageCache.get(depthMask.maskSource);\n const maskCanvas = this.createCanvas(baseW, baseH);\n const maskCtx = maskCanvas.getContext('2d');\n if (maskCtx) {\n maskCtx.drawImage(maskImg, cropX, cropY, cropW, cropH, 0, 0, baseW, baseH);\n const maskData = maskCtx.getImageData(0, 0, baseW, baseH);\n if (maskData) {\n applyDepthMask(imageData, maskData, depthMask);\n }\n }\n } catch {\n // Mask failed to load; continue without crashing\n }\n }\n\n baseCtx.putImageData(imageData, 0, 0);\n }\n }\n this.cachedBaseCanvas = baseCanvas;\n this.cachedBaseKey = baseKey;\n }\n\n // 3. Render base canvas onto target canvas with viewport zoom & pan\n ctx.save();\n // Center of canvas\n ctx.translate(vpWidth / 2 + pan.x, vpHeight / 2 + pan.y);\n ctx.scale(zoom, zoom);\n\n // Calculate fitted dimensions\n const fitScale = Math.min((vpWidth * 0.85) / baseW, (vpHeight * 0.85) / baseH, 1);\n const renderW = baseW * fitScale;\n const renderH = baseH * fitScale;\n\n // Draw base image\n ctx.drawImage(baseCanvas, -renderW / 2, -renderH / 2, renderW, renderH);\n\n // 4. Render Annotations scaled to fit image space\n const scaleFactor = fitScale;\n ctx.save();\n ctx.translate(-renderW / 2, -renderH / 2);\n ctx.scale(scaleFactor, scaleFactor);\n\n for (const ann of annotations) {\n await this.drawAnnotation(ctx, ann);\n if (options.showSelectionOverlay && ann.id === selectedAnnotationId) {\n this.drawAnnotationSelection(ctx, ann);\n }\n }\n\n // Render active live draft annotation\n if (options.draftAnnotation) {\n await this.drawAnnotation(ctx, options.draftAnnotation);\n }\n\n ctx.restore();\n\n ctx.restore();\n\n // 5. Draw Crop Grid Overlay if crop overlay is requested\n const isCroppingActive = Boolean(options.showCropOverlay || state.activeTool === 'crop');\n const activeCrop =\n (options.draftCrop !== undefined && options.draftCrop !== null)\n ? options.draftCrop\n : (crop ?? (isCroppingActive ? {\n x: 0,\n y: 0,\n width: naturalWidth,\n height: naturalHeight,\n } : null));\n\n if (isCroppingActive && activeCrop) {\n this.drawCropOverlay(ctx, state, vpWidth, vpHeight, naturalWidth, naturalHeight, activeCrop);\n }\n }\n\n /**\n * Draws an individual annotation onto the canvas.\n */\n public async drawAnnotation(ctx: CanvasRenderingContext2D, ann: Annotation): Promise<void> {\n ctx.save();\n if (ann.opacity !== undefined) {\n ctx.globalAlpha = ann.opacity;\n }\n\n if (ann.rotation) {\n ctx.translate(ann.x, ann.y);\n ctx.rotate((ann.rotation * Math.PI) / 180);\n ctx.translate(-ann.x, -ann.y);\n }\n\n switch (ann.type) {\n case 'pen': {\n const pen = ann as PenAnnotation;\n if (pen.points.length > 0) {\n ctx.beginPath();\n ctx.strokeStyle = pen.strokeColor;\n ctx.lineWidth = pen.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.moveTo(pen.points[0].x, pen.points[0].y);\n for (let i = 1; i < pen.points.length; i++) {\n ctx.lineTo(pen.points[i].x, pen.points[i].y);\n }\n ctx.stroke();\n }\n break;\n }\n\n case 'rect': {\n const rect = ann as RectAnnotation;\n ctx.strokeStyle = rect.strokeColor;\n ctx.lineWidth = rect.strokeWidth;\n if (rect.fillColor) {\n ctx.fillStyle = rect.fillColor;\n ctx.fillRect(rect.x, rect.y, rect.width, rect.height);\n }\n if (rect.strokeWidth > 0) {\n ctx.strokeRect(rect.x, rect.y, rect.width, rect.height);\n }\n break;\n }\n\n case 'circle': {\n const circle = ann as CircleAnnotation;\n ctx.beginPath();\n ctx.strokeStyle = circle.strokeColor;\n ctx.lineWidth = circle.strokeWidth;\n ctx.ellipse(circle.x, circle.y, Math.abs(circle.radiusX), Math.abs(circle.radiusY), 0, 0, 2 * Math.PI);\n if (circle.fillColor) {\n ctx.fillStyle = circle.fillColor;\n ctx.fill();\n }\n if (circle.strokeWidth > 0) {\n ctx.stroke();\n }\n break;\n }\n\n case 'arrow': {\n const arrow = ann as ArrowAnnotation;\n ctx.strokeStyle = arrow.strokeColor;\n ctx.fillStyle = arrow.strokeColor;\n ctx.lineWidth = arrow.strokeWidth;\n\n // Line body\n ctx.beginPath();\n ctx.moveTo(arrow.x, arrow.y);\n ctx.lineTo(arrow.endX, arrow.endY);\n ctx.stroke();\n\n // Arrow head\n const angle = Math.atan2(arrow.endY - arrow.y, arrow.endX - arrow.x);\n const headLen = Math.max(12, arrow.strokeWidth * 3);\n ctx.beginPath();\n ctx.moveTo(arrow.endX, arrow.endY);\n ctx.lineTo(\n arrow.endX - headLen * Math.cos(angle - Math.PI / 6),\n arrow.endY - headLen * Math.sin(angle - Math.PI / 6)\n );\n ctx.lineTo(\n arrow.endX - headLen * Math.cos(angle + Math.PI / 6),\n arrow.endY - headLen * Math.sin(angle + Math.PI / 6)\n );\n ctx.closePath();\n ctx.fill();\n break;\n }\n\n case 'line': {\n const line = ann as LineAnnotation;\n ctx.beginPath();\n ctx.strokeStyle = line.strokeColor;\n ctx.lineWidth = line.strokeWidth;\n ctx.moveTo(line.x, line.y);\n ctx.lineTo(line.endX, line.endY);\n ctx.stroke();\n break;\n }\n\n case 'text': {\n const textAnn = ann as TextAnnotation;\n ctx.font = `${textAnn.fontWeight || 'normal'} ${textAnn.fontSize}px ${textAnn.fontFamily || 'sans-serif'}`;\n ctx.fillStyle = textAnn.color;\n ctx.textBaseline = 'top';\n ctx.fillText(textAnn.text, textAnn.x, textAnn.y);\n break;\n }\n\n case 'image': {\n const imgAnn = ann as ImageAnnotation;\n try {\n const loadedImg = await this.imageCache.get(imgAnn.src);\n ctx.drawImage(loadedImg, imgAnn.x, imgAnn.y, imgAnn.width, imgAnn.height);\n } catch {\n // Skip if watermark image failed to load\n }\n break;\n }\n }\n\n ctx.restore();\n }\n\n /**\n * Draws selection box around active annotation.\n */\n private drawAnnotationSelection(ctx: CanvasRenderingContext2D, ann: Annotation): void {\n ctx.save();\n ctx.strokeStyle = '#3b82f6'; // Tailwind blue-500\n ctx.lineWidth = 1.5;\n ctx.setLineDash([4, 4]);\n\n const bounds = this.getAnnotationBounds(ann);\n const pad = 4;\n ctx.strokeRect(bounds.x - pad, bounds.y - pad, bounds.width + pad * 2, bounds.height + pad * 2);\n\n // Draw handles\n ctx.fillStyle = '#ffffff';\n ctx.strokeStyle = '#3b82f6';\n ctx.setLineDash([]);\n const handleSize = 6;\n const corners = [\n { x: bounds.x - pad, y: bounds.y - pad },\n { x: bounds.x + bounds.width + pad, y: bounds.y - pad },\n { x: bounds.x - pad, y: bounds.y + bounds.height + pad },\n { x: bounds.x + bounds.width + pad, y: bounds.y + bounds.height + pad },\n ];\n for (const c of corners) {\n ctx.fillRect(c.x - handleSize / 2, c.y - handleSize / 2, handleSize, handleSize);\n ctx.strokeRect(c.x - handleSize / 2, c.y - handleSize / 2, handleSize, handleSize);\n }\n\n ctx.restore();\n }\n\n public getAnnotationBounds(ann: Annotation): { x: number; y: number; width: number; height: number } {\n switch (ann.type) {\n case 'rect':\n return { x: ann.x, y: ann.y, width: ann.width, height: ann.height };\n case 'circle':\n return {\n x: ann.x - Math.abs(ann.radiusX),\n y: ann.y - Math.abs(ann.radiusY),\n width: Math.abs(ann.radiusX) * 2,\n height: Math.abs(ann.radiusY) * 2,\n };\n case 'arrow':\n case 'line': {\n const minX = Math.min(ann.x, ann.endX);\n const minY = Math.min(ann.y, ann.endY);\n return {\n x: minX,\n y: minY,\n width: Math.max(Math.abs(ann.endX - ann.x), 10),\n height: Math.max(Math.abs(ann.endY - ann.y), 10),\n };\n }\n case 'pen': {\n if (!ann.points.length) return { x: ann.x, y: ann.y, width: 0, height: 0 };\n let minX = ann.points[0].x, maxX = ann.points[0].x;\n let minY = ann.points[0].y, maxY = ann.points[0].y;\n for (const pt of ann.points) {\n if (pt.x < minX) minX = pt.x;\n if (pt.x > maxX) maxX = pt.x;\n if (pt.y < minY) minY = pt.y;\n if (pt.y > maxY) maxY = pt.y;\n }\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n }\n case 'text':\n return {\n x: ann.x,\n y: ann.y,\n width: (ann.text.length * ann.fontSize * 0.6),\n height: ann.fontSize * 1.2,\n };\n case 'image':\n return { x: ann.x, y: ann.y, width: ann.width, height: ann.height };\n }\n }\n\n /**\n * Draws a rule-of-thirds crop grid overlay.\n */\n private drawCropOverlay(\n ctx: CanvasRenderingContext2D,\n state: ImageEditorState,\n vpWidth: number,\n vpHeight: number,\n imgWidth: number,\n imgHeight: number,\n cropOverride?: CropBox | null\n ): void {\n const crop =\n (cropOverride !== undefined && cropOverride !== null)\n ? cropOverride\n : (state.crop ?? {\n x: 0,\n y: 0,\n width: imgWidth,\n height: imgHeight,\n });\n if (!crop) return;\n\n const { transform, pan, zoom } = state;\n const angle = ((transform.rotate % 360) + 360) % 360;\n const isSwapped = angle === 90 || angle === 270;\n const baseW = isSwapped ? imgHeight : imgWidth;\n const baseH = isSwapped ? imgWidth : imgHeight;\n\n ctx.save();\n ctx.translate(vpWidth / 2 + pan.x, vpHeight / 2 + pan.y);\n ctx.scale(zoom, zoom);\n\n const fitScale = Math.min((vpWidth * 0.85) / baseW, (vpHeight * 0.85) / baseH, 1);\n const renderW = baseW * fitScale;\n const renderH = baseH * fitScale;\n\n const startX = -renderW / 2 + (crop.x / imgWidth) * renderW;\n const startY = -renderH / 2 + (crop.y / imgHeight) * renderH;\n const cropBoxW = (crop.width / imgWidth) * renderW;\n const cropBoxH = (crop.height / imgHeight) * renderH;\n\n // Dark semi-transparent scrim outside crop box\n ctx.fillStyle = 'rgba(0, 0, 0, 0.45)';\n // Top\n ctx.fillRect(-renderW / 2, -renderH / 2, renderW, startY - (-renderH / 2));\n // Bottom\n ctx.fillRect(-renderW / 2, startY + cropBoxH, renderW, renderH / 2 - (startY + cropBoxH));\n // Left\n ctx.fillRect(-renderW / 2, startY, startX - (-renderW / 2), cropBoxH);\n // Right\n ctx.fillRect(startX + cropBoxW, startY, renderW / 2 - (startX + cropBoxW), cropBoxH);\n\n // Crop border\n ctx.strokeStyle = '#ffffff';\n ctx.lineWidth = 2;\n ctx.strokeRect(startX, startY, cropBoxW, cropBoxH);\n\n // Rule of thirds lines\n ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)';\n ctx.lineWidth = 1;\n ctx.beginPath();\n // Vertical lines\n ctx.moveTo(startX + cropBoxW / 3, startY);\n ctx.lineTo(startX + cropBoxW / 3, startY + cropBoxH);\n ctx.moveTo(startX + (cropBoxW * 2) / 3, startY);\n ctx.lineTo(startX + (cropBoxW * 2) / 3, startY + cropBoxH);\n // Horizontal lines\n ctx.moveTo(startX, startY + cropBoxH / 3);\n ctx.lineTo(startX + cropBoxW, startY + cropBoxH / 3);\n ctx.moveTo(startX, startY + (cropBoxH * 2) / 3);\n ctx.lineTo(startX + cropBoxW, startY + (cropBoxH * 2) / 3);\n ctx.stroke();\n\n // Corner and edge handles\n ctx.fillStyle = '#ffffff';\n ctx.strokeStyle = '#0284c7';\n ctx.lineWidth = 1.5;\n const handleSize = 10;\n const handles = [\n // Corners\n { x: startX, y: startY },\n { x: startX + cropBoxW, y: startY },\n { x: startX, y: startY + cropBoxH },\n { x: startX + cropBoxW, y: startY + cropBoxH },\n // Edges\n { x: startX + cropBoxW / 2, y: startY },\n { x: startX + cropBoxW / 2, y: startY + cropBoxH },\n { x: startX, y: startY + cropBoxH / 2 },\n { x: startX + cropBoxW, y: startY + cropBoxH / 2 },\n ];\n for (const h of handles) {\n ctx.fillRect(h.x - handleSize / 2, h.y - handleSize / 2, handleSize, handleSize);\n ctx.strokeRect(h.x - handleSize / 2, h.y - handleSize / 2, handleSize, handleSize);\n }\n\n ctx.restore();\n }\n\n private drawFittedImage(\n ctx: CanvasRenderingContext2D,\n img: CanvasImageSource,\n imgW: number,\n imgH: number,\n vpW: number,\n vpH: number,\n zoom: number,\n pan: { x: number; y: number }\n ): void {\n ctx.save();\n ctx.translate(vpW / 2 + pan.x, vpH / 2 + pan.y);\n ctx.scale(zoom, zoom);\n const fitScale = Math.min((vpW * 0.85) / imgW, (vpH * 0.85) / imgH, 1);\n const renderW = imgW * fitScale;\n const renderH = imgH * fitScale;\n ctx.drawImage(img, -renderW / 2, -renderH / 2, renderW, renderH);\n ctx.restore();\n }\n\n /**\n * Performs an export render at exact pixel dimensions without zoom/pan viewport offsets.\n */\n public async renderExport(\n imageSource: CanvasImageSource | string,\n state: ImageEditorState,\n options: ExportOptions = {}\n ): Promise<HTMLCanvasElement> {\n let img: CanvasImageSource;\n if (typeof imageSource === 'string') {\n img = await this.imageCache.get(imageSource);\n } else {\n img = imageSource;\n }\n\n const naturalWidth = (img as any).naturalWidth || (img as any).videoWidth || (img as any).width || 800;\n const naturalHeight = (img as any).naturalHeight || (img as any).videoHeight || (img as any).height || 600;\n\n const { crop, transform, adjustments, depthMask, annotations } = state;\n\n const cropX = crop ? Math.max(0, crop.x) : 0;\n const cropY = crop ? Math.max(0, crop.y) : 0;\n const cropW = crop ? Math.min(naturalWidth - cropX, crop.width) : naturalWidth;\n const cropH = crop ? Math.min(naturalHeight - cropY, crop.height) : naturalHeight;\n\n const angle = ((transform.rotate % 360) + 360) % 360;\n const isSwapped = angle === 90 || angle === 270;\n const baseW = isSwapped ? cropH : cropW;\n const baseH = isSwapped ? cropW : cropH;\n\n const exportW = options.width || baseW;\n const exportH = options.height || baseH;\n\n const exportCanvas = this.createCanvas(exportW, exportH);\n const ctx = exportCanvas.getContext('2d');\n if (!ctx) return exportCanvas;\n\n // Draw transformed base image\n const intermediateCanvas = this.createCanvas(baseW, baseH);\n const interCtx = intermediateCanvas.getContext('2d');\n if (interCtx) {\n interCtx.save();\n const rad = (angle * Math.PI) / 180;\n interCtx.translate(baseW / 2, baseH / 2);\n interCtx.rotate(rad);\n interCtx.scale(transform.flipH ? -1 : 1, transform.flipV ? -1 : 1);\n interCtx.drawImage(img, cropX, cropY, cropW, cropH, -cropW / 2, -cropH / 2, cropW, cropH);\n interCtx.restore();\n\n // Apply adjustments\n const imageData = interCtx.getImageData(0, 0, baseW, baseH);\n if (imageData) {\n applyAdjustments(imageData, adjustments);\n\n if (depthMask.enabled && depthMask.maskSource) {\n try {\n const maskImg = await this.imageCache.get(depthMask.maskSource);\n const maskCanvas = this.createCanvas(baseW, baseH);\n const maskCtx = maskCanvas.getContext('2d');\n if (maskCtx) {\n maskCtx.drawImage(maskImg, cropX, cropY, cropW, cropH, 0, 0, baseW, baseH);\n const maskData = maskCtx.getImageData(0, 0, baseW, baseH);\n if (maskData) {\n applyDepthMask(imageData, maskData, depthMask);\n }\n }\n } catch {\n // Mask skip\n }\n }\n\n interCtx.putImageData(imageData, 0, 0);\n }\n }\n\n // Scale to target export dimensions\n ctx.drawImage(intermediateCanvas, 0, 0, baseW, baseH, 0, 0, exportW, exportH);\n\n // Draw annotations if included\n if (options.includeAnnotations !== false && annotations.length > 0) {\n const annScaleX = exportW / baseW;\n const annScaleY = exportH / baseH;\n ctx.save();\n ctx.scale(annScaleX, annScaleY);\n for (const ann of annotations) {\n await this.drawAnnotation(ctx, ann);\n }\n ctx.restore();\n }\n\n return exportCanvas;\n }\n\n /**\n * Generates a binary mask canvas (white for mask/selected area, black for unmasked).\n * Useful for AI inpainting and selective generative fill.\n */\n public async renderMask(\n state: ImageEditorState,\n options?: { useAnnotations?: boolean; useCrop?: boolean }\n ): Promise<HTMLCanvasElement> {\n const naturalWidth = state.imageDimensions.width;\n const naturalHeight = state.imageDimensions.height;\n const maskCanvas = this.createCanvas(naturalWidth, naturalHeight);\n const ctx = maskCanvas.getContext('2d');\n if (!ctx) return maskCanvas;\n\n // Background: unmasked (black)\n ctx.fillStyle = '#000000';\n ctx.fillRect(0, 0, naturalWidth, naturalHeight);\n\n const useCrop = options?.useCrop ?? true;\n const useAnnotations = options?.useAnnotations ?? true;\n\n // Crop box mask (if active)\n if (useCrop && state.crop) {\n ctx.fillStyle = '#ffffff';\n ctx.fillRect(state.crop.x, state.crop.y, state.crop.width, state.crop.height);\n }\n\n // Annotation masks (drawn in solid white)\n if (useAnnotations && state.annotations.length > 0) {\n for (const ann of state.annotations) {\n ctx.save();\n if (ann.rotation) {\n ctx.translate(ann.x, ann.y);\n ctx.rotate((ann.rotation * Math.PI) / 180);\n ctx.translate(-ann.x, -ann.y);\n }\n\n if (ann.type === 'pen') {\n const pen = ann as PenAnnotation;\n if (pen.points.length > 0) {\n ctx.beginPath();\n ctx.strokeStyle = '#ffffff';\n ctx.lineWidth = Math.max(pen.strokeWidth, 8);\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.moveTo(pen.points[0].x, pen.points[0].y);\n for (let i = 1; i < pen.points.length; i++) {\n ctx.lineTo(pen.points[i].x, pen.points[i].y);\n }\n ctx.stroke();\n }\n } else if (ann.type === 'rect') {\n const rect = ann as RectAnnotation;\n ctx.fillStyle = '#ffffff';\n ctx.fillRect(rect.x, rect.y, rect.width, rect.height);\n } else if (ann.type === 'circle') {\n const circ = ann as CircleAnnotation;\n ctx.fillStyle = '#ffffff';\n ctx.beginPath();\n ctx.ellipse(circ.x, circ.y, circ.radiusX, circ.radiusY, 0, 0, Math.PI * 2);\n ctx.fill();\n } else {\n ctx.strokeStyle = '#ffffff';\n ctx.fillStyle = '#ffffff';\n await this.drawAnnotation(ctx, ann);\n }\n ctx.restore();\n }\n }\n\n return maskCanvas;\n }\n}\n\n","import {\n Annotation,\n CreateAnnotationPayload,\n AspectRatio,\n CropBox,\n DepthMaskOptions,\n ExportOptions,\n ImageAdjustments,\n ImageDimensions,\n ImageEditorOptions,\n ImageEditorState,\n ImageEditorTool,\n ImageTransform,\n ImagePoint,\n SerializedImageEditorState,\n} from './types';\nimport { ImageEditorRenderer, RenderOptions } from './renderer';\n\nexport const DEFAULT_ADJUSTMENTS: ImageAdjustments = {\n brightness: 0,\n contrast: 0,\n saturation: 0,\n exposure: 0,\n temperature: 0,\n blur: 0,\n opacity: 1,\n};\n\nexport const DEFAULT_TRANSFORM: ImageTransform = {\n rotate: 0,\n flipH: false,\n flipV: false,\n};\n\nexport const DEFAULT_DEPTH_MASK: DepthMaskOptions = {\n enabled: false,\n maskSource: null,\n mode: 'threshold',\n depthRange: [0, 255],\n softness: 10,\n invert: false,\n};\n\n/**\n * ImageEditorController manages the complete lifecycle, canvas rendering pipeline,\n * tool interaction states, undo/redo history, serialization, and image export.\n */\nexport class ImageEditorController {\n private state: ImageEditorState;\n private renderer: ImageEditorRenderer;\n private imageElement: CanvasImageSource | null = null;\n private listeners = new Set<(state: ImageEditorState) => void>();\n private onStateChange?: (state: ImageEditorState) => void;\n\n // History stacks\n private undoStack: SerializedImageEditorState[] = [];\n private redoStack: SerializedImageEditorState[] = [];\n private maxHistoryDepth: number;\n private isApplyingHistory = false;\n private initialSnapshot: SerializedImageEditorState;\n\n constructor(options: ImageEditorOptions = {}) {\n this.maxHistoryDepth = options.maxHistoryDepth ?? 50;\n this.onStateChange = options.onStateChange;\n this.renderer = new ImageEditorRenderer();\n\n const initialAdjustments: ImageAdjustments = {\n ...DEFAULT_ADJUSTMENTS,\n ...options.initialState?.adjustments,\n };\n\n const initialTransform: ImageTransform = {\n ...DEFAULT_TRANSFORM,\n ...options.initialState?.transform,\n };\n\n const initialDepthMask: DepthMaskOptions = {\n ...DEFAULT_DEPTH_MASK,\n ...options.initialState?.depthMask,\n };\n\n this.state = {\n sourceUrl: typeof options.image === 'string' ? options.image : null,\n imageDimensions: { width: 800, height: 600 },\n crop: options.initialState?.crop ?? null,\n transform: initialTransform,\n adjustments: initialAdjustments,\n depthMask: initialDepthMask,\n annotations: options.initialState?.annotations ?? [],\n selectedAnnotationId: null,\n activeTool: 'select',\n zoom: 1,\n pan: { x: 0, y: 0 },\n isOriginalCompared: false,\n };\n\n if (options.image instanceof Object && 'width' in options.image) {\n this.imageElement = options.image as CanvasImageSource;\n this.state.imageDimensions = {\n width: (options.image as any).naturalWidth || (options.image as any).width || 800,\n height: (options.image as any).naturalHeight || (options.image as any).height || 600,\n };\n }\n\n this.initialSnapshot = this.serializeState();\n\n if (typeof options.image === 'string') {\n this.loadImage(options.image);\n }\n }\n\n // --- Image Loading ---\n\n public async loadImage(src: string | HTMLImageElement): Promise<void> {\n if (typeof src === 'string') {\n this.state.sourceUrl = src;\n try {\n const img = await this.renderer.imageCache.get(src);\n this.imageElement = img;\n this.state.imageDimensions = {\n width: img.naturalWidth || img.width || 800,\n height: img.naturalHeight || img.height || 600,\n };\n } catch (err) {\n // In SSR / non-DOM environments, retain default dimensions\n }\n } else {\n this.imageElement = src;\n this.state.sourceUrl = (src as any).src || null;\n this.state.imageDimensions = {\n width: src.naturalWidth || src.width || 800,\n height: src.naturalHeight || src.height || 600,\n };\n }\n\n this.initialSnapshot = this.serializeState();\n this.notify();\n }\n\n public getImageElement(): CanvasImageSource | null {\n return this.imageElement;\n }\n\n // --- State & Subscription ---\n\n public getState(): ImageEditorState {\n return this.state;\n }\n\n public subscribe(listener: (state: ImageEditorState) => void): () => void {\n this.listeners.add(listener);\n listener(this.state);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private notify(): void {\n const currentState = { ...this.state };\n this.onStateChange?.(currentState);\n this.listeners.forEach((l) => l(currentState));\n }\n\n // --- History (Undo / Redo / Reset) ---\n\n private recordHistory(): void {\n if (this.isApplyingHistory) return;\n this.undoStack.push(this.serializeState());\n if (this.undoStack.length > this.maxHistoryDepth) {\n this.undoStack.shift();\n }\n this.redoStack = [];\n }\n\n public get canUndo(): boolean {\n return this.undoStack.length > 0;\n }\n\n public get canRedo(): boolean {\n return this.redoStack.length > 0;\n }\n\n public undo(): boolean {\n if (!this.canUndo) return false;\n this.isApplyingHistory = true;\n const current = this.serializeState();\n this.redoStack.push(current);\n\n const prev = this.undoStack.pop()!;\n this.applySerializedState(prev);\n this.isApplyingHistory = false;\n this.notify();\n return true;\n }\n\n public redo(): boolean {\n if (!this.canRedo) return false;\n this.isApplyingHistory = true;\n const current = this.serializeState();\n this.undoStack.push(current);\n\n const next = this.redoStack.pop()!;\n this.applySerializedState(next);\n this.isApplyingHistory = false;\n this.notify();\n return true;\n }\n\n public reset(): void {\n this.recordHistory();\n this.applySerializedState(this.initialSnapshot);\n this.state.zoom = 1;\n this.state.pan = { x: 0, y: 0 };\n this.state.activeTool = 'select';\n this.state.selectedAnnotationId = null;\n this.notify();\n }\n\n // --- Active Tool ---\n\n public setTool(tool: ImageEditorTool): void {\n if (this.state.activeTool === tool) return;\n this.state.activeTool = tool;\n if (tool !== 'select') {\n this.state.selectedAnnotationId = null;\n }\n // If switching to crop tool and no crop is defined, initialize crop to full image\n if (tool === 'crop' && !this.state.crop) {\n this.state.crop = {\n x: 0,\n y: 0,\n width: this.state.imageDimensions.width,\n height: this.state.imageDimensions.height,\n };\n }\n this.notify();\n }\n\n // --- Crop ---\n\n public setCrop(crop: CropBox | null, addToHistory = true): void {\n if (addToHistory) {\n this.recordHistory();\n }\n this.state.crop = crop;\n this.notify();\n }\n\n public applyCropPreset(ratio: AspectRatio): void {\n this.recordHistory();\n const { width, height } = this.state.imageDimensions;\n if (!ratio) {\n // Freeform: full image\n this.state.crop = { x: 0, y: 0, width, height };\n } else {\n let targetW = width;\n let targetH = targetW / ratio;\n\n if (targetH > height) {\n targetH = height;\n targetW = targetH * ratio;\n }\n\n const x = Math.max(0, Math.round((width - targetW) / 2));\n const y = Math.max(0, Math.round((height - targetH) / 2));\n this.state.crop = {\n x,\n y,\n width: Math.round(targetW),\n height: Math.round(targetH),\n };\n }\n this.notify();\n }\n\n public resetCrop(): void {\n this.recordHistory();\n this.state.crop = null;\n this.notify();\n }\n\n // --- Transformations (Rotate / Flip) ---\n\n public rotate(degrees: number): void {\n this.recordHistory();\n const current = this.state.transform.rotate;\n this.state.transform.rotate = (current + degrees) % 360;\n this.notify();\n }\n\n public setRotation(degrees: number): void {\n this.recordHistory();\n this.state.transform.rotate = degrees % 360;\n this.notify();\n }\n\n public flipHorizontal(): void {\n this.recordHistory();\n this.state.transform.flipH = !this.state.transform.flipH;\n this.notify();\n }\n\n public flipVertical(): void {\n this.recordHistory();\n this.state.transform.flipV = !this.state.transform.flipV;\n this.notify();\n }\n\n // --- Adjustments ---\n\n public setAdjustments(partial: Partial<ImageAdjustments>, commitToHistory = true): void {\n if (commitToHistory) {\n this.recordHistory();\n }\n this.state.adjustments = {\n ...this.state.adjustments,\n ...partial,\n };\n this.notify();\n }\n\n public resetAdjustments(): void {\n this.recordHistory();\n this.state.adjustments = { ...DEFAULT_ADJUSTMENTS };\n this.notify();\n }\n\n // --- Depth Masking (Gleamforge) ---\n\n public setDepthMask(partial: Partial<DepthMaskOptions>): void {\n this.recordHistory();\n this.state.depthMask = {\n ...this.state.depthMask,\n ...partial,\n };\n this.notify();\n }\n\n public resetDepthMask(): void {\n this.recordHistory();\n this.state.depthMask = { ...DEFAULT_DEPTH_MASK };\n this.notify();\n }\n\n // --- Annotations & Layers ---\n\n public addAnnotation(annotation: CreateAnnotationPayload): string {\n this.recordHistory();\n const id = `ann_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\n const fullAnnotation = { ...annotation, id } as Annotation;\n this.state.annotations = [...this.state.annotations, fullAnnotation];\n this.state.selectedAnnotationId = id;\n this.notify();\n return id;\n }\n\n public updateAnnotation(id: string, partial: Partial<Annotation>, commitToHistory = true): void {\n if (commitToHistory) {\n this.recordHistory();\n }\n this.state.annotations = this.state.annotations.map((ann) => {\n if (ann.id === id) {\n return { ...ann, ...partial } as Annotation;\n }\n return ann;\n });\n this.notify();\n }\n\n public removeAnnotation(id: string): void {\n this.recordHistory();\n this.state.annotations = this.state.annotations.filter((ann) => ann.id !== id);\n if (this.state.selectedAnnotationId === id) {\n this.state.selectedAnnotationId = null;\n }\n this.notify();\n }\n\n public clearAnnotations(): void {\n this.recordHistory();\n this.state.annotations = [];\n this.state.selectedAnnotationId = null;\n this.notify();\n }\n\n public selectAnnotation(id: string | null): void {\n this.state.selectedAnnotationId = id;\n this.notify();\n }\n\n // --- Zoom & Pan ---\n\n public setZoom(zoom: number): void {\n this.state.zoom = Math.max(0.1, Math.min(10, zoom));\n this.notify();\n }\n\n public zoomIn(factor = 1.2): void {\n this.setZoom(this.state.zoom * factor);\n }\n\n public zoomOut(factor = 1.2): void {\n this.setZoom(this.state.zoom / factor);\n }\n\n public resetZoom(): void {\n this.state.zoom = 1;\n this.state.pan = { x: 0, y: 0 };\n this.notify();\n }\n\n public setPan(pan: ImagePoint): void {\n this.state.pan = pan;\n this.notify();\n }\n\n public setComparing(comparing: boolean): void {\n if (this.state.isOriginalCompared === comparing) return;\n this.state.isOriginalCompared = comparing;\n this.notify();\n }\n\n // --- Rendering & Export ---\n\n public async render(targetCanvas: HTMLCanvasElement, options?: RenderOptions): Promise<void> {\n const source = this.imageElement || this.state.sourceUrl;\n if (!source) return;\n try {\n await this.renderer.renderToCanvas(targetCanvas, source, this.state, options);\n } catch {\n // In non-DOM or test mock environments without image loading, safely ignore\n }\n }\n\n public async renderExport(options?: ExportOptions): Promise<HTMLCanvasElement> {\n const source = this.imageElement || this.state.sourceUrl;\n if (!source) {\n return this.renderer.createCanvas(1, 1);\n }\n return this.renderer.renderExport(source, this.state, options);\n }\n\n public async toDataURL(options: ExportOptions = {}): Promise<string> {\n const canvas = await this.renderExport(options);\n const format = options.format || 'image/png';\n const quality = options.quality ?? 0.92;\n return canvas.toDataURL(format, quality);\n }\n\n public async toBlob(options: ExportOptions = {}): Promise<Blob> {\n const canvas = await this.renderExport(options);\n const format = options.format || 'image/png';\n const quality = options.quality ?? 0.92;\n\n return new Promise((resolve, reject) => {\n if (canvas.toBlob) {\n canvas.toBlob((blob) => {\n if (blob) resolve(blob);\n else reject(new Error('Failed to create Blob from canvas'));\n }, format, quality);\n } else {\n // Fallback for environments lacking canvas.toBlob\n try {\n const dataUrl = canvas.toDataURL(format, quality);\n const parts = dataUrl.split(',');\n const byteStr = atob(parts[1]);\n const ab = new ArrayBuffer(byteStr.length);\n const ia = new Uint8Array(ab);\n for (let i = 0; i < byteStr.length; i++) {\n ia[i] = byteStr.charCodeAt(i);\n }\n resolve(new Blob([ab], { type: format }));\n } catch (err) {\n reject(err);\n }\n }\n });\n }\n\n // --- AI Masking & Transformation Helpers ---\n\n public async renderMask(options?: { useAnnotations?: boolean; useCrop?: boolean }): Promise<HTMLCanvasElement> {\n return this.renderer.renderMask(this.state, options);\n }\n\n public async toMaskDataURL(options?: { useAnnotations?: boolean; useCrop?: boolean }): Promise<string> {\n const canvas = await this.renderMask(options);\n return canvas.toDataURL('image/png');\n }\n\n public async applyInpaintedImage(newDataUrl: string): Promise<void> {\n this.recordHistory();\n await this.loadImage(newDataUrl);\n this.clearAnnotations();\n }\n\n public async applyBackgroundRemovedImage(newDataUrl: string): Promise<void> {\n this.recordHistory();\n await this.loadImage(newDataUrl);\n }\n\n\n // --- Serialization ---\n\n public toJSON(): SerializedImageEditorState {\n return this.serializeState();\n }\n\n public loadJSON(data: SerializedImageEditorState): void {\n this.recordHistory();\n this.applySerializedState(data);\n this.notify();\n }\n\n private serializeState(): SerializedImageEditorState {\n return {\n version: 1,\n crop: this.state.crop ? { ...this.state.crop } : null,\n transform: { ...this.state.transform },\n adjustments: { ...this.state.adjustments },\n depthMask: {\n ...this.state.depthMask,\n depthRange: [...this.state.depthMask.depthRange] as [number, number],\n },\n annotations: JSON.parse(JSON.stringify(this.state.annotations)),\n };\n }\n\n private applySerializedState(data: SerializedImageEditorState): void {\n this.state.crop = data.crop ? { ...data.crop } : null;\n this.state.transform = { ...data.transform };\n this.state.adjustments = { ...data.adjustments };\n this.state.depthMask = {\n ...data.depthMask,\n depthRange: [...data.depthMask.depthRange] as [number, number],\n };\n this.state.annotations = JSON.parse(JSON.stringify(data.annotations || []));\n }\n}\n","import type { CropBox, ImageEditorState, ImagePoint } from './types';\n\nexport type CropHandle = 'nw' | 'ne' | 'se' | 'sw' | 'n' | 's' | 'e' | 'w' | 'inside' | null;\n\nexport interface ViewportMetrics {\n vpWidth: number;\n vpHeight: number;\n centerX: number;\n centerY: number;\n baseW: number;\n baseH: number;\n fitScale: number;\n renderW: number;\n renderH: number;\n totalScale: number;\n imageRect: { x: number; y: number; width: number; height: number };\n cropRect: { x: number; y: number; width: number; height: number } | null;\n}\n\n/**\n * Calculates current viewport projection metrics for canvas <-> image coordinate mapping.\n */\nexport function getViewportMetrics(\n state: ImageEditorState,\n vpWidth: number,\n vpHeight: number,\n isActivelyCropping = false,\n cropOverride?: CropBox | null\n): ViewportMetrics {\n const { imageDimensions, crop: stateCrop, transform, zoom, pan } = state;\n const naturalWidth = imageDimensions.width || 800;\n const naturalHeight = imageDimensions.height || 600;\n const activeCrop =\n (cropOverride !== undefined && cropOverride !== null)\n ? cropOverride\n : (stateCrop ?? (isActivelyCropping ? { x: 0, y: 0, width: naturalWidth, height: naturalHeight } : null));\n\n const cropW = (!isActivelyCropping && activeCrop) ? activeCrop.width : naturalWidth;\n const cropH = (!isActivelyCropping && activeCrop) ? activeCrop.height : naturalHeight;\n\n const angle = ((transform.rotate % 360) + 360) % 360;\n const isSwapped = angle === 90 || angle === 270;\n const baseW = isSwapped ? cropH : cropW;\n const baseH = isSwapped ? cropW : cropH;\n\n const fitScale = Math.min((vpWidth * 0.85) / baseW, (vpHeight * 0.85) / baseH, 1);\n const renderW = baseW * fitScale;\n const renderH = baseH * fitScale;\n const totalScale = zoom * fitScale;\n\n const centerX = vpWidth / 2 + pan.x;\n const centerY = vpHeight / 2 + pan.y;\n\n const imageRect = {\n x: centerX - (renderW * zoom) / 2,\n y: centerY - (renderH * zoom) / 2,\n width: renderW * zoom,\n height: renderH * zoom,\n };\n\n let cropRect: { x: number; y: number; width: number; height: number } | null = null;\n if (activeCrop) {\n const startX = -renderW / 2 + (activeCrop.x / naturalWidth) * renderW;\n const startY = -renderH / 2 + (activeCrop.y / naturalHeight) * renderH;\n const cropBoxW = (activeCrop.width / naturalWidth) * renderW;\n const cropBoxH = (activeCrop.height / naturalHeight) * renderH;\n\n cropRect = {\n x: centerX + zoom * startX,\n y: centerY + zoom * startY,\n width: zoom * cropBoxW,\n height: zoom * cropBoxH,\n };\n }\n\n return {\n vpWidth,\n vpHeight,\n centerX,\n centerY,\n baseW,\n baseH,\n fitScale,\n renderW,\n renderH,\n totalScale,\n imageRect,\n cropRect,\n };\n}\n\n/**\n * Converts a point on the canvas (in internal canvas pixel space) to image/annotation pixel space.\n */\nexport function canvasToImagePoint(\n canvasPt: ImagePoint,\n state: ImageEditorState,\n vpWidth: number,\n vpHeight: number,\n isActivelyCropping = false\n): ImagePoint {\n const metrics = getViewportMetrics(state, vpWidth, vpHeight, isActivelyCropping);\n const unzoomedX = (canvasPt.x - metrics.centerX) / state.zoom;\n const unzoomedY = (canvasPt.y - metrics.centerY) / state.zoom;\n\n const x = (unzoomedX + metrics.renderW / 2) / metrics.fitScale;\n const y = (unzoomedY + metrics.renderH / 2) / metrics.fitScale;\n\n return { x: Math.round(x), y: Math.round(y) };\n}\n\n/**\n * Converts a point in image/annotation pixel space to internal canvas pixel space.\n */\nexport function imageToCanvasPoint(\n imgPt: ImagePoint,\n state: ImageEditorState,\n vpWidth: number,\n vpHeight: number,\n isActivelyCropping = false\n): ImagePoint {\n const metrics = getViewportMetrics(state, vpWidth, vpHeight, isActivelyCropping);\n const unzoomedX = (imgPt.x - metrics.baseW / 2) * metrics.fitScale;\n const unzoomedY = (imgPt.y - metrics.baseH / 2) * metrics.fitScale;\n\n const canvasX = metrics.centerX + unzoomedX * state.zoom;\n const canvasY = metrics.centerY + unzoomedY * state.zoom;\n\n return { x: Math.round(canvasX), y: Math.round(canvasY) };\n}\n\n/**\n * Hit-tests the crop box and its corner/edge handles given a canvas point.\n */\nexport function hitTestCrop(\n canvasPt: ImagePoint,\n state: ImageEditorState,\n vpWidth: number,\n vpHeight: number,\n handleThreshold = 14,\n cropOverride?: CropBox | null\n): CropHandle {\n const metrics = getViewportMetrics(state, vpWidth, vpHeight, true, cropOverride);\n if (!metrics.cropRect) return null;\n\n const { x, y, width: w, height: h } = metrics.cropRect;\n const px = canvasPt.x;\n const py = canvasPt.y;\n\n // 1. Corners (highest priority)\n const dist = (x1: number, y1: number, x2: number, y2: number) =>\n Math.hypot(x1 - x2, y1 - y2);\n\n if (dist(px, py, x, y) <= handleThreshold) return 'nw';\n if (dist(px, py, x + w, y) <= handleThreshold) return 'ne';\n if (dist(px, py, x + w, y + h) <= handleThreshold) return 'se';\n if (dist(px, py, x, y + h) <= handleThreshold) return 'sw';\n\n // 2. Edges\n const nearX = px >= x - handleThreshold && px <= x + w + handleThreshold;\n const nearY = py >= y - handleThreshold && py <= y + h + handleThreshold;\n\n if (nearX && Math.abs(py - y) <= handleThreshold) return 'n';\n if (nearX && Math.abs(py - (y + h)) <= handleThreshold) return 's';\n if (nearY && Math.abs(px - x) <= handleThreshold) return 'w';\n if (nearY && Math.abs(px - (x + w)) <= handleThreshold) return 'e';\n\n // 3. Inside crop region\n if (px >= x && px <= x + w && py >= y && py <= y + h) {\n return 'inside';\n }\n\n return null;\n}\n\n/**\n * Returns CSS cursor style corresponding to a crop handle.\n */\nexport function getCropCursor(handle: CropHandle): string {\n switch (handle) {\n case 'nw':\n case 'se':\n return 'nwse-resize';\n case 'ne':\n case 'sw':\n return 'nesw-resize';\n case 'n':\n case 's':\n return 'ns-resize';\n case 'e':\n case 'w':\n return 'ew-resize';\n case 'inside':\n return 'move';\n default:\n return 'crosshair';\n }\n}\n\n/**\n * Calculates updated crop rectangle given a drag interaction.\n */\nexport function calculateCropDrag(\n startCrop: CropBox,\n handle: CropHandle,\n startImgPt: ImagePoint,\n currentImgPt: ImagePoint,\n imgWidth: number,\n imgHeight: number,\n aspectRatio?: number | null\n): CropBox {\n const dx = currentImgPt.x - startImgPt.x;\n const dy = currentImgPt.y - startImgPt.y;\n\n // Whole crop box move\n if (handle === 'inside') {\n const maxX = Math.max(0, imgWidth - startCrop.width);\n const maxY = Math.max(0, imgHeight - startCrop.height);\n const newX = Math.max(0, Math.min(maxX, startCrop.x + dx));\n const newY = Math.max(0, Math.min(maxY, startCrop.y + dy));\n return {\n x: Math.round(newX),\n y: Math.round(newY),\n width: startCrop.width,\n height: startCrop.height,\n };\n }\n\n // Handle resizing\n let left = startCrop.x;\n let top = startCrop.y;\n let right = startCrop.x + startCrop.width;\n let bottom = startCrop.y + startCrop.height;\n\n const minSize = 20;\n\n if (handle === 'e' || handle === 'ne' || handle === 'se') {\n right = Math.min(imgWidth, Math.max(left + minSize, startCrop.x + startCrop.width + dx));\n }\n if (handle === 'w' || handle === 'nw' || handle === 'sw') {\n left = Math.max(0, Math.min(right - minSize, startCrop.x + dx));\n }\n if (handle === 's' || handle === 'se' || handle === 'sw') {\n bottom = Math.min(imgHeight, Math.max(top + minSize, startCrop.y + startCrop.height + dy));\n }\n if (handle === 'n' || handle === 'nw' || handle === 'ne') {\n top = Math.max(0, Math.min(bottom - minSize, startCrop.y + dy));\n }\n\n // Maintain aspect ratio if requested\n if (aspectRatio && aspectRatio > 0) {\n let currentW = right - left;\n let currentH = bottom - top;\n\n if (handle === 'e' || handle === 'w') {\n currentH = Math.min(imgHeight, currentW / aspectRatio);\n bottom = Math.min(imgHeight, top + currentH);\n } else if (handle === 'n' || handle === 's') {\n currentW = Math.min(imgWidth, currentH * aspectRatio);\n right = Math.min(imgWidth, left + currentW);\n } else {\n // Corner resizing\n const targetH = currentW / aspectRatio;\n if (top + targetH <= imgHeight) {\n bottom = top + targetH;\n } else {\n const targetW = currentH * aspectRatio;\n right = Math.min(imgWidth, left + targetW);\n }\n }\n }\n\n return {\n x: Math.round(left),\n y: Math.round(top),\n width: Math.round(Math.max(minSize, right - left)),\n height: Math.round(Math.max(minSize, bottom - top)),\n };\n}\n\n/**\n * Calculates bounding box of an annotation.\n */\nexport function getAnnotationBounds(ann: import('./types').Annotation): { x: number; y: number; width: number; height: number } {\n switch (ann.type) {\n case 'rect':\n return { x: ann.x, y: ann.y, width: ann.width, height: ann.height };\n case 'circle':\n return {\n x: ann.x - ann.radiusX,\n y: ann.y - ann.radiusY,\n width: ann.radiusX * 2,\n height: ann.radiusY * 2,\n };\n case 'arrow':\n case 'line': {\n const minX = Math.min(ann.x, ann.endX);\n const minY = Math.min(ann.y, ann.endY);\n return {\n x: minX,\n y: minY,\n width: Math.max(Math.abs(ann.endX - ann.x), 10),\n height: Math.max(Math.abs(ann.endY - ann.y), 10),\n };\n }\n case 'pen': {\n if (!ann.points.length) return { x: ann.x, y: ann.y, width: 0, height: 0 };\n let minX = ann.points[0].x;\n let maxX = ann.points[0].x;\n let minY = ann.points[0].y;\n let maxY = ann.points[0].y;\n for (const pt of ann.points) {\n if (pt.x < minX) minX = pt.x;\n if (pt.x > maxX) maxX = pt.x;\n if (pt.y < minY) minY = pt.y;\n if (pt.y > maxY) maxY = pt.y;\n }\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n }\n case 'text':\n return {\n x: ann.x,\n y: ann.y,\n width: ann.text.length * ann.fontSize * 0.6,\n height: ann.fontSize * 1.2,\n };\n case 'image':\n return { x: ann.x, y: ann.y, width: ann.width, height: ann.height };\n }\n}\n\n/**\n * Hit tests if a point in image space is within an annotation's bounds.\n */\nexport function hitTestAnnotation(imgPt: ImagePoint, ann: import('./types').Annotation, padding = 10): boolean {\n const b = getAnnotationBounds(ann);\n return (\n imgPt.x >= b.x - padding &&\n imgPt.x <= b.x + b.width + padding &&\n imgPt.y >= b.y - padding &&\n imgPt.y <= b.y + b.height + padding\n );\n}\n\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/image-editor/index.cjs","../../src/image-editor/filters.ts","../../src/image-editor/renderer.ts","../../src/image-editor/controller.ts","../../src/image-editor/geometry.ts"],"names":[],"mappings":"AAAA;ACEA,SAAS,KAAA,CAAM,KAAA,EAAe,IAAA,EAAM,CAAA,EAAG,IAAA,EAAM,GAAA,EAAa;AACxD,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,KAAK,CAAC,CAAA;AAC3C;AAMO,SAAS,gBAAA,CAAiB,SAAA,EAAsB,WAAA,EAAqC;AAC1F,EAAA,MAAM,EAAE,UAAA,EAAY,QAAA,EAAU,UAAA,EAAY,QAAA,EAAU,WAAA,EAAa,OAAA,EAAS,KAAK,EAAA,EAAI,WAAA;AACnF,EAAA,MAAM,KAAA,EAAO,SAAA,CAAU,IAAA;AACvB,EAAA,MAAM,IAAA,EAAM,IAAA,CAAK,MAAA;AAEjB,EAAA,MAAM,cAAA,EAAgB,WAAA,IAAe,CAAA;AACrC,EAAA,MAAM,YAAA,EAAc,SAAA,IAAa,CAAA;AACjC,EAAA,MAAM,cAAA,EAAgB,WAAA,IAAe,CAAA;AACrC,EAAA,MAAM,YAAA,EAAc,SAAA,IAAa,CAAA;AACjC,EAAA,MAAM,eAAA,EAAiB,YAAA,IAAgB,CAAA;AACvC,EAAA,MAAM,WAAA,EAAa,QAAA,EAAU,CAAA;AAG7B,EAAA,MAAM,eAAA,EAAiB,YAAA,EAClB,IAAA,EAAA,CAAO,SAAA,EAAW,GAAA,EAAA,EAAA,CAAS,IAAA,EAAA,CAAO,IAAA,EAAM,QAAA,CAAA,EAAA,EACzC,CAAA;AAGJ,EAAA,MAAM,mBAAA,EAAqB,YAAA,EAAc,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,SAAA,EAAW,EAAE,EAAA,EAAI,CAAA;AAGtE,EAAA,MAAM,cAAA,EAAgB,cAAA,EAAgB,EAAA,EAAI,WAAA,EAAa,IAAA,EAAM,CAAA;AAG7D,EAAA,MAAM,WAAA,EAAa,eAAA,EAAiB,YAAA,EAAc,IAAA,EAAM,CAAA;AAExD,EAAA,GAAA,CAAI,cAAA,GAAiB,YAAA,GAAe,cAAA,GAAiB,YAAA,GAAe,eAAA,GAAkB,UAAA,EAAY;AAChG,IAAA,IAAA,CAAA,IAAS,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,GAAA,EAAK,EAAA,GAAK,CAAA,EAAG;AAC/B,MAAA,IAAI,EAAA,EAAI,IAAA,CAAK,CAAC,CAAA;AACd,MAAA,IAAI,EAAA,EAAI,IAAA,CAAK,EAAA,EAAI,CAAC,CAAA;AAClB,MAAA,IAAI,EAAA,EAAI,IAAA,CAAK,EAAA,EAAI,CAAC,CAAA;AAClB,MAAA,IAAI,EAAA,EAAI,IAAA,CAAK,EAAA,EAAI,CAAC,CAAA;AAGlB,MAAA,GAAA,CAAI,WAAA,EAAa;AACf,QAAA,EAAA,EAAI,KAAA,CAAM,EAAA,EAAI,kBAAkB,CAAA;AAChC,QAAA,EAAA,EAAI,KAAA,CAAM,EAAA,EAAI,kBAAkB,CAAA;AAChC,QAAA,EAAA,EAAI,KAAA,CAAM,EAAA,EAAI,kBAAkB,CAAA;AAAA,MAClC;AAGA,MAAA,GAAA,CAAI,aAAA,EAAe;AACjB,QAAA,MAAM,QAAA,EAAU,WAAA,EAAa,IAAA;AAC7B,QAAA,EAAA,EAAI,KAAA,CAAM,EAAA,EAAI,OAAO,CAAA;AACrB,QAAA,EAAA,EAAI,KAAA,CAAM,EAAA,EAAI,OAAO,CAAA;AACrB,QAAA,EAAA,EAAI,KAAA,CAAM,EAAA,EAAI,OAAO,CAAA;AAAA,MACvB;AAGA,MAAA,GAAA,CAAI,WAAA,EAAa;AACf,QAAA,EAAA,EAAI,KAAA,CAAM,eAAA,EAAA,CAAkB,EAAA,EAAI,GAAA,EAAA,EAAO,GAAG,CAAA;AAC1C,QAAA,EAAA,EAAI,KAAA,CAAM,eAAA,EAAA,CAAkB,EAAA,EAAI,GAAA,EAAA,EAAO,GAAG,CAAA;AAC1C,QAAA,EAAA,EAAI,KAAA,CAAM,eAAA,EAAA,CAAkB,EAAA,EAAI,GAAA,EAAA,EAAO,GAAG,CAAA;AAAA,MAC5C;AAGA,MAAA,GAAA,CAAI,aAAA,EAAe;AACjB,QAAA,MAAM,UAAA,EAAY,OAAA,EAAS,EAAA,EAAI,MAAA,EAAQ,EAAA,EAAI,MAAA,EAAQ,CAAA;AACnD,QAAA,EAAA,EAAI,KAAA,CAAM,UAAA,EAAA,CAAa,EAAA,EAAI,SAAA,EAAA,EAAa,aAAa,CAAA;AACrD,QAAA,EAAA,EAAI,KAAA,CAAM,UAAA,EAAA,CAAa,EAAA,EAAI,SAAA,EAAA,EAAa,aAAa,CAAA;AACrD,QAAA,EAAA,EAAI,KAAA,CAAM,UAAA,EAAA,CAAa,EAAA,EAAI,SAAA,EAAA,EAAa,aAAa,CAAA;AAAA,MACvD;AAGA,MAAA,GAAA,CAAI,cAAA,EAAgB;AAClB,QAAA,EAAA,EAAI,KAAA,CAAM,EAAA,EAAI,UAAU,CAAA;AACxB,QAAA,EAAA,EAAI,KAAA,CAAM,EAAA,EAAI,UAAU,CAAA;AAAA,MAC1B;AAGA,MAAA,GAAA,CAAI,UAAA,EAAY;AACd,QAAA,EAAA,EAAI,KAAA,CAAM,EAAA,EAAI,OAAO,CAAA;AAAA,MACvB;AAEA,MAAA,IAAA,CAAK,CAAC,EAAA,EAAI,CAAA;AACV,MAAA,IAAA,CAAK,EAAA,EAAI,CAAC,EAAA,EAAI,CAAA;AACd,MAAA,IAAA,CAAK,EAAA,EAAI,CAAC,EAAA,EAAI,CAAA;AACd,MAAA,IAAA,CAAK,EAAA,EAAI,CAAC,EAAA,EAAI,CAAA;AAAA,IAChB;AAAA,EACF;AAGA,EAAA,GAAA,CAAI,KAAA,EAAO,CAAA,EAAG;AACZ,IAAA,gBAAA,CAAiB,SAAA,EAAW,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,EAAG,EAAE,CAAC,CAAA;AAAA,EAC5D;AACF;AAKA,SAAS,gBAAA,CAAiB,SAAA,EAAsB,MAAA,EAAsB;AACpE,EAAA,GAAA,CAAI,OAAA,EAAS,CAAA,EAAG,MAAA;AAChB,EAAA,MAAM,EAAA,EAAI,SAAA,CAAU,KAAA;AACpB,EAAA,MAAM,EAAA,EAAI,SAAA,CAAU,MAAA;AACpB,EAAA,MAAM,IAAA,EAAM,SAAA,CAAU,IAAA;AACtB,EAAA,MAAM,KAAA,EAAO,IAAI,iBAAA,CAAkB,GAAA,CAAI,MAAM,CAAA;AAG7C,EAAA,IAAA,CAAA,IAAS,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AAC1B,IAAA,IAAA,CAAA,IAAS,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AAC1B,MAAA,IAAI,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA;AAC7B,MAAA,IAAI,MAAA,EAAQ,CAAA;AACZ,MAAA,IAAA,CAAA,IAAS,EAAA,EAAI,CAAC,MAAA,EAAQ,EAAA,GAAK,MAAA,EAAQ,CAAA,EAAA,EAAK;AACtC,QAAA,MAAM,GAAA,EAAK,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,EAAA,EAAI,CAAC,CAAC,CAAA;AAC7C,QAAA,MAAM,IAAA,EAAA,CAAO,EAAA,EAAI,EAAA,EAAI,EAAA,EAAA,EAAM,CAAA;AAC3B,QAAA,EAAA,GAAK,GAAA,CAAI,GAAG,CAAA;AACZ,QAAA,EAAA,GAAK,GAAA,CAAI,IAAA,EAAM,CAAC,CAAA;AAChB,QAAA,EAAA,GAAK,GAAA,CAAI,IAAA,EAAM,CAAC,CAAA;AAChB,QAAA,EAAA,GAAK,GAAA,CAAI,IAAA,EAAM,CAAC,CAAA;AAChB,QAAA,KAAA,EAAA;AAAA,MACF;AACA,MAAA,MAAM,OAAA,EAAA,CAAU,EAAA,EAAI,EAAA,EAAI,CAAA,EAAA,EAAK,CAAA;AAC7B,MAAA,IAAA,CAAK,MAAM,EAAA,EAAI,IAAA,CAAK,KAAA,CAAM,EAAA,EAAI,KAAK,CAAA;AACnC,MAAA,IAAA,CAAK,OAAA,EAAS,CAAC,EAAA,EAAI,IAAA,CAAK,KAAA,CAAM,EAAA,EAAI,KAAK,CAAA;AACvC,MAAA,IAAA,CAAK,OAAA,EAAS,CAAC,EAAA,EAAI,IAAA,CAAK,KAAA,CAAM,EAAA,EAAI,KAAK,CAAA;AACvC,MAAA,IAAA,CAAK,OAAA,EAAS,CAAC,EAAA,EAAI,IAAA,CAAK,KAAA,CAAM,EAAA,EAAI,KAAK,CAAA;AAAA,IACzC;AAAA,EACF;AAGA,EAAA,IAAA,CAAA,IAAS,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AAC1B,IAAA,IAAA,CAAA,IAAS,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AAC1B,MAAA,IAAI,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,CAAA;AAC7B,MAAA,IAAI,MAAA,EAAQ,CAAA;AACZ,MAAA,IAAA,CAAA,IAAS,EAAA,EAAI,CAAC,MAAA,EAAQ,EAAA,GAAK,MAAA,EAAQ,CAAA,EAAA,EAAK;AACtC,QAAA,MAAM,GAAA,EAAK,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,EAAA,EAAI,CAAC,CAAC,CAAA;AAC7C,QAAA,MAAM,IAAA,EAAA,CAAO,GAAA,EAAK,EAAA,EAAI,CAAA,EAAA,EAAK,CAAA;AAC3B,QAAA,EAAA,GAAK,IAAA,CAAK,GAAG,CAAA;AACb,QAAA,EAAA,GAAK,IAAA,CAAK,IAAA,EAAM,CAAC,CAAA;AACjB,QAAA,EAAA,GAAK,IAAA,CAAK,IAAA,EAAM,CAAC,CAAA;AACjB,QAAA,EAAA,GAAK,IAAA,CAAK,IAAA,EAAM,CAAC,CAAA;AACjB,QAAA,KAAA,EAAA;AAAA,MACF;AACA,MAAA,MAAM,OAAA,EAAA,CAAU,EAAA,EAAI,EAAA,EAAI,CAAA,EAAA,EAAK,CAAA;AAC7B,MAAA,GAAA,CAAI,MAAM,EAAA,EAAI,IAAA,CAAK,KAAA,CAAM,EAAA,EAAI,KAAK,CAAA;AAClC,MAAA,GAAA,CAAI,OAAA,EAAS,CAAC,EAAA,EAAI,IAAA,CAAK,KAAA,CAAM,EAAA,EAAI,KAAK,CAAA;AACtC,MAAA,GAAA,CAAI,OAAA,EAAS,CAAC,EAAA,EAAI,IAAA,CAAK,KAAA,CAAM,EAAA,EAAI,KAAK,CAAA;AACtC,MAAA,GAAA,CAAI,OAAA,EAAS,CAAC,EAAA,EAAI,IAAA,CAAK,KAAA,CAAM,EAAA,EAAI,KAAK,CAAA;AAAA,IACxC;AAAA,EACF;AACF;AAMO,SAAS,cAAA,CACd,aAAA,EACA,aAAA,EACA,OAAA,EACM;AACN,EAAA,MAAM,EAAE,IAAA,EAAM,UAAA,EAAY,QAAA,EAAU,OAAO,EAAA,EAAI,OAAA;AAC/C,EAAA,MAAM,SAAA,EAAW,aAAA,CAAc,IAAA;AAC/B,EAAA,MAAM,SAAA,EAAW,aAAA,CAAc,IAAA;AAC/B,EAAA,MAAM,IAAA,EAAM,IAAA,CAAK,GAAA,CAAI,QAAA,CAAS,MAAA,EAAQ,QAAA,CAAS,MAAM,CAAA;AACrD,EAAA,MAAM,kBAAA,EAAoB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,SAAA,GAAY,EAAE,CAAA;AACpD,EAAA,MAAM,SAAA,EAAW,UAAA,CAAW,CAAC,CAAA;AAC7B,EAAA,MAAM,SAAA,EAAW,UAAA,CAAW,CAAC,CAAA;AAE7B,EAAA,IAAA,CAAA,IAAS,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,GAAA,EAAK,EAAA,GAAK,CAAA,EAAG;AAC/B,IAAA,IAAI,UAAA,EAAY,QAAA,CAAS,CAAC,CAAA;AAC1B,IAAA,GAAA,CAAI,MAAA,EAAQ;AACV,MAAA,UAAA,EAAY,IAAA,EAAM,SAAA;AAAA,IACpB;AAEA,IAAA,IAAI,UAAA;AAEJ,IAAA,GAAA,CAAI,KAAA,IAAS,WAAA,EAAa;AACxB,MAAA,GAAA,CAAI,UAAA,GAAa,SAAA,EAAW,iBAAA,EAAmB;AAC7C,QAAA,WAAA,EAAa,CAAA;AAAA,MACf,EAAA,KAAA,GAAA,CAAW,UAAA,EAAY,QAAA,EAAU;AAC/B,QAAA,WAAA,EAAa,IAAA,CAAK,KAAA,CAAA,CAAQ,SAAA,EAAW,kBAAA,EAAoB,SAAA,EAAA,EAAa,kBAAA,EAAqB,GAAG,CAAA;AAAA,MAChG,EAAA,KAAA,GAAA,CAAW,UAAA,GAAa,QAAA,EAAU;AAChC,QAAA,WAAA,EAAa,GAAA;AAAA,MACf,EAAA,KAAA,GAAA,CAAW,UAAA,EAAY,SAAA,EAAW,iBAAA,EAAmB;AACnD,QAAA,WAAA,EAAa,CAAA;AAAA,MACf,EAAA,KAAO;AACL,QAAA,WAAA,EAAa,IAAA,CAAK,KAAA,CAAA,CAAQ,UAAA,EAAA,CAAa,SAAA,EAAW,iBAAA,CAAA,EAAA,EAAsB,kBAAA,EAAqB,GAAG,CAAA;AAAA,MAClG;AAAA,IACF,EAAA,KAAO;AAEL,MAAA,WAAA,EAAa,KAAA,CAAM,UAAA,EAAY,QAAQ,CAAA;AAAA,IACzC;AAGA,IAAA,QAAA,CAAS,EAAA,EAAI,CAAC,EAAA,EAAI,KAAA,CAAO,QAAA,CAAS,EAAA,EAAI,CAAC,EAAA,EAAI,WAAA,EAAc,GAAG,CAAA;AAAA,EAC9D;AACF;AAMO,SAAS,WAAA,CAAY,SAAA,EAA4B;AACtD,EAAA,MAAM,KAAA,EAAO,SAAA,CAAU,IAAA;AACvB,EAAA,MAAM,IAAA,EAAM,IAAA,CAAK,MAAA;AACjB,EAAA,IAAA,CAAA,IAAS,EAAA,EAAI,CAAA,EAAG,EAAA,EAAI,GAAA,EAAK,EAAA,GAAK,CAAA,EAAG;AAC/B,IAAA,IAAA,CAAK,CAAC,EAAA,EAAI,GAAA;AAAA,EACZ;AACF;AD5DA;AACA;AEzHA,IAAM,WAAA,YAAN,MAAiB;AAAA,iBACP,MAAA,kBAAQ,IAAI,GAAA,CAA8B,EAAA;AAAA,EAElD,MAAa,GAAA,CAAI,GAAA,EAAwC;AACvD,IAAA,MAAM,SAAA,EAAW,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AACnC,IAAA,GAAA,CAAI,SAAA,GAAY,QAAA,CAAS,QAAA,EAAU;AACjC,MAAA,OAAO,QAAA;AAAA,IACT;AAEA,IAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,EAAA,GAAW;AACtC,MAAA,GAAA,CAAI,OAAO,OAAA,IAAW,YAAA,GAAe,OAAO,MAAA,IAAU,WAAA,EAAa;AACjE,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,6DAA6D,CAAC,CAAA;AAC/E,QAAA,MAAA;AAAA,MACF;AACA,MAAA,MAAM,IAAA,EAAM,IAAI,KAAA,CAAM,CAAA;AACtB,MAAA,GAAA,CAAI,YAAA,EAAc,WAAA;AAClB,MAAA,GAAA,CAAI,OAAA,EAAS,CAAA,EAAA,GAAM;AACjB,QAAA,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,GAAG,CAAA;AACvB,QAAA,OAAA,CAAQ,GAAG,CAAA;AAAA,MACb,CAAA;AACA,MAAA,GAAA,CAAI,QAAA,EAAU,CAAC,CAAA,EAAA,GAAM,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,2BAAA,EAA8B,GAAG,CAAA,CAAA;AAC7D,MAAA;AAGsD,MAAA;AAC7C,QAAA;AACX,UAAA;AACuD,YAAA;AACC,YAAA;AACJ,YAAA;AAChD,UAAA;AAER,UAAA;AACuB,UAAA;AACZ,UAAA;AACR,QAAA;AACP,MAAA;AACD,IAAA;AACH,EAAA;AAEqD,EAAA;AAC5B,IAAA;AACzB,EAAA;AAEqB,EAAA;AACF,IAAA;AACnB,EAAA;AACF;AAE+C;AAMd;AACX,kBAAA;AACiC,kBAAA;AAC7B,kBAAA;AAAA;AAAA;AAAA;AAK8C,EAAA;AACL,IAAA;AACpB,MAAA;AACF,MAAA;AACE,MAAA;AAClC,MAAA;AACT,IAAA;AAEO,IAAA;AAC+B,MAAA;AACE,MAAA;AACpB,MAAA;AACD,MAAA;AACH,MAAA;AAAC,MAAA;AACjB,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAUiB,EAAA;AACyB,IAAA;AAC9B,IAAA;AAEN,IAAA;AACiC,IAAA;AACQ,MAAA;AACtC,IAAA;AACC,MAAA;AACR,IAAA;AAEmG,IAAA;AACN,IAAA;AAE/B,IAAA;AAGR,IAAA;AACE,IAAA;AAEnC,IAAA;AACC,IAAA;AAEe,IAAA;AAGb,IAAA;AAC+C,MAAA;AACrE,MAAA;AACF,IAAA;AAIoE,IAAA;AACA,IAAA;AACA,IAAA;AACE,IAAA;AACC,IAAA;AAEtB,IAAA;AACL,IAAA;AACV,IAAA;AACA,IAAA;AAGqC,IAAA;AAEjD,IAAA;AAC6B,IAAA;AACN,MAAA;AACD,MAAA;AAE7B,MAAA;AACE,QAAA;AAGmB,QAAA;AACM,QAAA;AACpB,QAAA;AAC8C,QAAA;AAGxD,QAAA;AACN,UAAA;AACA,UAAA;AAAO,UAAA;AAAO,UAAA;AAAO,UAAA;AACZ,UAAA;AAAY,UAAA;AAAG,UAAA;AAAO,UAAA;AACjC,QAAA;AAEgB,QAAA;AAGyC,QAAA;AAC1C,QAAA;AAC0B,UAAA;AAEQ,UAAA;AACzC,YAAA;AAC4D,cAAA;AACb,cAAA;AACP,cAAA;AAC7B,cAAA;AACkD,gBAAA;AACL,gBAAA;AAC1C,gBAAA;AACiC,kBAAA;AAC/C,gBAAA;AACF,cAAA;AACM,YAAA;AAER,YAAA;AACF,UAAA;AAEoC,UAAA;AACtC,QAAA;AACF,MAAA;AACwB,MAAA;AACH,MAAA;AACvB,IAAA;AAGS,IAAA;AAE8C,IAAA;AACnC,IAAA;AAGoD,IAAA;AAChD,IAAA;AACA,IAAA;AAG8C,IAAA;AAGlD,IAAA;AACX,IAAA;AAC+B,IAAA;AACN,IAAA;AAEH,IAAA;AACK,MAAA;AACmC,MAAA;AAC9B,QAAA;AACvC,MAAA;AACF,IAAA;AAG6B,IAAA;AAC2B,MAAA;AACxD,IAAA;AAEY,IAAA;AAEA,IAAA;AAGsD,IAAA;AAEN,IAAA;AAGjD,MAAA;AACA,MAAA;AACI,MAAA;AACC,MAAA;AACN,IAAA;AAE0B,IAAA;AACgC,MAAA;AACpE,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAK2F,EAAA;AAChF,IAAA;AACsB,IAAA;AACP,MAAA;AACxB,IAAA;AAEkB,IAAA;AACU,MAAA;AACe,MAAA;AACb,MAAA;AAC9B,IAAA;AAEkB,IAAA;AACJ,MAAA;AACE,QAAA;AACe,QAAA;AACX,UAAA;AACQ,UAAA;AACF,UAAA;AACN,UAAA;AACC,UAAA;AAC4B,UAAA;AACC,UAAA;AACC,YAAA;AAC7C,UAAA;AACW,UAAA;AACb,QAAA;AACA,QAAA;AACF,MAAA;AAEa,MAAA;AACE,QAAA;AACU,QAAA;AACF,QAAA;AACD,QAAA;AACG,UAAA;AAC+B,UAAA;AACtD,QAAA;AAC0B,QAAA;AAC8B,UAAA;AACxD,QAAA;AACA,QAAA;AACF,MAAA;AAEe,MAAA;AACE,QAAA;AACD,QAAA;AACW,QAAA;AACF,QAAA;AAC4C,QAAA;AAC7C,QAAA;AACG,UAAA;AACd,UAAA;AACX,QAAA;AAC4B,QAAA;AACf,UAAA;AACb,QAAA;AACA,QAAA;AACF,MAAA;AAEc,MAAA;AACE,QAAA;AACU,QAAA;AACF,QAAA;AACA,QAAA;AAGR,QAAA;AACa,QAAA;AACM,QAAA;AACtB,QAAA;AAGwD,QAAA;AACjB,QAAA;AACpC,QAAA;AACmB,QAAA;AAC7B,QAAA;AACiD,UAAA;AACA,UAAA;AACrD,QAAA;AACI,QAAA;AACiD,UAAA;AACA,UAAA;AACrD,QAAA;AACc,QAAA;AACL,QAAA;AACT,QAAA;AACF,MAAA;AAEa,MAAA;AACE,QAAA;AACC,QAAA;AACS,QAAA;AACF,QAAA;AACI,QAAA;AACM,QAAA;AACpB,QAAA;AACX,QAAA;AACF,MAAA;AAEa,MAAA;AACK,QAAA;AACsD,QAAA;AAC9C,QAAA;AACL,QAAA;AAC4B,QAAA;AAC/C,QAAA;AACF,MAAA;AAEc,MAAA;AACG,QAAA;AACX,QAAA;AACoD,UAAA;AACY,UAAA;AAC5D,QAAA;AAER,QAAA;AACA,QAAA;AACF,MAAA;AACF,IAAA;AAEY,IAAA;AACd,EAAA;AAAA;AAAA;AAAA;AAKsF,EAAA;AAC3E,IAAA;AACS,IAAA;AACF,IAAA;AACM,IAAA;AAEqB,IAAA;AAC/B,IAAA;AAC2D,IAAA;AAGvD,IAAA;AACE,IAAA;AACA,IAAA;AACC,IAAA;AACH,IAAA;AACyB,MAAA;AACe,MAAA;AACC,MAAA;AACe,MAAA;AACxE,IAAA;AACyB,IAAA;AAC8C,MAAA;AACE,MAAA;AACzE,IAAA;AAEY,IAAA;AACd,EAAA;AAEqG,EAAA;AACjF,IAAA;AACX,MAAA;AAC+D,QAAA;AAC/D,MAAA;AACI,QAAA;AAC0B,UAAA;AACA,UAAA;AACA,UAAA;AACC,UAAA;AAClC,QAAA;AACG,MAAA;AACQ,MAAA;AAC0B,QAAA;AACA,QAAA;AAC9B,QAAA;AACF,UAAA;AACA,UAAA;AAC2C,UAAA;AACC,UAAA;AACjD,QAAA;AACF,MAAA;AACY,MAAA;AACqD,QAAA;AACd,QAAA;AACA,QAAA;AACpB,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AAC7B,QAAA;AACmE,QAAA;AACrE,MAAA;AACK,MAAA;AACI,QAAA;AACE,UAAA;AACA,UAAA;AACkC,UAAA;AAClB,UAAA;AACzB,QAAA;AACG,MAAA;AAC+D,QAAA;AACtE,IAAA;AACF,EAAA;AAAA;AAAA;AAAA;AAYE,EAAA;AAIM,IAAA;AAEK,MAAA;AACA,MAAA;AACI,MAAA;AACC,MAAA;AACV,IAAA;AACK,IAAA;AAEsB,IAAA;AACgB,IAAA;AACL,IAAA;AACN,IAAA;AACD,IAAA;AAE5B,IAAA;AAC8C,IAAA;AACnC,IAAA;AAEoD,IAAA;AAChD,IAAA;AACA,IAAA;AAE4B,IAAA;AACC,IAAA;AACV,IAAA;AACE,IAAA;AAG7B,IAAA;AAEyD,IAAA;AAEH,IAAA;AAEF,IAAA;AAEJ,IAAA;AAG9C,IAAA;AACF,IAAA;AACiC,IAAA;AAG/B,IAAA;AACF,IAAA;AACF,IAAA;AAE0B,IAAA;AACW,IAAA;AACL,IAAA;AACW,IAAA;AAEjB,IAAA;AACW,IAAA;AACL,IAAA;AACW,IAAA;AAC9C,IAAA;AAGK,IAAA;AACE,IAAA;AACF,IAAA;AACG,IAAA;AACH,IAAA;AAAA;AAES,MAAA;AACW,MAAA;AACA,MAAA;AACW,MAAA;AAAA;AAEP,MAAA;AACW,MAAA;AACX,MAAA;AACW,MAAA;AACnD,IAAA;AACyB,IAAA;AAC8C,MAAA;AACE,MAAA;AACzE,IAAA;AAEY,IAAA;AACd,EAAA;AAWQ,EAAA;AACG,IAAA;AACqC,IAAA;AAC1B,IAAA;AACiD,IAAA;AAC9C,IAAA;AACA,IAAA;AACwC,IAAA;AACnD,IAAA;AACd,EAAA;AAAA;AAAA;AAAA;AAS8B,EAAA;AACxB,IAAA;AACiC,IAAA;AACQ,MAAA;AACtC,IAAA;AACC,MAAA;AACR,IAAA;AAEmG,IAAA;AACN,IAAA;AAE5B,IAAA;AAEtB,IAAA;AACA,IAAA;AACuB,IAAA;AACE,IAAA;AAEnB,IAAA;AACL,IAAA;AACV,IAAA;AACA,IAAA;AAED,IAAA;AACC,IAAA;AAEqB,IAAA;AACf,IAAA;AACvB,IAAA;AAGwC,IAAA;AACN,IAAA;AACrC,IAAA;AACE,MAAA;AACkB,MAAA;AACO,MAAA;AACpB,MAAA;AAC8C,MAAA;AACA,MAAA;AAChD,MAAA;AAGyC,MAAA;AAC3C,MAAA;AAC0B,QAAA;AAEQ,QAAA;AACzC,UAAA;AAC4D,YAAA;AACb,YAAA;AACP,YAAA;AAC7B,YAAA;AACkD,cAAA;AACL,cAAA;AAC1C,cAAA;AACiC,gBAAA;AAC/C,cAAA;AACF,YAAA;AACM,UAAA;AAER,UAAA;AACF,QAAA;AAEqC,QAAA;AACvC,MAAA;AACF,IAAA;AAGqE,IAAA;AAGD,IAAA;AACtC,MAAA;AACA,MAAA;AACnB,MAAA;AACqB,MAAA;AACC,MAAA;AACK,QAAA;AACpC,MAAA;AACY,MAAA;AACd,IAAA;AAEO,IAAA;AACT,EAAA;AAAA;AAAA;AAAA;AAAA;AAS8B,EAAA;AACe,IAAA;AACC,IAAA;AACoB,IAAA;AAC1B,IAAA;AACrB,IAAA;AAGD,IAAA;AAC8B,IAAA;AAEV,IAAA;AACc,IAAA;AAGvB,IAAA;AACT,MAAA;AACsD,MAAA;AACxE,IAAA;AAGoD,IAAA;AACb,MAAA;AAC1B,QAAA;AACS,QAAA;AACU,UAAA;AACe,UAAA;AACb,UAAA;AAC9B,QAAA;AAEwB,QAAA;AACV,UAAA;AACe,UAAA;AACX,YAAA;AACI,YAAA;AACyB,YAAA;AAC7B,YAAA;AACC,YAAA;AAC4B,YAAA;AACC,YAAA;AACC,cAAA;AAC7C,YAAA;AACW,YAAA;AACb,UAAA;AAC8B,QAAA;AACjB,UAAA;AACG,UAAA;AACoC,UAAA;AACpB,QAAA;AACnB,UAAA;AACG,UAAA;AACF,UAAA;AACqD,UAAA;AAC1D,UAAA;AACJ,QAAA;AACa,UAAA;AACF,UAAA;AACkB,UAAA;AACpC,QAAA;AACY,QAAA;AACd,MAAA;AACF,IAAA;AAEO,IAAA;AACT,EAAA;AACF;AFpC8E;AACA;AGlsBzB;AACvC,EAAA;AACF,EAAA;AACE,EAAA;AACF,EAAA;AACG,EAAA;AACP,EAAA;AACG,EAAA;AACX;AAEiD;AACvC,EAAA;AACD,EAAA;AACA,EAAA;AACT;AAEoD;AACzC,EAAA;AACG,EAAA;AACN,EAAA;AACa,EAAA;AACT,EAAA;AACF,EAAA;AACV;AAMmC;AACzB,EAAA;AACA,EAAA;AACyC,kBAAA;AACc,kBAAA;AACvD,EAAA;AAAA;AAG2C,kBAAA;AACA,kBAAA;AAC3C,EAAA;AACoB,kBAAA;AACpB,EAAA;AAEsC,EAAA;AACM,IAAA;AACrB,IAAA;AACW,IAAA;AAEK,IAAA;AACxC,MAAA;AACsB,MAAA;AAC3B,IAAA;AAEyC,IAAA;AACpC,MAAA;AACsB,MAAA;AAC3B,IAAA;AAE2C,IAAA;AACtC,MAAA;AACsB,MAAA;AAC3B,IAAA;AAEa,IAAA;AACoD,MAAA;AACpB,MAAA;AACP,MAAA;AACzB,MAAA;AACE,MAAA;AACF,MAAA;AACwC,MAAA;AAC7B,MAAA;AACV,MAAA;AACN,MAAA;AACY,MAAA;AACE,MAAA;AACtB,IAAA;AAEiE,IAAA;AACnC,MAAA;AACC,MAAA;AACmD,QAAA;AACG,QAAA;AACnF,MAAA;AACF,IAAA;AAE2C,IAAA;AAEJ,IAAA;AACT,MAAA;AAC9B,IAAA;AACF,EAAA;AAAA;AAIsE,EAAA;AACvC,IAAA;AACJ,MAAA;AACnB,MAAA;AACgD,QAAA;AAC9B,QAAA;AACS,QAAA;AACa,UAAA;AACG,UAAA;AAC7C,QAAA;AACY,MAAA;AAEd,MAAA;AACK,IAAA;AACe,MAAA;AACuB,MAAA;AACd,MAAA;AACa,QAAA;AACG,QAAA;AAC7C,MAAA;AACF,IAAA;AAE2C,IAAA;AAC/B,IAAA;AACd,EAAA;AAEmD,EAAA;AACrC,IAAA;AACd,EAAA;AAAA;AAIoC,EAAA;AACtB,IAAA;AACd,EAAA;AAE0E,EAAA;AAC7C,IAAA;AACR,IAAA;AACN,IAAA;AACmB,MAAA;AAChC,IAAA;AACF,EAAA;AAEuB,EAAA;AACgB,IAAA;AACJ,oBAAA;AACY,IAAA;AAC/C,EAAA;AAAA;AAI8B,EAAA;AACA,IAAA;AACa,IAAA;AACS,IAAA;AAC3B,MAAA;AACvB,IAAA;AACkB,IAAA;AACpB,EAAA;AAE8B,EAAA;AACG,IAAA;AACjC,EAAA;AAE8B,EAAA;AACG,IAAA;AACjC,EAAA;AAEuB,EAAA;AACK,IAAA;AACD,IAAA;AACW,IAAA;AACT,IAAA;AAEK,IAAA;AACF,IAAA;AACL,IAAA;AACb,IAAA;AACL,IAAA;AACT,EAAA;AAEuB,EAAA;AACK,IAAA;AACD,IAAA;AACW,IAAA;AACT,IAAA;AAEK,IAAA;AACF,IAAA;AACL,IAAA;AACb,IAAA;AACL,IAAA;AACT,EAAA;AAEqB,EAAA;AACA,IAAA;AAC2B,IAAA;AAC5B,IAAA;AACY,IAAA;AACN,IAAA;AACU,IAAA;AACtB,IAAA;AACd,EAAA;AAAA;AAI4C,EAAA;AACN,IAAA;AACZ,IAAA;AACD,IAAA;AACa,MAAA;AACpC,IAAA;AAEyC,IAAA;AACrB,MAAA;AACb,QAAA;AACA,QAAA;AAC+B,QAAA;AACC,QAAA;AACrC,MAAA;AACF,IAAA;AACY,IAAA;AACd,EAAA;AAAA;AAIgE,EAAA;AAC5C,IAAA;AACG,MAAA;AACrB,IAAA;AACkB,IAAA;AACN,IAAA;AACd,EAAA;AAEiD,EAAA;AAC5B,IAAA;AACkB,IAAA;AACzB,IAAA;AAEoC,MAAA;AACzC,IAAA;AACS,MAAA;AACU,MAAA;AAEF,MAAA;AACV,QAAA;AACU,QAAA;AACtB,MAAA;AAEuD,MAAA;AACC,MAAA;AACtC,MAAA;AAChB,QAAA;AACA,QAAA;AACyB,QAAA;AACC,QAAA;AAC5B,MAAA;AACF,IAAA;AACY,IAAA;AACd,EAAA;AAEyB,EAAA;AACJ,IAAA;AACD,IAAA;AACN,IAAA;AACd,EAAA;AAAA;AAIqC,EAAA;AAChB,IAAA;AACkB,IAAA;AACe,IAAA;AACxC,IAAA;AACd,EAAA;AAE0C,EAAA;AACrB,IAAA;AACqB,IAAA;AAC5B,IAAA;AACd,EAAA;AAE8B,EAAA;AACT,IAAA;AACgC,IAAA;AACvC,IAAA;AACd,EAAA;AAE4B,EAAA;AACP,IAAA;AACgC,IAAA;AACvC,IAAA;AACd,EAAA;AAAA;AAIwF,EAAA;AACjE,IAAA;AACA,MAAA;AACrB,IAAA;AACyB,IAAA;AACT,MAAA;AACX,MAAA;AACL,IAAA;AACY,IAAA;AACd,EAAA;AAEgC,EAAA;AACX,IAAA;AAC+B,IAAA;AACtC,IAAA;AACd,EAAA;AAAA;AAI8D,EAAA;AACzC,IAAA;AACI,IAAA;AACP,MAAA;AACX,MAAA;AACL,IAAA;AACY,IAAA;AACd,EAAA;AAE8B,EAAA;AACT,IAAA;AAC4B,IAAA;AACnC,IAAA;AACd,EAAA;AAAA;AAIkE,EAAA;AAC7C,IAAA;AACmD,IAAA;AAC3B,IAAA;AACwB,IAAA;AACjC,IAAA;AACtB,IAAA;AACL,IAAA;AACT,EAAA;AAEgG,EAAA;AACzE,IAAA;AACA,MAAA;AACrB,IAAA;AAC6D,IAAA;AACxC,MAAA;AACW,QAAA;AAC9B,MAAA;AACO,MAAA;AACR,IAAA;AACW,IAAA;AACd,EAAA;AAE0C,EAAA;AACrB,IAAA;AACiD,IAAA;AACxB,IAAA;AACR,MAAA;AACpC,IAAA;AACY,IAAA;AACd,EAAA;AAEgC,EAAA;AACX,IAAA;AACO,IAAA;AACQ,IAAA;AACtB,IAAA;AACd,EAAA;AAEiD,EAAA;AACb,IAAA;AACtB,IAAA;AACd,EAAA;AAAA;AAImC,EAAA;AACiB,IAAA;AACtC,IAAA;AACd,EAAA;AAEkC,EAAA;AACK,IAAA;AACvC,EAAA;AAEmC,EAAA;AACI,IAAA;AACvC,EAAA;AAEyB,EAAA;AACL,IAAA;AACY,IAAA;AAClB,IAAA;AACd,EAAA;AAEqC,EAAA;AAClB,IAAA;AACL,IAAA;AACd,EAAA;AAE8C,EAAA;AACK,IAAA;AACjB,IAAA;AACpB,IAAA;AACd,EAAA;AAAA;AAI6F,EAAA;AAC5C,IAAA;AAClC,IAAA;AACT,IAAA;AACmE,MAAA;AAC/D,IAAA;AAER,IAAA;AACF,EAAA;AAE+E,EAAA;AAC9B,IAAA;AAClC,IAAA;AAC2B,MAAA;AACxC,IAAA;AAC6D,IAAA;AAC/D,EAAA;AAEqE,EAAA;AACrB,IAAA;AACb,IAAA;AACE,IAAA;AACI,IAAA;AACzC,EAAA;AAEgE,EAAA;AAChB,IAAA;AACb,IAAA;AACE,IAAA;AAEK,IAAA;AACnB,MAAA;AACO,QAAA;AACA,UAAA;AACoC,UAAA;AAC1C,QAAA;AACb,MAAA;AAED,QAAA;AAC8C,UAAA;AACjB,UAAA;AACF,UAAA;AACY,UAAA;AACb,UAAA;AACa,UAAA;AACX,YAAA;AAC9B,UAAA;AACwC,UAAA;AAC5B,QAAA;AACF,UAAA;AACZ,QAAA;AACF,MAAA;AACD,IAAA;AACH,EAAA;AAAA;AAI+G,EAAA;AAC1D,IAAA;AACrD,EAAA;AAEuG,EAAA;AACzD,IAAA;AACT,IAAA;AACrC,EAAA;AAEoE,EAAA;AAC/C,IAAA;AACY,IAAA;AACT,IAAA;AACxB,EAAA;AAE4E,EAAA;AACvD,IAAA;AACY,IAAA;AACjC,EAAA;AAAA;AAK4C,EAAA;AACf,IAAA;AAC7B,EAAA;AAEwD,EAAA;AACnC,IAAA;AACW,IAAA;AAClB,IAAA;AACd,EAAA;AAEqD,EAAA;AAC5C,IAAA;AACI,MAAA;AACwC,MAAA;AACZ,MAAA;AACI,MAAA;AAC9B,MAAA;AACK,QAAA;AACiC,QAAA;AACjD,MAAA;AAC8D,MAAA;AAChE,IAAA;AACF,EAAA;AAEqE,EAAA;AAClB,IAAA;AACN,IAAA;AACI,IAAA;AACxB,IAAA;AACb,MAAA;AACiC,MAAA;AAC3C,IAAA;AAC0E,IAAA;AAC5E,EAAA;AACF;AH6mB8E;AACA;AI9mCvD;AAG8C,EAAA;AACrB,EAAA;AACE,EAAA;AAG1C,EAAA;AAGiE,EAAA;AACC,EAAA;AAEvB,EAAA;AACL,EAAA;AACV,EAAA;AACA,EAAA;AAE8C,EAAA;AACxD,EAAA;AACA,EAAA;AACE,EAAA;AAEQ,EAAA;AACC,EAAA;AAEjB,EAAA;AACgB,IAAA;AACA,IAAA;AACf,IAAA;AACC,IAAA;AACpB,EAAA;AAE+E,EAAA;AAC/D,EAAA;AACgD,IAAA;AACC,IAAA;AACV,IAAA;AACE,IAAA;AAE5C,IAAA;AACW,MAAA;AACA,MAAA;AACN,MAAA;AACC,MAAA;AACjB,IAAA;AACF,EAAA;AAEO,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAUE;AAE6D,EAAA;AACJ,EAAA;AACA,EAAA;AAEH,EAAA;AACA,EAAA;AAEV,EAAA;AAC9C;AAUE;AAE6D,EAAA;AACH,EAAA;AACA,EAAA;AAEN,EAAA;AACA,EAAA;AAEI,EAAA;AAC1D;AAUoB;AAGiD,EAAA;AACrC,EAAA;AAEgB,EAAA;AAC1B,EAAA;AACA,EAAA;AAIS,EAAA;AAEqB,EAAA;AACI,EAAA;AACI,EAAA;AACJ,EAAA;AAGG,EAAA;AACA,EAAA;AAEA,EAAA;AACM,EAAA;AACN,EAAA;AACM,EAAA;AAGT,EAAA;AAC7C,IAAA;AACT,EAAA;AAEO,EAAA;AACT;AAK0D;AACxC,EAAA;AACT,IAAA;AACA,IAAA;AACI,MAAA;AACJ,IAAA;AACA,IAAA;AACI,MAAA;AACJ,IAAA;AACA,IAAA;AACI,MAAA;AACJ,IAAA;AACA,IAAA;AACI,MAAA;AACJ,IAAA;AACI,MAAA;AACT,IAAA;AACS,MAAA;AACX,EAAA;AACF;AAUE;AAIuC,EAAA;AACA,EAAA;AAGd,EAAA;AAC4B,IAAA;AACE,IAAA;AACI,IAAA;AACA,IAAA;AAClD,IAAA;AACa,MAAA;AACA,MAAA;AACD,MAAA;AACC,MAAA;AACpB,IAAA;AACF,EAAA;AAGqB,EAAA;AACD,EAAA;AACgB,EAAA;AACC,EAAA;AAErB,EAAA;AAE0C,EAAA;AACU,IAAA;AACpE,EAAA;AAC0D,EAAA;AACM,IAAA;AAChE,EAAA;AAC0D,EAAA;AACW,IAAA;AACrE,EAAA;AAC0D,EAAA;AACM,IAAA;AAChE,EAAA;AAGoC,EAAA;AACX,IAAA;AACC,IAAA;AAEc,IAAA;AACiB,MAAA;AACV,MAAA;AACA,IAAA;AACS,MAAA;AACV,MAAA;AACrC,IAAA;AAEsB,MAAA;AACK,MAAA;AACf,QAAA;AACV,MAAA;AACsB,QAAA;AACc,QAAA;AAC3C,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACa,IAAA;AACD,IAAA;AACgC,IAAA;AACC,IAAA;AACpD,EAAA;AACF;AAKgI;AAC5G,EAAA;AACX,IAAA;AAC+D,MAAA;AAC/D,IAAA;AACI,MAAA;AACU,QAAA;AACA,QAAA;AACM,QAAA;AACC,QAAA;AACxB,MAAA;AACG,IAAA;AACQ,IAAA;AAC0B,MAAA;AACA,MAAA;AAC9B,MAAA;AACF,QAAA;AACA,QAAA;AAC2C,QAAA;AACC,QAAA;AACjD,MAAA;AACF,IAAA;AACY,IAAA;AAC6D,MAAA;AAC9C,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACI,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AAC7B,MAAA;AACmE,MAAA;AACrE,IAAA;AACK,IAAA;AACI,MAAA;AACE,QAAA;AACA,QAAA;AACiC,QAAA;AACjB,QAAA;AACzB,MAAA;AACG,IAAA;AAC+D,MAAA;AACtE,EAAA;AACF;AAK+G;AAC5E,EAAA;AAI/B,EAAA;AAGJ;AJ6gC8E;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/editor/editor/packages/core/dist/image-editor/index.cjs","sourcesContent":[null,"import type { DepthMaskOptions, ImageAdjustments } from './types';\n\nfunction clamp(value: number, min = 0, max = 255): number {\n return Math.max(min, Math.min(max, value));\n}\n\n/**\n * Applies color and tone adjustments (brightness, contrast, saturation, exposure, temperature, opacity, blur)\n * directly to the pixel data of an ImageData buffer.\n */\nexport function applyAdjustments(imageData: ImageData, adjustments: ImageAdjustments): void {\n const { brightness, contrast, saturation, exposure, temperature, opacity, blur } = adjustments;\n const data = imageData.data;\n const len = data.length;\n\n const hasBrightness = brightness !== 0;\n const hasContrast = contrast !== 0;\n const hasSaturation = saturation !== 0;\n const hasExposure = exposure !== 0;\n const hasTemperature = temperature !== 0;\n const hasOpacity = opacity < 1;\n\n // Pre-calculate contrast factor\n const contrastFactor = hasContrast\n ? (259 * (contrast + 255)) / (255 * (259 - contrast))\n : 1;\n\n // Pre-calculate exposure multiplier\n const exposureMultiplier = hasExposure ? Math.pow(2, exposure / 50) : 1;\n\n // Pre-calculate saturation multiplier\n const satMultiplier = hasSaturation ? 1 + saturation / 100 : 1;\n\n // Pre-calculate temperature offsets\n const tempOffset = hasTemperature ? temperature * 0.8 : 0;\n\n if (hasBrightness || hasContrast || hasSaturation || hasExposure || hasTemperature || hasOpacity) {\n for (let i = 0; i < len; i += 4) {\n let r = data[i];\n let g = data[i + 1];\n let b = data[i + 2];\n let a = data[i + 3];\n\n // Exposure\n if (hasExposure) {\n r = clamp(r * exposureMultiplier);\n g = clamp(g * exposureMultiplier);\n b = clamp(b * exposureMultiplier);\n }\n\n // Brightness\n if (hasBrightness) {\n const bOffset = brightness * 2.55;\n r = clamp(r + bOffset);\n g = clamp(g + bOffset);\n b = clamp(b + bOffset);\n }\n\n // Contrast\n if (hasContrast) {\n r = clamp(contrastFactor * (r - 128) + 128);\n g = clamp(contrastFactor * (g - 128) + 128);\n b = clamp(contrastFactor * (b - 128) + 128);\n }\n\n // Saturation\n if (hasSaturation) {\n const luminance = 0.2989 * r + 0.587 * g + 0.114 * b;\n r = clamp(luminance + (r - luminance) * satMultiplier);\n g = clamp(luminance + (g - luminance) * satMultiplier);\n b = clamp(luminance + (b - luminance) * satMultiplier);\n }\n\n // Temperature / Warmth\n if (hasTemperature) {\n r = clamp(r + tempOffset);\n b = clamp(b - tempOffset);\n }\n\n // Opacity\n if (hasOpacity) {\n a = clamp(a * opacity);\n }\n\n data[i] = r;\n data[i + 1] = g;\n data[i + 2] = b;\n data[i + 3] = a;\n }\n }\n\n // Apply blur if requested\n if (blur > 0) {\n applyFastBoxBlur(imageData, Math.min(Math.round(blur), 40));\n }\n}\n\n/**\n * Fast separable box blur running horizontally and vertically.\n */\nfunction applyFastBoxBlur(imageData: ImageData, radius: number): void {\n if (radius < 1) return;\n const w = imageData.width;\n const h = imageData.height;\n const src = imageData.data;\n const temp = new Uint8ClampedArray(src.length);\n\n // Horizontal blur pass\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < w; x++) {\n let r = 0, g = 0, b = 0, a = 0;\n let count = 0;\n for (let k = -radius; k <= radius; k++) {\n const px = Math.min(w - 1, Math.max(0, x + k));\n const idx = (y * w + px) * 4;\n r += src[idx];\n g += src[idx + 1];\n b += src[idx + 2];\n a += src[idx + 3];\n count++;\n }\n const dstIdx = (y * w + x) * 4;\n temp[dstIdx] = Math.round(r / count);\n temp[dstIdx + 1] = Math.round(g / count);\n temp[dstIdx + 2] = Math.round(b / count);\n temp[dstIdx + 3] = Math.round(a / count);\n }\n }\n\n // Vertical blur pass back into src\n for (let x = 0; x < w; x++) {\n for (let y = 0; y < h; y++) {\n let r = 0, g = 0, b = 0, a = 0;\n let count = 0;\n for (let k = -radius; k <= radius; k++) {\n const py = Math.min(h - 1, Math.max(0, y + k));\n const idx = (py * w + x) * 4;\n r += temp[idx];\n g += temp[idx + 1];\n b += temp[idx + 2];\n a += temp[idx + 3];\n count++;\n }\n const dstIdx = (y * w + x) * 4;\n src[dstIdx] = Math.round(r / count);\n src[dstIdx + 1] = Math.round(g / count);\n src[dstIdx + 2] = Math.round(b / count);\n src[dstIdx + 3] = Math.round(a / count);\n }\n }\n}\n\n/**\n * Blends a depth/alpha mask into the main image data based on Gleamforge depth masking.\n * Supports threshold range with softness gradation, brightness mode, and optional inversion.\n */\nexport function applyDepthMask(\n mainImageData: ImageData,\n maskImageData: ImageData,\n options: DepthMaskOptions\n): void {\n const { mode, depthRange, softness, invert } = options;\n const mainData = mainImageData.data;\n const maskData = maskImageData.data;\n const len = Math.min(mainData.length, maskData.length);\n const effectiveSoftness = Math.max(1, softness || 10);\n const minDepth = depthRange[0];\n const maxDepth = depthRange[1];\n\n for (let i = 0; i < len; i += 4) {\n let maskValue = maskData[i]; // Use red channel of mask (or grayscale)\n if (invert) {\n maskValue = 255 - maskValue;\n }\n\n let alphaValue: number;\n\n if (mode === 'threshold') {\n if (maskValue >= maxDepth + effectiveSoftness) {\n alphaValue = 0;\n } else if (maskValue > maxDepth) {\n alphaValue = Math.round(((maxDepth + effectiveSoftness - maskValue) / effectiveSoftness) * 255);\n } else if (maskValue >= minDepth) {\n alphaValue = 255;\n } else if (maskValue < minDepth - effectiveSoftness) {\n alphaValue = 0;\n } else {\n alphaValue = Math.round(((maskValue - (minDepth - effectiveSoftness)) / effectiveSoftness) * 255);\n }\n } else {\n // Brightness mode\n alphaValue = clamp(maskValue + minDepth);\n }\n\n // Multiply existing alpha with mask alpha\n mainData[i + 3] = clamp((mainData[i + 3] * alphaValue) / 255);\n }\n}\n\n/**\n * Removes alpha channel by setting all alpha bytes to 255 (completely opaque).\n * From Gleamforge image-filters.ts.\n */\nexport function removeAlpha(imageData: ImageData): void {\n const data = imageData.data;\n const len = data.length;\n for (let i = 3; i < len; i += 4) {\n data[i] = 255;\n }\n}\n","import type {\n Annotation,\n CropBox,\n ExportOptions,\n ImageEditorState,\n ImageTransform,\n PenAnnotation,\n RectAnnotation,\n CircleAnnotation,\n ArrowAnnotation,\n LineAnnotation,\n TextAnnotation,\n ImageAnnotation,\n} from './types';\nimport { applyAdjustments, applyDepthMask } from './filters';\n\nexport interface RenderOptions {\n viewportWidth?: number;\n viewportHeight?: number;\n showCropOverlay?: boolean;\n showSelectionOverlay?: boolean;\n interactive?: boolean;\n draftAnnotation?: Annotation | null;\n draftCrop?: CropBox | null;\n}\n\n/**\n * Image Cache to avoid refetching HTMLImageElement objects repeatedly during re-renders.\n */\nclass ImageCache {\n private cache = new Map<string, HTMLImageElement>();\n\n public async get(src: string): Promise<HTMLImageElement> {\n const existing = this.cache.get(src);\n if (existing && existing.complete) {\n return existing;\n }\n\n return new Promise((resolve, reject) => {\n if (typeof window === 'undefined' || typeof Image === 'undefined') {\n reject(new Error('Image loading is only supported in browser/DOM environments'));\n return;\n }\n const img = new Image();\n img.crossOrigin = 'anonymous';\n img.onload = () => {\n this.cache.set(src, img);\n resolve(img);\n };\n img.onerror = (e) => reject(new Error(`Failed to load image from: ${src}`));\n img.src = src;\n\n // In JSDOM or test environments where Image resource loading is inactive, resolve promptly\n if (typeof navigator !== 'undefined' && /jsdom/i.test(navigator.userAgent)) {\n setTimeout(() => {\n try {\n Object.defineProperty(img, 'naturalWidth', { value: 800, configurable: true });\n Object.defineProperty(img, 'naturalHeight', { value: 600, configurable: true });\n Object.defineProperty(img, 'complete', { value: true, configurable: true });\n } catch {\n // Ignore definition errors in restrictive environments\n }\n this.cache.set(src, img);\n resolve(img);\n }, 10);\n }\n });\n }\n\n public set(src: string, img: HTMLImageElement): void {\n this.cache.set(src, img);\n }\n\n public clear(): void {\n this.cache.clear();\n }\n}\n\nexport const globalImageCache = new ImageCache();\n\n/**\n * Renders an image with all its transformations, filters, depth masks, and annotations\n * to a target HTMLCanvasElement.\n */\nexport class ImageEditorRenderer {\n public imageCache = globalImageCache;\n private cachedBaseCanvas: HTMLCanvasElement | null = null;\n private cachedBaseKey = '';\n\n /**\n * Helper to create a canvas element safely in browser/JSDOM.\n */\n public createCanvas(width: number, height: number): HTMLCanvasElement {\n if (typeof document !== 'undefined' && document.createElement) {\n const c = document.createElement('canvas');\n c.width = Math.max(1, Math.round(width));\n c.height = Math.max(1, Math.round(height));\n return c;\n }\n // Fallback for non-DOM / headless\n return {\n width: Math.max(1, Math.round(width)),\n height: Math.max(1, Math.round(height)),\n getContext: () => null,\n toDataURL: () => '',\n toBlob: () => {},\n } as unknown as HTMLCanvasElement;\n }\n\n /**\n * Renders the editor state onto a canvas (viewport or preview).\n */\n public async renderToCanvas(\n targetCanvas: HTMLCanvasElement,\n imageSource: CanvasImageSource | string,\n state: ImageEditorState,\n options: RenderOptions = {}\n ): Promise<void> {\n const ctx = targetCanvas.getContext('2d');\n if (!ctx) return;\n\n let img: CanvasImageSource;\n if (typeof imageSource === 'string') {\n img = await this.imageCache.get(imageSource);\n } else {\n img = imageSource;\n }\n\n const naturalWidth = (img as any).naturalWidth || (img as any).videoWidth || (img as any).width || 800;\n const naturalHeight = (img as any).naturalHeight || (img as any).videoHeight || (img as any).height || 600;\n\n const { crop, transform, adjustments, depthMask, annotations, selectedAnnotationId, zoom, pan, isOriginalCompared } = state;\n\n // Viewport dimensions\n const vpWidth = options.viewportWidth || targetCanvas.width;\n const vpHeight = options.viewportHeight || targetCanvas.height;\n\n targetCanvas.width = vpWidth;\n targetCanvas.height = vpHeight;\n\n ctx.clearRect(0, 0, vpWidth, vpHeight);\n\n // If in compare mode, render original image directly and exit\n if (isOriginalCompared) {\n this.drawFittedImage(ctx, img, naturalWidth, naturalHeight, vpWidth, vpHeight, zoom, pan);\n return;\n }\n\n // Determine working base dimensions based on crop or natural\n // When actively in crop tool or overlay requested, show full image so user can adjust the crop box\n const isActivelyCropping = Boolean(options.showCropOverlay || state.activeTool === 'crop');\n const cropX = (!isActivelyCropping && crop) ? Math.max(0, crop.x) : 0;\n const cropY = (!isActivelyCropping && crop) ? Math.max(0, crop.y) : 0;\n const cropW = (!isActivelyCropping && crop) ? Math.min(naturalWidth - cropX, crop.width) : naturalWidth;\n const cropH = (!isActivelyCropping && crop) ? Math.min(naturalHeight - cropY, crop.height) : naturalHeight;\n\n const angle = ((transform.rotate % 360) + 360) % 360;\n const isSwapped = angle === 90 || angle === 270;\n const baseW = isSwapped ? cropH : cropW;\n const baseH = isSwapped ? cropW : cropH;\n\n // 1. Offscreen canvas for Base Image + Transform (Flip/Rotate) + Crop + Adjustments\n const baseKey = `${state.sourceUrl}-${naturalWidth}x${naturalHeight}-${cropX},${cropY},${cropW},${cropH}-${angle},${transform.flipH},${transform.flipV}-${adjustments.brightness},${adjustments.contrast},${adjustments.saturation},${adjustments.exposure},${adjustments.temperature},${adjustments.blur},${adjustments.opacity}-${depthMask.enabled},${depthMask.depthRange?.[0]}-${depthMask.depthRange?.[1]},${depthMask.softness},${depthMask.invert},${depthMask.mode}`;\n\n let baseCanvas = this.cachedBaseCanvas;\n if (!baseCanvas || this.cachedBaseKey !== baseKey) {\n baseCanvas = this.createCanvas(baseW, baseH);\n const baseCtx = baseCanvas.getContext('2d');\n\n if (baseCtx) {\n baseCtx.save();\n\n // Handle Flip & Rotate\n const rad = (angle * Math.PI) / 180;\n baseCtx.translate(baseW / 2, baseH / 2);\n baseCtx.rotate(rad);\n baseCtx.scale(transform.flipH ? -1 : 1, transform.flipV ? -1 : 1);\n\n // Draw the cropped portion centered\n baseCtx.drawImage(\n img,\n cropX, cropY, cropW, cropH,\n -cropW / 2, -cropH / 2, cropW, cropH\n );\n\n baseCtx.restore();\n\n // 2. Pixel adjustments & Depth Masking\n const imageData = baseCtx.getImageData(0, 0, baseW, baseH);\n if (imageData) {\n applyAdjustments(imageData, adjustments);\n\n if (depthMask.enabled && depthMask.maskSource) {\n try {\n const maskImg = await this.imageCache.get(depthMask.maskSource);\n const maskCanvas = this.createCanvas(baseW, baseH);\n const maskCtx = maskCanvas.getContext('2d');\n if (maskCtx) {\n maskCtx.drawImage(maskImg, cropX, cropY, cropW, cropH, 0, 0, baseW, baseH);\n const maskData = maskCtx.getImageData(0, 0, baseW, baseH);\n if (maskData) {\n applyDepthMask(imageData, maskData, depthMask);\n }\n }\n } catch {\n // Mask failed to load; continue without crashing\n }\n }\n\n baseCtx.putImageData(imageData, 0, 0);\n }\n }\n this.cachedBaseCanvas = baseCanvas;\n this.cachedBaseKey = baseKey;\n }\n\n // 3. Render base canvas onto target canvas with viewport zoom & pan\n ctx.save();\n // Center of canvas\n ctx.translate(vpWidth / 2 + pan.x, vpHeight / 2 + pan.y);\n ctx.scale(zoom, zoom);\n\n // Calculate fitted dimensions\n const fitScale = Math.min((vpWidth * 0.85) / baseW, (vpHeight * 0.85) / baseH, 1);\n const renderW = baseW * fitScale;\n const renderH = baseH * fitScale;\n\n // Draw base image\n ctx.drawImage(baseCanvas, -renderW / 2, -renderH / 2, renderW, renderH);\n\n // 4. Render Annotations scaled to fit image space\n const scaleFactor = fitScale;\n ctx.save();\n ctx.translate(-renderW / 2, -renderH / 2);\n ctx.scale(scaleFactor, scaleFactor);\n\n for (const ann of annotations) {\n await this.drawAnnotation(ctx, ann);\n if (options.showSelectionOverlay && ann.id === selectedAnnotationId) {\n this.drawAnnotationSelection(ctx, ann);\n }\n }\n\n // Render active live draft annotation\n if (options.draftAnnotation) {\n await this.drawAnnotation(ctx, options.draftAnnotation);\n }\n\n ctx.restore();\n\n ctx.restore();\n\n // 5. Draw Crop Grid Overlay if crop overlay is requested\n const isCroppingActive = Boolean(options.showCropOverlay || state.activeTool === 'crop');\n const activeCrop =\n (options.draftCrop !== undefined && options.draftCrop !== null)\n ? options.draftCrop\n : (crop ?? (isCroppingActive ? {\n x: 0,\n y: 0,\n width: naturalWidth,\n height: naturalHeight,\n } : null));\n\n if (isCroppingActive && activeCrop) {\n this.drawCropOverlay(ctx, state, vpWidth, vpHeight, naturalWidth, naturalHeight, activeCrop);\n }\n }\n\n /**\n * Draws an individual annotation onto the canvas.\n */\n public async drawAnnotation(ctx: CanvasRenderingContext2D, ann: Annotation): Promise<void> {\n ctx.save();\n if (ann.opacity !== undefined) {\n ctx.globalAlpha = ann.opacity;\n }\n\n if (ann.rotation) {\n ctx.translate(ann.x, ann.y);\n ctx.rotate((ann.rotation * Math.PI) / 180);\n ctx.translate(-ann.x, -ann.y);\n }\n\n switch (ann.type) {\n case 'pen': {\n const pen = ann as PenAnnotation;\n if (pen.points.length > 0) {\n ctx.beginPath();\n ctx.strokeStyle = pen.strokeColor;\n ctx.lineWidth = pen.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.moveTo(pen.points[0].x, pen.points[0].y);\n for (let i = 1; i < pen.points.length; i++) {\n ctx.lineTo(pen.points[i].x, pen.points[i].y);\n }\n ctx.stroke();\n }\n break;\n }\n\n case 'rect': {\n const rect = ann as RectAnnotation;\n ctx.strokeStyle = rect.strokeColor;\n ctx.lineWidth = rect.strokeWidth;\n if (rect.fillColor) {\n ctx.fillStyle = rect.fillColor;\n ctx.fillRect(rect.x, rect.y, rect.width, rect.height);\n }\n if (rect.strokeWidth > 0) {\n ctx.strokeRect(rect.x, rect.y, rect.width, rect.height);\n }\n break;\n }\n\n case 'circle': {\n const circle = ann as CircleAnnotation;\n ctx.beginPath();\n ctx.strokeStyle = circle.strokeColor;\n ctx.lineWidth = circle.strokeWidth;\n ctx.ellipse(circle.x, circle.y, Math.abs(circle.radiusX), Math.abs(circle.radiusY), 0, 0, 2 * Math.PI);\n if (circle.fillColor) {\n ctx.fillStyle = circle.fillColor;\n ctx.fill();\n }\n if (circle.strokeWidth > 0) {\n ctx.stroke();\n }\n break;\n }\n\n case 'arrow': {\n const arrow = ann as ArrowAnnotation;\n ctx.strokeStyle = arrow.strokeColor;\n ctx.fillStyle = arrow.strokeColor;\n ctx.lineWidth = arrow.strokeWidth;\n\n // Line body\n ctx.beginPath();\n ctx.moveTo(arrow.x, arrow.y);\n ctx.lineTo(arrow.endX, arrow.endY);\n ctx.stroke();\n\n // Arrow head\n const angle = Math.atan2(arrow.endY - arrow.y, arrow.endX - arrow.x);\n const headLen = Math.max(12, arrow.strokeWidth * 3);\n ctx.beginPath();\n ctx.moveTo(arrow.endX, arrow.endY);\n ctx.lineTo(\n arrow.endX - headLen * Math.cos(angle - Math.PI / 6),\n arrow.endY - headLen * Math.sin(angle - Math.PI / 6)\n );\n ctx.lineTo(\n arrow.endX - headLen * Math.cos(angle + Math.PI / 6),\n arrow.endY - headLen * Math.sin(angle + Math.PI / 6)\n );\n ctx.closePath();\n ctx.fill();\n break;\n }\n\n case 'line': {\n const line = ann as LineAnnotation;\n ctx.beginPath();\n ctx.strokeStyle = line.strokeColor;\n ctx.lineWidth = line.strokeWidth;\n ctx.moveTo(line.x, line.y);\n ctx.lineTo(line.endX, line.endY);\n ctx.stroke();\n break;\n }\n\n case 'text': {\n const textAnn = ann as TextAnnotation;\n ctx.font = `${textAnn.fontWeight || 'normal'} ${textAnn.fontSize}px ${textAnn.fontFamily || 'sans-serif'}`;\n ctx.fillStyle = textAnn.color;\n ctx.textBaseline = 'top';\n ctx.fillText(textAnn.text, textAnn.x, textAnn.y);\n break;\n }\n\n case 'image': {\n const imgAnn = ann as ImageAnnotation;\n try {\n const loadedImg = await this.imageCache.get(imgAnn.src);\n ctx.drawImage(loadedImg, imgAnn.x, imgAnn.y, imgAnn.width, imgAnn.height);\n } catch {\n // Skip if watermark image failed to load\n }\n break;\n }\n }\n\n ctx.restore();\n }\n\n /**\n * Draws selection box around active annotation.\n */\n private drawAnnotationSelection(ctx: CanvasRenderingContext2D, ann: Annotation): void {\n ctx.save();\n ctx.strokeStyle = '#3b82f6'; // Tailwind blue-500\n ctx.lineWidth = 1.5;\n ctx.setLineDash([4, 4]);\n\n const bounds = this.getAnnotationBounds(ann);\n const pad = 4;\n ctx.strokeRect(bounds.x - pad, bounds.y - pad, bounds.width + pad * 2, bounds.height + pad * 2);\n\n // Draw handles\n ctx.fillStyle = '#ffffff';\n ctx.strokeStyle = '#3b82f6';\n ctx.setLineDash([]);\n const handleSize = 6;\n const corners = [\n { x: bounds.x - pad, y: bounds.y - pad },\n { x: bounds.x + bounds.width + pad, y: bounds.y - pad },\n { x: bounds.x - pad, y: bounds.y + bounds.height + pad },\n { x: bounds.x + bounds.width + pad, y: bounds.y + bounds.height + pad },\n ];\n for (const c of corners) {\n ctx.fillRect(c.x - handleSize / 2, c.y - handleSize / 2, handleSize, handleSize);\n ctx.strokeRect(c.x - handleSize / 2, c.y - handleSize / 2, handleSize, handleSize);\n }\n\n ctx.restore();\n }\n\n public getAnnotationBounds(ann: Annotation): { x: number; y: number; width: number; height: number } {\n switch (ann.type) {\n case 'rect':\n return { x: ann.x, y: ann.y, width: ann.width, height: ann.height };\n case 'circle':\n return {\n x: ann.x - Math.abs(ann.radiusX),\n y: ann.y - Math.abs(ann.radiusY),\n width: Math.abs(ann.radiusX) * 2,\n height: Math.abs(ann.radiusY) * 2,\n };\n case 'arrow':\n case 'line': {\n const minX = Math.min(ann.x, ann.endX);\n const minY = Math.min(ann.y, ann.endY);\n return {\n x: minX,\n y: minY,\n width: Math.max(Math.abs(ann.endX - ann.x), 10),\n height: Math.max(Math.abs(ann.endY - ann.y), 10),\n };\n }\n case 'pen': {\n if (!ann.points.length) return { x: ann.x, y: ann.y, width: 0, height: 0 };\n let minX = ann.points[0].x, maxX = ann.points[0].x;\n let minY = ann.points[0].y, maxY = ann.points[0].y;\n for (const pt of ann.points) {\n if (pt.x < minX) minX = pt.x;\n if (pt.x > maxX) maxX = pt.x;\n if (pt.y < minY) minY = pt.y;\n if (pt.y > maxY) maxY = pt.y;\n }\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n }\n case 'text':\n return {\n x: ann.x,\n y: ann.y,\n width: (ann.text.length * ann.fontSize * 0.6),\n height: ann.fontSize * 1.2,\n };\n case 'image':\n return { x: ann.x, y: ann.y, width: ann.width, height: ann.height };\n }\n }\n\n /**\n * Draws a rule-of-thirds crop grid overlay.\n */\n private drawCropOverlay(\n ctx: CanvasRenderingContext2D,\n state: ImageEditorState,\n vpWidth: number,\n vpHeight: number,\n imgWidth: number,\n imgHeight: number,\n cropOverride?: CropBox | null\n ): void {\n const crop =\n (cropOverride !== undefined && cropOverride !== null)\n ? cropOverride\n : (state.crop ?? {\n x: 0,\n y: 0,\n width: imgWidth,\n height: imgHeight,\n });\n if (!crop) return;\n\n const { transform, pan, zoom } = state;\n const angle = ((transform.rotate % 360) + 360) % 360;\n const isSwapped = angle === 90 || angle === 270;\n const baseW = isSwapped ? imgHeight : imgWidth;\n const baseH = isSwapped ? imgWidth : imgHeight;\n\n ctx.save();\n ctx.translate(vpWidth / 2 + pan.x, vpHeight / 2 + pan.y);\n ctx.scale(zoom, zoom);\n\n const fitScale = Math.min((vpWidth * 0.85) / baseW, (vpHeight * 0.85) / baseH, 1);\n const renderW = baseW * fitScale;\n const renderH = baseH * fitScale;\n\n const startX = -renderW / 2 + (crop.x / imgWidth) * renderW;\n const startY = -renderH / 2 + (crop.y / imgHeight) * renderH;\n const cropBoxW = (crop.width / imgWidth) * renderW;\n const cropBoxH = (crop.height / imgHeight) * renderH;\n\n // Dark semi-transparent scrim outside crop box\n ctx.fillStyle = 'rgba(0, 0, 0, 0.45)';\n // Top\n ctx.fillRect(-renderW / 2, -renderH / 2, renderW, startY - (-renderH / 2));\n // Bottom\n ctx.fillRect(-renderW / 2, startY + cropBoxH, renderW, renderH / 2 - (startY + cropBoxH));\n // Left\n ctx.fillRect(-renderW / 2, startY, startX - (-renderW / 2), cropBoxH);\n // Right\n ctx.fillRect(startX + cropBoxW, startY, renderW / 2 - (startX + cropBoxW), cropBoxH);\n\n // Crop border\n ctx.strokeStyle = '#ffffff';\n ctx.lineWidth = 2;\n ctx.strokeRect(startX, startY, cropBoxW, cropBoxH);\n\n // Rule of thirds lines\n ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)';\n ctx.lineWidth = 1;\n ctx.beginPath();\n // Vertical lines\n ctx.moveTo(startX + cropBoxW / 3, startY);\n ctx.lineTo(startX + cropBoxW / 3, startY + cropBoxH);\n ctx.moveTo(startX + (cropBoxW * 2) / 3, startY);\n ctx.lineTo(startX + (cropBoxW * 2) / 3, startY + cropBoxH);\n // Horizontal lines\n ctx.moveTo(startX, startY + cropBoxH / 3);\n ctx.lineTo(startX + cropBoxW, startY + cropBoxH / 3);\n ctx.moveTo(startX, startY + (cropBoxH * 2) / 3);\n ctx.lineTo(startX + cropBoxW, startY + (cropBoxH * 2) / 3);\n ctx.stroke();\n\n // Corner and edge handles\n ctx.fillStyle = '#ffffff';\n ctx.strokeStyle = '#0284c7';\n ctx.lineWidth = 1.5;\n const handleSize = 10;\n const handles = [\n // Corners\n { x: startX, y: startY },\n { x: startX + cropBoxW, y: startY },\n { x: startX, y: startY + cropBoxH },\n { x: startX + cropBoxW, y: startY + cropBoxH },\n // Edges\n { x: startX + cropBoxW / 2, y: startY },\n { x: startX + cropBoxW / 2, y: startY + cropBoxH },\n { x: startX, y: startY + cropBoxH / 2 },\n { x: startX + cropBoxW, y: startY + cropBoxH / 2 },\n ];\n for (const h of handles) {\n ctx.fillRect(h.x - handleSize / 2, h.y - handleSize / 2, handleSize, handleSize);\n ctx.strokeRect(h.x - handleSize / 2, h.y - handleSize / 2, handleSize, handleSize);\n }\n\n ctx.restore();\n }\n\n private drawFittedImage(\n ctx: CanvasRenderingContext2D,\n img: CanvasImageSource,\n imgW: number,\n imgH: number,\n vpW: number,\n vpH: number,\n zoom: number,\n pan: { x: number; y: number }\n ): void {\n ctx.save();\n ctx.translate(vpW / 2 + pan.x, vpH / 2 + pan.y);\n ctx.scale(zoom, zoom);\n const fitScale = Math.min((vpW * 0.85) / imgW, (vpH * 0.85) / imgH, 1);\n const renderW = imgW * fitScale;\n const renderH = imgH * fitScale;\n ctx.drawImage(img, -renderW / 2, -renderH / 2, renderW, renderH);\n ctx.restore();\n }\n\n /**\n * Performs an export render at exact pixel dimensions without zoom/pan viewport offsets.\n */\n public async renderExport(\n imageSource: CanvasImageSource | string,\n state: ImageEditorState,\n options: ExportOptions = {}\n ): Promise<HTMLCanvasElement> {\n let img: CanvasImageSource;\n if (typeof imageSource === 'string') {\n img = await this.imageCache.get(imageSource);\n } else {\n img = imageSource;\n }\n\n const naturalWidth = (img as any).naturalWidth || (img as any).videoWidth || (img as any).width || 800;\n const naturalHeight = (img as any).naturalHeight || (img as any).videoHeight || (img as any).height || 600;\n\n const { crop, transform, adjustments, depthMask, annotations } = state;\n\n const cropX = crop ? Math.max(0, crop.x) : 0;\n const cropY = crop ? Math.max(0, crop.y) : 0;\n const cropW = crop ? Math.min(naturalWidth - cropX, crop.width) : naturalWidth;\n const cropH = crop ? Math.min(naturalHeight - cropY, crop.height) : naturalHeight;\n\n const angle = ((transform.rotate % 360) + 360) % 360;\n const isSwapped = angle === 90 || angle === 270;\n const baseW = isSwapped ? cropH : cropW;\n const baseH = isSwapped ? cropW : cropH;\n\n const exportW = options.width || baseW;\n const exportH = options.height || baseH;\n\n const exportCanvas = this.createCanvas(exportW, exportH);\n const ctx = exportCanvas.getContext('2d');\n if (!ctx) return exportCanvas;\n\n // Draw transformed base image\n const intermediateCanvas = this.createCanvas(baseW, baseH);\n const interCtx = intermediateCanvas.getContext('2d');\n if (interCtx) {\n interCtx.save();\n const rad = (angle * Math.PI) / 180;\n interCtx.translate(baseW / 2, baseH / 2);\n interCtx.rotate(rad);\n interCtx.scale(transform.flipH ? -1 : 1, transform.flipV ? -1 : 1);\n interCtx.drawImage(img, cropX, cropY, cropW, cropH, -cropW / 2, -cropH / 2, cropW, cropH);\n interCtx.restore();\n\n // Apply adjustments\n const imageData = interCtx.getImageData(0, 0, baseW, baseH);\n if (imageData) {\n applyAdjustments(imageData, adjustments);\n\n if (depthMask.enabled && depthMask.maskSource) {\n try {\n const maskImg = await this.imageCache.get(depthMask.maskSource);\n const maskCanvas = this.createCanvas(baseW, baseH);\n const maskCtx = maskCanvas.getContext('2d');\n if (maskCtx) {\n maskCtx.drawImage(maskImg, cropX, cropY, cropW, cropH, 0, 0, baseW, baseH);\n const maskData = maskCtx.getImageData(0, 0, baseW, baseH);\n if (maskData) {\n applyDepthMask(imageData, maskData, depthMask);\n }\n }\n } catch {\n // Mask skip\n }\n }\n\n interCtx.putImageData(imageData, 0, 0);\n }\n }\n\n // Scale to target export dimensions\n ctx.drawImage(intermediateCanvas, 0, 0, baseW, baseH, 0, 0, exportW, exportH);\n\n // Draw annotations if included\n if (options.includeAnnotations !== false && annotations.length > 0) {\n const annScaleX = exportW / baseW;\n const annScaleY = exportH / baseH;\n ctx.save();\n ctx.scale(annScaleX, annScaleY);\n for (const ann of annotations) {\n await this.drawAnnotation(ctx, ann);\n }\n ctx.restore();\n }\n\n return exportCanvas;\n }\n\n /**\n * Generates a binary mask canvas (white for mask/selected area, black for unmasked).\n * Useful for AI inpainting and selective generative fill.\n */\n public async renderMask(\n state: ImageEditorState,\n options?: { useAnnotations?: boolean; useCrop?: boolean }\n ): Promise<HTMLCanvasElement> {\n const naturalWidth = state.imageDimensions.width;\n const naturalHeight = state.imageDimensions.height;\n const maskCanvas = this.createCanvas(naturalWidth, naturalHeight);\n const ctx = maskCanvas.getContext('2d');\n if (!ctx) return maskCanvas;\n\n // Background: unmasked (black)\n ctx.fillStyle = '#000000';\n ctx.fillRect(0, 0, naturalWidth, naturalHeight);\n\n const useCrop = options?.useCrop ?? true;\n const useAnnotations = options?.useAnnotations ?? true;\n\n // Crop box mask (if active)\n if (useCrop && state.crop) {\n ctx.fillStyle = '#ffffff';\n ctx.fillRect(state.crop.x, state.crop.y, state.crop.width, state.crop.height);\n }\n\n // Annotation masks (drawn in solid white)\n if (useAnnotations && state.annotations.length > 0) {\n for (const ann of state.annotations) {\n ctx.save();\n if (ann.rotation) {\n ctx.translate(ann.x, ann.y);\n ctx.rotate((ann.rotation * Math.PI) / 180);\n ctx.translate(-ann.x, -ann.y);\n }\n\n if (ann.type === 'pen') {\n const pen = ann as PenAnnotation;\n if (pen.points.length > 0) {\n ctx.beginPath();\n ctx.strokeStyle = '#ffffff';\n ctx.lineWidth = Math.max(pen.strokeWidth, 8);\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.moveTo(pen.points[0].x, pen.points[0].y);\n for (let i = 1; i < pen.points.length; i++) {\n ctx.lineTo(pen.points[i].x, pen.points[i].y);\n }\n ctx.stroke();\n }\n } else if (ann.type === 'rect') {\n const rect = ann as RectAnnotation;\n ctx.fillStyle = '#ffffff';\n ctx.fillRect(rect.x, rect.y, rect.width, rect.height);\n } else if (ann.type === 'circle') {\n const circ = ann as CircleAnnotation;\n ctx.fillStyle = '#ffffff';\n ctx.beginPath();\n ctx.ellipse(circ.x, circ.y, circ.radiusX, circ.radiusY, 0, 0, Math.PI * 2);\n ctx.fill();\n } else {\n ctx.strokeStyle = '#ffffff';\n ctx.fillStyle = '#ffffff';\n await this.drawAnnotation(ctx, ann);\n }\n ctx.restore();\n }\n }\n\n return maskCanvas;\n }\n}\n\n","import {\n Annotation,\n CreateAnnotationPayload,\n AspectRatio,\n CropBox,\n DepthMaskOptions,\n ExportOptions,\n ImageAdjustments,\n ImageDimensions,\n ImageEditorOptions,\n ImageEditorState,\n ImageEditorTool,\n ImageTransform,\n ImagePoint,\n SerializedImageEditorState,\n} from './types';\nimport { ImageEditorRenderer, RenderOptions } from './renderer';\n\nexport const DEFAULT_ADJUSTMENTS: ImageAdjustments = {\n brightness: 0,\n contrast: 0,\n saturation: 0,\n exposure: 0,\n temperature: 0,\n blur: 0,\n opacity: 1,\n};\n\nexport const DEFAULT_TRANSFORM: ImageTransform = {\n rotate: 0,\n flipH: false,\n flipV: false,\n};\n\nexport const DEFAULT_DEPTH_MASK: DepthMaskOptions = {\n enabled: false,\n maskSource: null,\n mode: 'threshold',\n depthRange: [0, 255],\n softness: 10,\n invert: false,\n};\n\n/**\n * ImageEditorController manages the complete lifecycle, canvas rendering pipeline,\n * tool interaction states, undo/redo history, serialization, and image export.\n */\nexport class ImageEditorController {\n private state: ImageEditorState;\n private renderer: ImageEditorRenderer;\n private imageElement: CanvasImageSource | null = null;\n private listeners = new Set<(state: ImageEditorState) => void>();\n private onStateChange?: (state: ImageEditorState) => void;\n\n // History stacks\n private undoStack: SerializedImageEditorState[] = [];\n private redoStack: SerializedImageEditorState[] = [];\n private maxHistoryDepth: number;\n private isApplyingHistory = false;\n private initialSnapshot: SerializedImageEditorState;\n\n constructor(options: ImageEditorOptions = {}) {\n this.maxHistoryDepth = options.maxHistoryDepth ?? 50;\n this.onStateChange = options.onStateChange;\n this.renderer = new ImageEditorRenderer();\n\n const initialAdjustments: ImageAdjustments = {\n ...DEFAULT_ADJUSTMENTS,\n ...options.initialState?.adjustments,\n };\n\n const initialTransform: ImageTransform = {\n ...DEFAULT_TRANSFORM,\n ...options.initialState?.transform,\n };\n\n const initialDepthMask: DepthMaskOptions = {\n ...DEFAULT_DEPTH_MASK,\n ...options.initialState?.depthMask,\n };\n\n this.state = {\n sourceUrl: typeof options.image === 'string' ? options.image : null,\n imageDimensions: { width: 800, height: 600 },\n crop: options.initialState?.crop ?? null,\n transform: initialTransform,\n adjustments: initialAdjustments,\n depthMask: initialDepthMask,\n annotations: options.initialState?.annotations ?? [],\n selectedAnnotationId: null,\n activeTool: 'select',\n zoom: 1,\n pan: { x: 0, y: 0 },\n isOriginalCompared: false,\n };\n\n if (options.image instanceof Object && 'width' in options.image) {\n this.imageElement = options.image as CanvasImageSource;\n this.state.imageDimensions = {\n width: (options.image as any).naturalWidth || (options.image as any).width || 800,\n height: (options.image as any).naturalHeight || (options.image as any).height || 600,\n };\n }\n\n this.initialSnapshot = this.serializeState();\n\n if (typeof options.image === 'string') {\n this.loadImage(options.image);\n }\n }\n\n // --- Image Loading ---\n\n public async loadImage(src: string | HTMLImageElement): Promise<void> {\n if (typeof src === 'string') {\n this.state.sourceUrl = src;\n try {\n const img = await this.renderer.imageCache.get(src);\n this.imageElement = img;\n this.state.imageDimensions = {\n width: img.naturalWidth || img.width || 800,\n height: img.naturalHeight || img.height || 600,\n };\n } catch (err) {\n // In SSR / non-DOM environments, retain default dimensions\n }\n } else {\n this.imageElement = src;\n this.state.sourceUrl = (src as any).src || null;\n this.state.imageDimensions = {\n width: src.naturalWidth || src.width || 800,\n height: src.naturalHeight || src.height || 600,\n };\n }\n\n this.initialSnapshot = this.serializeState();\n this.notify();\n }\n\n public getImageElement(): CanvasImageSource | null {\n return this.imageElement;\n }\n\n // --- State & Subscription ---\n\n public getState(): ImageEditorState {\n return this.state;\n }\n\n public subscribe(listener: (state: ImageEditorState) => void): () => void {\n this.listeners.add(listener);\n listener(this.state);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private notify(): void {\n const currentState = { ...this.state };\n this.onStateChange?.(currentState);\n this.listeners.forEach((l) => l(currentState));\n }\n\n // --- History (Undo / Redo / Reset) ---\n\n private recordHistory(): void {\n if (this.isApplyingHistory) return;\n this.undoStack.push(this.serializeState());\n if (this.undoStack.length > this.maxHistoryDepth) {\n this.undoStack.shift();\n }\n this.redoStack = [];\n }\n\n public get canUndo(): boolean {\n return this.undoStack.length > 0;\n }\n\n public get canRedo(): boolean {\n return this.redoStack.length > 0;\n }\n\n public undo(): boolean {\n if (!this.canUndo) return false;\n this.isApplyingHistory = true;\n const current = this.serializeState();\n this.redoStack.push(current);\n\n const prev = this.undoStack.pop()!;\n this.applySerializedState(prev);\n this.isApplyingHistory = false;\n this.notify();\n return true;\n }\n\n public redo(): boolean {\n if (!this.canRedo) return false;\n this.isApplyingHistory = true;\n const current = this.serializeState();\n this.undoStack.push(current);\n\n const next = this.redoStack.pop()!;\n this.applySerializedState(next);\n this.isApplyingHistory = false;\n this.notify();\n return true;\n }\n\n public reset(): void {\n this.recordHistory();\n this.applySerializedState(this.initialSnapshot);\n this.state.zoom = 1;\n this.state.pan = { x: 0, y: 0 };\n this.state.activeTool = 'select';\n this.state.selectedAnnotationId = null;\n this.notify();\n }\n\n // --- Active Tool ---\n\n public setTool(tool: ImageEditorTool): void {\n if (this.state.activeTool === tool) return;\n this.state.activeTool = tool;\n if (tool !== 'select') {\n this.state.selectedAnnotationId = null;\n }\n // If switching to crop tool and no crop is defined, initialize crop to full image\n if (tool === 'crop' && !this.state.crop) {\n this.state.crop = {\n x: 0,\n y: 0,\n width: this.state.imageDimensions.width,\n height: this.state.imageDimensions.height,\n };\n }\n this.notify();\n }\n\n // --- Crop ---\n\n public setCrop(crop: CropBox | null, addToHistory = true): void {\n if (addToHistory) {\n this.recordHistory();\n }\n this.state.crop = crop;\n this.notify();\n }\n\n public applyCropPreset(ratio: AspectRatio): void {\n this.recordHistory();\n const { width, height } = this.state.imageDimensions;\n if (!ratio) {\n // Freeform: full image\n this.state.crop = { x: 0, y: 0, width, height };\n } else {\n let targetW = width;\n let targetH = targetW / ratio;\n\n if (targetH > height) {\n targetH = height;\n targetW = targetH * ratio;\n }\n\n const x = Math.max(0, Math.round((width - targetW) / 2));\n const y = Math.max(0, Math.round((height - targetH) / 2));\n this.state.crop = {\n x,\n y,\n width: Math.round(targetW),\n height: Math.round(targetH),\n };\n }\n this.notify();\n }\n\n public resetCrop(): void {\n this.recordHistory();\n this.state.crop = null;\n this.notify();\n }\n\n // --- Transformations (Rotate / Flip) ---\n\n public rotate(degrees: number): void {\n this.recordHistory();\n const current = this.state.transform.rotate;\n this.state.transform.rotate = (current + degrees) % 360;\n this.notify();\n }\n\n public setRotation(degrees: number): void {\n this.recordHistory();\n this.state.transform.rotate = degrees % 360;\n this.notify();\n }\n\n public flipHorizontal(): void {\n this.recordHistory();\n this.state.transform.flipH = !this.state.transform.flipH;\n this.notify();\n }\n\n public flipVertical(): void {\n this.recordHistory();\n this.state.transform.flipV = !this.state.transform.flipV;\n this.notify();\n }\n\n // --- Adjustments ---\n\n public setAdjustments(partial: Partial<ImageAdjustments>, commitToHistory = true): void {\n if (commitToHistory) {\n this.recordHistory();\n }\n this.state.adjustments = {\n ...this.state.adjustments,\n ...partial,\n };\n this.notify();\n }\n\n public resetAdjustments(): void {\n this.recordHistory();\n this.state.adjustments = { ...DEFAULT_ADJUSTMENTS };\n this.notify();\n }\n\n // --- Depth Masking (Gleamforge) ---\n\n public setDepthMask(partial: Partial<DepthMaskOptions>): void {\n this.recordHistory();\n this.state.depthMask = {\n ...this.state.depthMask,\n ...partial,\n };\n this.notify();\n }\n\n public resetDepthMask(): void {\n this.recordHistory();\n this.state.depthMask = { ...DEFAULT_DEPTH_MASK };\n this.notify();\n }\n\n // --- Annotations & Layers ---\n\n public addAnnotation(annotation: CreateAnnotationPayload): string {\n this.recordHistory();\n const id = `ann_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\n const fullAnnotation = { ...annotation, id } as Annotation;\n this.state.annotations = [...this.state.annotations, fullAnnotation];\n this.state.selectedAnnotationId = id;\n this.notify();\n return id;\n }\n\n public updateAnnotation(id: string, partial: Partial<Annotation>, commitToHistory = true): void {\n if (commitToHistory) {\n this.recordHistory();\n }\n this.state.annotations = this.state.annotations.map((ann) => {\n if (ann.id === id) {\n return { ...ann, ...partial } as Annotation;\n }\n return ann;\n });\n this.notify();\n }\n\n public removeAnnotation(id: string): void {\n this.recordHistory();\n this.state.annotations = this.state.annotations.filter((ann) => ann.id !== id);\n if (this.state.selectedAnnotationId === id) {\n this.state.selectedAnnotationId = null;\n }\n this.notify();\n }\n\n public clearAnnotations(): void {\n this.recordHistory();\n this.state.annotations = [];\n this.state.selectedAnnotationId = null;\n this.notify();\n }\n\n public selectAnnotation(id: string | null): void {\n this.state.selectedAnnotationId = id;\n this.notify();\n }\n\n // --- Zoom & Pan ---\n\n public setZoom(zoom: number): void {\n this.state.zoom = Math.max(0.1, Math.min(10, zoom));\n this.notify();\n }\n\n public zoomIn(factor = 1.2): void {\n this.setZoom(this.state.zoom * factor);\n }\n\n public zoomOut(factor = 1.2): void {\n this.setZoom(this.state.zoom / factor);\n }\n\n public resetZoom(): void {\n this.state.zoom = 1;\n this.state.pan = { x: 0, y: 0 };\n this.notify();\n }\n\n public setPan(pan: ImagePoint): void {\n this.state.pan = pan;\n this.notify();\n }\n\n public setComparing(comparing: boolean): void {\n if (this.state.isOriginalCompared === comparing) return;\n this.state.isOriginalCompared = comparing;\n this.notify();\n }\n\n // --- Rendering & Export ---\n\n public async render(targetCanvas: HTMLCanvasElement, options?: RenderOptions): Promise<void> {\n const source = this.imageElement || this.state.sourceUrl;\n if (!source) return;\n try {\n await this.renderer.renderToCanvas(targetCanvas, source, this.state, options);\n } catch {\n // In non-DOM or test mock environments without image loading, safely ignore\n }\n }\n\n public async renderExport(options?: ExportOptions): Promise<HTMLCanvasElement> {\n const source = this.imageElement || this.state.sourceUrl;\n if (!source) {\n return this.renderer.createCanvas(1, 1);\n }\n return this.renderer.renderExport(source, this.state, options);\n }\n\n public async toDataURL(options: ExportOptions = {}): Promise<string> {\n const canvas = await this.renderExport(options);\n const format = options.format || 'image/png';\n const quality = options.quality ?? 0.92;\n return canvas.toDataURL(format, quality);\n }\n\n public async toBlob(options: ExportOptions = {}): Promise<Blob> {\n const canvas = await this.renderExport(options);\n const format = options.format || 'image/png';\n const quality = options.quality ?? 0.92;\n\n return new Promise((resolve, reject) => {\n if (canvas.toBlob) {\n canvas.toBlob((blob) => {\n if (blob) resolve(blob);\n else reject(new Error('Failed to create Blob from canvas'));\n }, format, quality);\n } else {\n // Fallback for environments lacking canvas.toBlob\n try {\n const dataUrl = canvas.toDataURL(format, quality);\n const parts = dataUrl.split(',');\n const byteStr = atob(parts[1]);\n const ab = new ArrayBuffer(byteStr.length);\n const ia = new Uint8Array(ab);\n for (let i = 0; i < byteStr.length; i++) {\n ia[i] = byteStr.charCodeAt(i);\n }\n resolve(new Blob([ab], { type: format }));\n } catch (err) {\n reject(err);\n }\n }\n });\n }\n\n // --- AI Masking & Transformation Helpers ---\n\n public async renderMask(options?: { useAnnotations?: boolean; useCrop?: boolean }): Promise<HTMLCanvasElement> {\n return this.renderer.renderMask(this.state, options);\n }\n\n public async toMaskDataURL(options?: { useAnnotations?: boolean; useCrop?: boolean }): Promise<string> {\n const canvas = await this.renderMask(options);\n return canvas.toDataURL('image/png');\n }\n\n public async applyInpaintedImage(newDataUrl: string): Promise<void> {\n this.recordHistory();\n await this.loadImage(newDataUrl);\n this.clearAnnotations();\n }\n\n public async applyBackgroundRemovedImage(newDataUrl: string): Promise<void> {\n this.recordHistory();\n await this.loadImage(newDataUrl);\n }\n\n\n // --- Serialization ---\n\n public toJSON(): SerializedImageEditorState {\n return this.serializeState();\n }\n\n public loadJSON(data: SerializedImageEditorState): void {\n this.recordHistory();\n this.applySerializedState(data);\n this.notify();\n }\n\n private serializeState(): SerializedImageEditorState {\n return {\n version: 1,\n crop: this.state.crop ? { ...this.state.crop } : null,\n transform: { ...this.state.transform },\n adjustments: { ...this.state.adjustments },\n depthMask: {\n ...this.state.depthMask,\n depthRange: [...this.state.depthMask.depthRange] as [number, number],\n },\n annotations: JSON.parse(JSON.stringify(this.state.annotations)),\n };\n }\n\n private applySerializedState(data: SerializedImageEditorState): void {\n this.state.crop = data.crop ? { ...data.crop } : null;\n this.state.transform = { ...data.transform };\n this.state.adjustments = { ...data.adjustments };\n this.state.depthMask = {\n ...data.depthMask,\n depthRange: [...data.depthMask.depthRange] as [number, number],\n };\n this.state.annotations = JSON.parse(JSON.stringify(data.annotations || []));\n }\n}\n","import type { CropBox, ImageEditorState, ImagePoint } from './types';\n\nexport type CropHandle = 'nw' | 'ne' | 'se' | 'sw' | 'n' | 's' | 'e' | 'w' | 'inside' | null;\n\nexport interface ViewportMetrics {\n vpWidth: number;\n vpHeight: number;\n centerX: number;\n centerY: number;\n baseW: number;\n baseH: number;\n fitScale: number;\n renderW: number;\n renderH: number;\n totalScale: number;\n imageRect: { x: number; y: number; width: number; height: number };\n cropRect: { x: number; y: number; width: number; height: number } | null;\n}\n\n/**\n * Calculates current viewport projection metrics for canvas <-> image coordinate mapping.\n */\nexport function getViewportMetrics(\n state: ImageEditorState,\n vpWidth: number,\n vpHeight: number,\n isActivelyCropping = false,\n cropOverride?: CropBox | null\n): ViewportMetrics {\n const { imageDimensions, crop: stateCrop, transform, zoom, pan } = state;\n const naturalWidth = imageDimensions.width || 800;\n const naturalHeight = imageDimensions.height || 600;\n const activeCrop =\n (cropOverride !== undefined && cropOverride !== null)\n ? cropOverride\n : (stateCrop ?? (isActivelyCropping ? { x: 0, y: 0, width: naturalWidth, height: naturalHeight } : null));\n\n const cropW = (!isActivelyCropping && activeCrop) ? activeCrop.width : naturalWidth;\n const cropH = (!isActivelyCropping && activeCrop) ? activeCrop.height : naturalHeight;\n\n const angle = ((transform.rotate % 360) + 360) % 360;\n const isSwapped = angle === 90 || angle === 270;\n const baseW = isSwapped ? cropH : cropW;\n const baseH = isSwapped ? cropW : cropH;\n\n const fitScale = Math.min((vpWidth * 0.85) / baseW, (vpHeight * 0.85) / baseH, 1);\n const renderW = baseW * fitScale;\n const renderH = baseH * fitScale;\n const totalScale = zoom * fitScale;\n\n const centerX = vpWidth / 2 + pan.x;\n const centerY = vpHeight / 2 + pan.y;\n\n const imageRect = {\n x: centerX - (renderW * zoom) / 2,\n y: centerY - (renderH * zoom) / 2,\n width: renderW * zoom,\n height: renderH * zoom,\n };\n\n let cropRect: { x: number; y: number; width: number; height: number } | null = null;\n if (activeCrop) {\n const startX = -renderW / 2 + (activeCrop.x / naturalWidth) * renderW;\n const startY = -renderH / 2 + (activeCrop.y / naturalHeight) * renderH;\n const cropBoxW = (activeCrop.width / naturalWidth) * renderW;\n const cropBoxH = (activeCrop.height / naturalHeight) * renderH;\n\n cropRect = {\n x: centerX + zoom * startX,\n y: centerY + zoom * startY,\n width: zoom * cropBoxW,\n height: zoom * cropBoxH,\n };\n }\n\n return {\n vpWidth,\n vpHeight,\n centerX,\n centerY,\n baseW,\n baseH,\n fitScale,\n renderW,\n renderH,\n totalScale,\n imageRect,\n cropRect,\n };\n}\n\n/**\n * Converts a point on the canvas (in internal canvas pixel space) to image/annotation pixel space.\n */\nexport function canvasToImagePoint(\n canvasPt: ImagePoint,\n state: ImageEditorState,\n vpWidth: number,\n vpHeight: number,\n isActivelyCropping = false\n): ImagePoint {\n const metrics = getViewportMetrics(state, vpWidth, vpHeight, isActivelyCropping);\n const unzoomedX = (canvasPt.x - metrics.centerX) / state.zoom;\n const unzoomedY = (canvasPt.y - metrics.centerY) / state.zoom;\n\n const x = (unzoomedX + metrics.renderW / 2) / metrics.fitScale;\n const y = (unzoomedY + metrics.renderH / 2) / metrics.fitScale;\n\n return { x: Math.round(x), y: Math.round(y) };\n}\n\n/**\n * Converts a point in image/annotation pixel space to internal canvas pixel space.\n */\nexport function imageToCanvasPoint(\n imgPt: ImagePoint,\n state: ImageEditorState,\n vpWidth: number,\n vpHeight: number,\n isActivelyCropping = false\n): ImagePoint {\n const metrics = getViewportMetrics(state, vpWidth, vpHeight, isActivelyCropping);\n const unzoomedX = (imgPt.x - metrics.baseW / 2) * metrics.fitScale;\n const unzoomedY = (imgPt.y - metrics.baseH / 2) * metrics.fitScale;\n\n const canvasX = metrics.centerX + unzoomedX * state.zoom;\n const canvasY = metrics.centerY + unzoomedY * state.zoom;\n\n return { x: Math.round(canvasX), y: Math.round(canvasY) };\n}\n\n/**\n * Hit-tests the crop box and its corner/edge handles given a canvas point.\n */\nexport function hitTestCrop(\n canvasPt: ImagePoint,\n state: ImageEditorState,\n vpWidth: number,\n vpHeight: number,\n handleThreshold = 14,\n cropOverride?: CropBox | null\n): CropHandle {\n const metrics = getViewportMetrics(state, vpWidth, vpHeight, true, cropOverride);\n if (!metrics.cropRect) return null;\n\n const { x, y, width: w, height: h } = metrics.cropRect;\n const px = canvasPt.x;\n const py = canvasPt.y;\n\n // 1. Corners (highest priority)\n const dist = (x1: number, y1: number, x2: number, y2: number) =>\n Math.hypot(x1 - x2, y1 - y2);\n\n if (dist(px, py, x, y) <= handleThreshold) return 'nw';\n if (dist(px, py, x + w, y) <= handleThreshold) return 'ne';\n if (dist(px, py, x + w, y + h) <= handleThreshold) return 'se';\n if (dist(px, py, x, y + h) <= handleThreshold) return 'sw';\n\n // 2. Edges\n const nearX = px >= x - handleThreshold && px <= x + w + handleThreshold;\n const nearY = py >= y - handleThreshold && py <= y + h + handleThreshold;\n\n if (nearX && Math.abs(py - y) <= handleThreshold) return 'n';\n if (nearX && Math.abs(py - (y + h)) <= handleThreshold) return 's';\n if (nearY && Math.abs(px - x) <= handleThreshold) return 'w';\n if (nearY && Math.abs(px - (x + w)) <= handleThreshold) return 'e';\n\n // 3. Inside crop region\n if (px >= x && px <= x + w && py >= y && py <= y + h) {\n return 'inside';\n }\n\n return null;\n}\n\n/**\n * Returns CSS cursor style corresponding to a crop handle.\n */\nexport function getCropCursor(handle: CropHandle): string {\n switch (handle) {\n case 'nw':\n case 'se':\n return 'nwse-resize';\n case 'ne':\n case 'sw':\n return 'nesw-resize';\n case 'n':\n case 's':\n return 'ns-resize';\n case 'e':\n case 'w':\n return 'ew-resize';\n case 'inside':\n return 'move';\n default:\n return 'crosshair';\n }\n}\n\n/**\n * Calculates updated crop rectangle given a drag interaction.\n */\nexport function calculateCropDrag(\n startCrop: CropBox,\n handle: CropHandle,\n startImgPt: ImagePoint,\n currentImgPt: ImagePoint,\n imgWidth: number,\n imgHeight: number,\n aspectRatio?: number | null\n): CropBox {\n const dx = currentImgPt.x - startImgPt.x;\n const dy = currentImgPt.y - startImgPt.y;\n\n // Whole crop box move\n if (handle === 'inside') {\n const maxX = Math.max(0, imgWidth - startCrop.width);\n const maxY = Math.max(0, imgHeight - startCrop.height);\n const newX = Math.max(0, Math.min(maxX, startCrop.x + dx));\n const newY = Math.max(0, Math.min(maxY, startCrop.y + dy));\n return {\n x: Math.round(newX),\n y: Math.round(newY),\n width: startCrop.width,\n height: startCrop.height,\n };\n }\n\n // Handle resizing\n let left = startCrop.x;\n let top = startCrop.y;\n let right = startCrop.x + startCrop.width;\n let bottom = startCrop.y + startCrop.height;\n\n const minSize = 20;\n\n if (handle === 'e' || handle === 'ne' || handle === 'se') {\n right = Math.min(imgWidth, Math.max(left + minSize, startCrop.x + startCrop.width + dx));\n }\n if (handle === 'w' || handle === 'nw' || handle === 'sw') {\n left = Math.max(0, Math.min(right - minSize, startCrop.x + dx));\n }\n if (handle === 's' || handle === 'se' || handle === 'sw') {\n bottom = Math.min(imgHeight, Math.max(top + minSize, startCrop.y + startCrop.height + dy));\n }\n if (handle === 'n' || handle === 'nw' || handle === 'ne') {\n top = Math.max(0, Math.min(bottom - minSize, startCrop.y + dy));\n }\n\n // Maintain aspect ratio if requested\n if (aspectRatio && aspectRatio > 0) {\n let currentW = right - left;\n let currentH = bottom - top;\n\n if (handle === 'e' || handle === 'w') {\n currentH = Math.min(imgHeight, currentW / aspectRatio);\n bottom = Math.min(imgHeight, top + currentH);\n } else if (handle === 'n' || handle === 's') {\n currentW = Math.min(imgWidth, currentH * aspectRatio);\n right = Math.min(imgWidth, left + currentW);\n } else {\n // Corner resizing\n const targetH = currentW / aspectRatio;\n if (top + targetH <= imgHeight) {\n bottom = top + targetH;\n } else {\n const targetW = currentH * aspectRatio;\n right = Math.min(imgWidth, left + targetW);\n }\n }\n }\n\n return {\n x: Math.round(left),\n y: Math.round(top),\n width: Math.round(Math.max(minSize, right - left)),\n height: Math.round(Math.max(minSize, bottom - top)),\n };\n}\n\n/**\n * Calculates bounding box of an annotation.\n */\nexport function getAnnotationBounds(ann: import('./types').Annotation): { x: number; y: number; width: number; height: number } {\n switch (ann.type) {\n case 'rect':\n return { x: ann.x, y: ann.y, width: ann.width, height: ann.height };\n case 'circle':\n return {\n x: ann.x - ann.radiusX,\n y: ann.y - ann.radiusY,\n width: ann.radiusX * 2,\n height: ann.radiusY * 2,\n };\n case 'arrow':\n case 'line': {\n const minX = Math.min(ann.x, ann.endX);\n const minY = Math.min(ann.y, ann.endY);\n return {\n x: minX,\n y: minY,\n width: Math.max(Math.abs(ann.endX - ann.x), 10),\n height: Math.max(Math.abs(ann.endY - ann.y), 10),\n };\n }\n case 'pen': {\n if (!ann.points.length) return { x: ann.x, y: ann.y, width: 0, height: 0 };\n let minX = ann.points[0].x;\n let maxX = ann.points[0].x;\n let minY = ann.points[0].y;\n let maxY = ann.points[0].y;\n for (const pt of ann.points) {\n if (pt.x < minX) minX = pt.x;\n if (pt.x > maxX) maxX = pt.x;\n if (pt.y < minY) minY = pt.y;\n if (pt.y > maxY) maxY = pt.y;\n }\n return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };\n }\n case 'text':\n return {\n x: ann.x,\n y: ann.y,\n width: ann.text.length * ann.fontSize * 0.6,\n height: ann.fontSize * 1.2,\n };\n case 'image':\n return { x: ann.x, y: ann.y, width: ann.width, height: ann.height };\n }\n}\n\n/**\n * Hit tests if a point in image space is within an annotation's bounds.\n */\nexport function hitTestAnnotation(imgPt: ImagePoint, ann: import('./types').Annotation, padding = 10): boolean {\n const b = getAnnotationBounds(ann);\n return (\n imgPt.x >= b.x - padding &&\n imgPt.x <= b.x + b.width + padding &&\n imgPt.y >= b.y - padding &&\n imgPt.y <= b.y + b.height + padding\n );\n}\n\n"]}