@codingfactory/mediables-vue 2.19.2 → 2.20.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 (33) hide show
  1. package/dist/{PixiFrameExporter-CRe8z6ua.js → PixiFrameExporter-CGQJH9OE.js} +2 -2
  2. package/dist/{PixiFrameExporter-CRe8z6ua.js.map → PixiFrameExporter-CGQJH9OE.js.map} +1 -1
  3. package/dist/{PixiFrameExporter-R6iQjzVw.cjs → PixiFrameExporter-CfUbYDmD.cjs} +2 -2
  4. package/dist/{PixiFrameExporter-R6iQjzVw.cjs.map → PixiFrameExporter-CfUbYDmD.cjs.map} +1 -1
  5. package/dist/editor-v2-CFLWp7RZ.cjs +2 -0
  6. package/dist/editor-v2-CFLWp7RZ.cjs.map +1 -0
  7. package/dist/editor-v2-DjhJBaCS.js +7187 -0
  8. package/dist/editor-v2-DjhJBaCS.js.map +1 -0
  9. package/dist/filters/index.d.ts +1 -0
  10. package/dist/filters/recipeToCssFilter.d.ts +71 -0
  11. package/dist/index-Dae8SHT7.js.map +1 -1
  12. package/dist/index-QOKC8XA_.cjs.map +1 -1
  13. package/dist/index-ST4ukv22.cjs +357 -0
  14. package/dist/index-ST4ukv22.cjs.map +1 -0
  15. package/dist/index-rw6mdaKW.js +38906 -0
  16. package/dist/index-rw6mdaKW.js.map +1 -0
  17. package/dist/index.d.ts +3 -2
  18. package/dist/mediables-vanilla.cjs +1 -1
  19. package/dist/mediables-vanilla.mjs +10 -9
  20. package/dist/mediables-vue.cjs +1 -1
  21. package/dist/mediables-vue.mjs +97 -87
  22. package/dist/style.css +1 -1
  23. package/dist/utils/videoRecipeCapabilities.d.ts +15 -0
  24. package/dist/vanilla-exports.d.ts +2 -1
  25. package/package.json +1 -1
  26. package/dist/editor-BQ_nY4P6.js +0 -4302
  27. package/dist/editor-BQ_nY4P6.js.map +0 -1
  28. package/dist/editor-CUV1pIsV.cjs +0 -2
  29. package/dist/editor-CUV1pIsV.cjs.map +0 -1
  30. package/dist/index-B8LxZ37n.js +0 -41633
  31. package/dist/index-B8LxZ37n.js.map +0 -1
  32. package/dist/index-DutUeSES.cjs +0 -357
  33. package/dist/index-DutUeSES.cjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"editor-CUV1pIsV.cjs","sources":["../resources/js/vanilla-editor/renderer/PixiRenderer.js","../resources/js/vanilla-editor/icons/icons.js","../resources/js/vanilla-editor/filters/categories.js","../resources/js/vanilla-editor/renderer/FilterManager.js","../resources/js/vanilla-editor/renderer/CropManager.js","../resources/js/vanilla-editor/renderer/RemoveBgManager.js","../resources/js/vanilla-editor/ui/UIBuilder.js","../resources/js/vanilla-editor/ui/Toolbar.js","../resources/js/vanilla-editor/ui/CategoryCarousel.js","../resources/js/vanilla-editor/ui/FilterCarousel.js","../resources/js/vanilla-editor/ui/FilterAdjustments.js","../resources/js/vanilla-editor/ui/MobileFilterDrawer.js","../resources/js/vanilla-editor/ui/MobileActiveFilters.js","../resources/js/vanilla-editor/ui/CropControls.js","../resources/js/vanilla-editor/ui/ActiveFiltersPanel.js","../resources/js/vanilla-editor/presets/index.js","../resources/js/vanilla-editor/VanillaImageEditor.js"],"sourcesContent":["/**\n * PIXI.js v8 Renderer for the vanilla image editor\n * Handles canvas lifecycle, image loading, zoom, and export\n */\nimport { EventEmitter } from '../core/EventEmitter.js'\n\nconst TEXT_BLEND_MODES = Object.freeze(['normal', 'multiply', 'screen', 'overlay'])\nconst DEFAULT_TEXT_EFFECTS = Object.freeze({\n stroke: Object.freeze({ enabled: false, color: '#000000', width: 0 }),\n shadow: Object.freeze({ enabled: false, color: '#000000', alpha: 0.5, blur: 0, distance: 0, angle: 45 }),\n glow: Object.freeze({ enabled: false, color: '#ffffff', alpha: 0.5, blur: 0 }),\n backdrop: Object.freeze({ enabled: false, color: '#000000', opacity: 0.5, padding: 0, radius: 0, blur: 0 }),\n})\n\nexport class PixiRenderer extends EventEmitter {\n constructor() {\n super()\n this.app = null\n this.sprite = null\n this.originalTexture = null\n this.baseTexture = null\n this.fitScale = 1\n this.zoom = 1\n this._container = null\n this._lastExportDimensions = null\n /** @type {Promise<boolean>|null} Resolves when mount() finishes */\n this._mountPromise = null\n this._mountToken = 0\n this._destroyed = false\n this._layerDocument = null\n this._layerDisplayObjects = new Map()\n this._renderableLayerIds = []\n this._loadedTextureUrl = null\n this._layerRenderToken = 0\n this._layerFilterFactory = null\n }\n\n /**\n * Whether the renderer is fully initialized and ready for use.\n * @returns {boolean}\n */\n get isReady() {\n return !!(this.app?.renderer)\n }\n\n /**\n * Get CSS size of the canvas (for proper coordinate mapping).\n * Accesses renderer.canvas directly to avoid PIXI Application's\n * canvas getter which throws when renderer is undefined.\n */\n _getCssSize() {\n const canvas = this.app?.renderer?.canvas\n const w = canvas?.clientWidth ?? this.app?.screen?.width ?? 0\n const h = canvas?.clientHeight ?? this.app?.screen?.height ?? 0\n return { w, h }\n }\n\n /**\n * Force render the stage\n */\n render() {\n try {\n if (this.app?.renderer && this.app?.stage) {\n this.app.renderer.render(this.app.stage)\n }\n } catch (e) {\n console.warn('[PixiRenderer] render error:', e)\n }\n }\n\n /**\n * Wait for the next animation frame, with a timeout fallback for environments\n * where the frame callback never arrives while the editor is open.\n * @param {number} timeoutMs\n * @returns {Promise<void>}\n */\n _waitForNextFrame(timeoutMs = 32) {\n return new Promise((resolve) => {\n if (typeof window === 'undefined') {\n resolve()\n return\n }\n\n let settled = false\n let timeoutId = 0\n\n const finish = () => {\n if (settled) return\n settled = true\n window.clearTimeout(timeoutId)\n resolve()\n }\n\n timeoutId = window.setTimeout(finish, timeoutMs)\n\n if (typeof window.requestAnimationFrame === 'function') {\n window.requestAnimationFrame(() => finish())\n return\n }\n\n finish()\n })\n }\n\n /**\n * Calculate fit scale for a texture\n * @param {PIXI.Texture} tex\n * @returns {number}\n */\n getFitScaleFor(tex) {\n if (!this.app || !tex) return 1\n const { w, h } = this._getCssSize()\n if (w <= 1 || h <= 1) return 1\n let scale = Math.min(w / tex.width, h / tex.height) * 0.9\n if (!Number.isFinite(scale) || scale <= 0) scale = 1\n return scale\n }\n\n /**\n * Apply view transform (position and scale) to sprite\n */\n applyViewTransform(opts = {}) {\n if (!this.app || !this.sprite || !this.originalTexture) return\n\n const s = this.fitScale * this.zoom\n const { w, h } = this._getCssSize()\n\n const desired = opts.keepCenter\n ? { x: this.sprite.x + this.sprite.width / 2, y: this.sprite.y + this.sprite.height / 2 }\n : (opts.center ?? { x: w / 2, y: h / 2 })\n\n this.sprite.width = this.originalTexture.width * s\n this.sprite.height = this.originalTexture.height * s\n this.sprite.x = desired.x - this.sprite.width / 2\n this.sprite.y = desired.y - this.sprite.height / 2\n\n this._syncLayerDisplayObjects()\n\n this.render()\n }\n\n /**\n * Set zoom level\n * @param {number} z - Zoom level (0.1 to 8)\n * @param {Object} opts - Options\n */\n setZoom(z, opts = {}) {\n this.zoom = Math.max(0.1, Math.min(8, z))\n this.applyViewTransform({ keepCenter: opts.keepCenter ?? true })\n this.emit('zoomChange', this.zoom)\n }\n\n /**\n * Fit image to screen\n */\n fitToScreen() {\n if (this.originalTexture) {\n this.fitScale = this.getFitScaleFor(this.originalTexture)\n this.setZoom(1, { keepCenter: false })\n }\n }\n\n /**\n * Mount PIXI application to container\n * @param {HTMLElement} container\n * @param {number} backgroundColor\n * @param {Object} [options]\n * @param {number} [options.backgroundAlpha=1] - 0 makes the canvas transparent\n * so a host-rendered background (e.g. CSS checkerboard) shows through.\n */\n async mount(container, backgroundColor = 0xffffff, options = {}) {\n if (!container) return\n this._destroyed = false\n this._container = container\n const mountToken = ++this._mountToken\n\n const doMount = async () => {\n const PIXI = window.PIXI\n if (!PIXI) {\n throw new Error('PIXI.js not found. Please ensure PIXI is loaded globally.')\n }\n\n // Enable CORS for cross-origin image loading (e.g. S3 presigned URLs)\n if (PIXI.Assets?.setPreferences) {\n PIXI.Assets.setPreferences({ crossOrigin: 'anonymous' })\n }\n\n const width = container.clientWidth || 600\n const height = container.clientHeight || 400\n\n const backgroundAlpha = typeof options.backgroundAlpha === 'number'\n ? options.backgroundAlpha\n : 1\n\n const app = new PIXI.Application()\n this.app = app\n await app.init({\n width,\n height,\n backgroundColor,\n backgroundAlpha,\n antialias: true,\n autoDensity: true,\n resolution: window.devicePixelRatio || 1\n })\n\n if (this._destroyed || this._mountToken !== mountToken || this.app !== app) {\n try {\n app.destroy(true, { children: true, texture: true })\n } catch (_e) {\n // Ignore destroy races; the active renderer has already moved on.\n }\n return false\n }\n\n container.appendChild(app.canvas)\n\n // Style the canvas\n const canvas = app.canvas\n canvas.classList.add('pixi-canvas')\n canvas.style.width = '100%'\n canvas.style.height = '100%'\n canvas.style.transform = 'translateZ(0)'\n canvas.style.willChange = 'transform'\n\n // Accessibility: label the canvas for screen readers\n canvas.setAttribute('role', 'img')\n canvas.setAttribute('aria-label', 'Image editor canvas — use toolbar controls to edit the image')\n\n this.emit('mounted', { width, height })\n return true\n }\n\n this._mountPromise = doMount()\n return await this._mountPromise\n }\n\n /**\n * Load an image texture\n * @param {string} imageUrl - URL, data URL, or blob URL of the image\n * @param {Object} [options]\n * @param {() => boolean} [options.isCurrent] - Returns false when this load\n * has been superseded and must not mutate the renderer.\n */\n async loadTexture(imageUrl, options = {}) {\n const isCurrent = () => typeof options.isCurrent !== 'function' || options.isCurrent() !== false\n\n // Wait for mount() to finish if it's still in progress\n if (this._mountPromise) {\n await this._mountPromise\n }\n\n if (!this.app?.renderer || !isCurrent()) return false\n\n const PIXI = window.PIXI\n\n // Load image via HTMLImageElement then create texture directly.\n // Bypasses PIXI.Assets.load() which can't handle blob: or data: URLs.\n const img = await this._loadImageElement(imageUrl)\n if (!img) {\n console.error('[PixiRenderer.loadTexture] Failed to load image from', imageUrl)\n return false\n }\n\n // Re-check after async — renderer may have been destroyed\n if (!this.app?.renderer || !isCurrent()) return false\n\n // Wait for layout to settle before mutating the renderer. Fall back to a\n // short timeout when the environment never delivers the frame callback.\n await this._waitForNextFrame()\n\n // Re-check after async — this load may have been superseded during frame settlement.\n if (!this.app?.renderer || !isCurrent()) return false\n\n const tex = PIXI.Texture.from(img)\n\n if (!tex) {\n console.error('[PixiRenderer.loadTexture] Failed to create texture from image')\n return false\n }\n\n this.originalTexture = tex\n this.baseTexture = tex\n this._loadedTextureUrl = typeof imageUrl === 'string' ? imageUrl : null\n\n // Remove old sprite\n if (this.sprite) {\n this.app.stage.removeChild(this.sprite)\n this.sprite.destroy()\n }\n\n // Create new sprite\n this.sprite = new PIXI.Sprite(tex)\n this.app.stage.addChild(this.sprite)\n\n // Calculate fit scale and apply\n this.fitScale = this.getFitScaleFor(tex)\n this.zoom = 1\n\n const { w, h } = this._getCssSize()\n this.applyViewTransform({ center: { x: w / 2, y: h / 2 } })\n\n const settledScale = this.getFitScaleFor(tex)\n if (Math.abs(settledScale - this.fitScale) / Math.max(1e-6, settledScale) > 0.02) {\n this.fitScale = settledScale\n this.applyViewTransform({ center: { x: w / 2, y: h / 2 } })\n }\n\n this.render()\n this.emit('textureLoaded', { width: tex.width, height: tex.height })\n return true\n }\n\n /**\n * Render a v2 layer document in preview order. The existing subject sprite\n * remains the filter/crop target; auxiliary layers are synced to its preview\n * geometry until arbitrary per-layer transforms are introduced.\n * @param {Object|null} document\n * @returns {Promise<boolean>}\n */\n async renderLayerDocument(document) {\n if (!this.app?.stage || !this.sprite || !document || !Array.isArray(document.layers)) {\n return false\n }\n\n const PIXI = window.PIXI\n if (!PIXI) return false\n\n const renderToken = ++this._layerRenderToken\n const isCurrentRender = () => renderToken === this._layerRenderToken\n this._destroyLayerDisplayObjects()\n this._layerDocument = JSON.parse(JSON.stringify(document))\n this._renderableLayerIds = []\n\n try {\n this.app.stage.removeChild(this.sprite)\n } catch (_e) {\n // The sprite may not have been attached yet in tests or during reloads.\n }\n\n let subjectAdded = false\n\n for (const layer of this._layerDocument.layers) {\n if (!layer || layer.visible === false) {\n if (layer?.role === 'subject') {\n this.sprite.visible = false\n this.sprite.__layerId = layer.id\n subjectAdded = true\n }\n continue\n }\n\n if (this._isSubjectLayer(layer)) {\n const displayObject = await this._createSubjectDisplayObjectForLayer(PIXI, layer)\n if (!isCurrentRender()) {\n this._destroyTransientDisplayObject(displayObject)\n return false\n }\n if (displayObject) {\n this.sprite.visible = false\n displayObject.__layerId = layer.id\n displayObject.visible = true\n displayObject.alpha = this._layerOpacity(layer)\n this._layerDisplayObjects.set(layer.id, { layer, displayObject })\n this._syncLayerDisplayObject(layer, displayObject)\n this.app.stage.addChild(displayObject)\n this._renderableLayerIds.push(layer.id)\n subjectAdded = true\n continue\n }\n\n this.sprite.__layerId = layer.id\n this.sprite.visible = true\n this.sprite.alpha = this._layerOpacity(layer)\n this._applyLayerFilterInstances(this.sprite, layer)\n this.app.stage.addChild(this.sprite)\n this._renderableLayerIds.push(layer.id)\n subjectAdded = true\n continue\n }\n\n const displayObject = await this._createDisplayObjectForLayer(PIXI, layer)\n if (!isCurrentRender()) {\n this._destroyTransientDisplayObject(displayObject)\n return false\n }\n if (!displayObject) continue\n\n displayObject.__layerId = layer.id\n displayObject.visible = true\n displayObject.alpha = this._layerOpacity(layer)\n this._layerDisplayObjects.set(layer.id, { layer, displayObject })\n this._syncLayerDisplayObject(layer, displayObject)\n this.app.stage.addChild(displayObject)\n this._renderableLayerIds.push(layer.id)\n }\n\n if (!subjectAdded) {\n this.sprite.visible = false\n }\n\n if (!isCurrentRender()) return false\n this.render()\n\n return true\n }\n\n getRenderableLayerIds() {\n return [...this._renderableLayerIds]\n }\n\n setLayerFilterFactory(factory) {\n this._layerFilterFactory = typeof factory === 'function' ? factory : null\n }\n\n applyLayerFilters(document = this._layerDocument) {\n if (!document || !Array.isArray(document.layers)) return false\n\n this._layerDocument = JSON.parse(JSON.stringify(document))\n\n for (const layer of this._layerDocument.layers) {\n if (!layer) continue\n\n const layerEntry = this._layerDisplayObjects.get(layer.id) || null\n if (layerEntry) layerEntry.layer = layer\n\n let displayObject = layerEntry?.displayObject || null\n if (!displayObject && this._isSubjectLayer(layer) && this.sprite?.__layerId === layer.id) {\n displayObject = this.sprite\n }\n\n if (!displayObject) continue\n this._applyLayerFilterInstances(displayObject, layer)\n }\n\n this.render()\n return true\n }\n\n _isSubjectLayer(layer) {\n return layer?.role === 'subject' || layer?.id === 'subject-layer'\n }\n\n _layerSupportsImageFilters(layer) {\n if (!layer || typeof layer !== 'object') return false\n if (layer.type === 'image') return true\n if (layer.type === 'background' || layer.role === 'background') {\n return layer.fill?.kind === 'media'\n }\n\n return false\n }\n\n _layerOpacity(layer) {\n const opacity = Number(layer?.opacity)\n if (!Number.isFinite(opacity)) return 1\n return Math.max(0, Math.min(1, opacity))\n }\n\n _applyLayerFilterInstances(displayObject, layer, context = {}) {\n if (!displayObject || !this._layerSupportsImageFilters(layer)) return\n\n let filters = []\n if (this._layerFilterFactory) {\n try {\n filters = this._layerFilterFactory(layer, {\n displayObject,\n forExport: context.forExport === true,\n previewToNativeScale: context.previewToNativeScale || 1,\n sourceWidth: context.sourceWidth,\n sourceHeight: context.sourceHeight,\n }) || []\n } catch (error) {\n console.error('[PixiRenderer] Failed to create layer filters:', error)\n filters = []\n }\n }\n\n const imageFilters = Array.isArray(filters) ? filters.filter(Boolean) : []\n for (const filter of imageFilters) {\n if (!filter || (typeof filter !== 'object' && typeof filter !== 'function')) continue\n try {\n Object.defineProperty(filter, '__mediablesLayerImageFilter', {\n value: true,\n configurable: true,\n })\n } catch (_error) {\n try {\n filter.__mediablesLayerImageFilter = true\n } catch (_ignored) {}\n }\n }\n\n const preservedFilters = Array.isArray(displayObject.filters)\n ? displayObject.filters.filter(filter => filter?.__mediablesLayerImageFilter !== true)\n : []\n const combinedFilters = [...preservedFilters, ...imageFilters]\n displayObject.filters = combinedFilters.length > 0 ? combinedFilters : null\n }\n\n async _createDisplayObjectForLayer(PIXI, layer) {\n const textDisplayObject = this._createTextDisplayObjectForLayer(PIXI, layer)\n if (textDisplayObject) return textDisplayObject\n\n const imageDisplayObject = await this._createImageDisplayObjectForLayer(PIXI, layer)\n if (imageDisplayObject) return imageDisplayObject\n\n const fill = this._backgroundFillForLayer(layer)\n\n if (fill?.kind === 'media') {\n const texture = await this._textureForLayerSource(PIXI, fill.source)\n if (!texture) return null\n\n const displayObject = this._createBackgroundSprite(PIXI, texture, fill.fit)\n displayObject.__sourceWidth = Number(fill.source?.originalWidth) || texture.width || 1\n displayObject.__sourceHeight = Number(fill.source?.originalHeight) || texture.height || 1\n this._applyLayerEffects(PIXI, displayObject, layer)\n this._applyLayerFilterInstances(displayObject, layer)\n\n return displayObject\n }\n\n if (fill?.kind === 'gradient') {\n const texture = this._createGradientTexture(PIXI, fill, 512, 512)\n if (!texture || typeof PIXI.Sprite !== 'function') return null\n\n const displayObject = new PIXI.Sprite(texture)\n displayObject.__ownsTexture = true\n displayObject.__sourceWidth = 512\n displayObject.__sourceHeight = 512\n this._applyLayerEffects(PIXI, displayObject, layer)\n\n return displayObject\n }\n\n if (this._backgroundColorForLayer(layer) && typeof PIXI.Graphics === 'function') {\n const displayObject = new PIXI.Graphics()\n this._applyLayerEffects(PIXI, displayObject, layer)\n\n return displayObject\n }\n\n return null\n }\n\n _isTextLayer(layer) {\n return layer?.type === 'text' || layer?.role === 'text'\n }\n\n _normalizeTextBox(layer) {\n const box = layer?.text?.box && typeof layer.text.box === 'object'\n ? layer.text.box\n : {}\n\n return {\n x: this._clampNumber(box.x, 0, 1, 0.2),\n y: this._clampNumber(box.y, 0, 1, 0.2),\n width: this._clampNumber(box.width, 0.05, 1, 0.6),\n height: this._clampNumber(box.height, 0.05, 1, 0.18),\n }\n }\n\n _clampNumber(value, min, max, fallback) {\n const numeric = Number(value)\n if (!Number.isFinite(numeric)) return fallback\n\n return Math.max(min, Math.min(max, numeric))\n }\n\n _supportedTextFontFamily(value) {\n const allowed = ['Inter', 'Arial', 'Helvetica', 'Georgia', 'Times New Roman', 'Courier New', 'Verdana']\n const candidate = typeof value === 'string' ? value.trim() : ''\n\n return allowed.includes(candidate) ? candidate : 'Inter'\n }\n\n _supportedTextColor(value, fallback = '#000000') {\n return typeof value === 'string' && /^#[0-9a-f]{6}$/i.test(value)\n ? value\n : fallback\n }\n\n _textBlendMode(layer) {\n const mode = layer?.blendMode\n return TEXT_BLEND_MODES.includes(mode) ? mode : 'normal'\n }\n\n _textContentForLayer(layer) {\n const content = layer?.text?.content\n return content == null ? '' : String(content)\n }\n\n _textStyleForLayer(layer, scale = 1, wordWrapWidth = 1, options = {}) {\n const style = layer?.text?.style && typeof layer.text.style === 'object'\n ? layer.text.style\n : {}\n const effects = this._textEffectsForLayer(layer)\n const fontSize = this._clampNumber(style.fontSize, 8, 400, 64)\n const lineHeight = this._clampNumber(style.lineHeight, 0.8, 3, 1.2)\n const letterSpacing = this._clampNumber(style.letterSpacing, -100, 100, 0)\n const fontWeight = this._normalizeTextFontWeight(style.fontWeight)\n const fontStyle = ['normal', 'italic'].includes(style.fontStyle) ? style.fontStyle : 'normal'\n const align = ['left', 'center', 'right'].includes(style.align) ? style.align : 'center'\n const fill = options.fill || (typeof style.fill === 'string' && /^#[0-9a-f]{6}$/i.test(style.fill) ? style.fill : '#ffffff')\n const textStyle = {\n fontFamily: this._supportedTextFontFamily(style.fontFamily),\n fontSize: fontSize * scale,\n fontWeight,\n fontStyle,\n fill,\n align,\n lineHeight: fontSize * lineHeight * scale,\n letterSpacing: letterSpacing * scale,\n wordWrap: true,\n wordWrapWidth: Math.max(1, wordWrapWidth),\n breakWords: true,\n }\n\n if (options.stroke !== false && effects.stroke.enabled && effects.stroke.width > 0) {\n textStyle.stroke = {\n color: effects.stroke.color,\n width: effects.stroke.width * scale,\n }\n }\n\n return textStyle\n }\n\n _normalizeTextFontWeight(value) {\n if (value === 'normal') return 400\n if (value === 'bold') return 700\n\n const numeric = Number(value)\n if (!Number.isFinite(numeric)) return 700\n if (numeric < 100 || numeric > 900 || numeric % 100 !== 0) return 700\n\n return numeric\n }\n\n _textEffectsForLayer(layer) {\n const source = layer?.text?.effects && typeof layer.text.effects === 'object'\n ? layer.text.effects\n : {}\n const stroke = source.stroke && typeof source.stroke === 'object' ? source.stroke : {}\n const shadow = source.shadow && typeof source.shadow === 'object' ? source.shadow : {}\n const glow = source.glow && typeof source.glow === 'object' ? source.glow : {}\n const backdrop = source.backdrop && typeof source.backdrop === 'object' ? source.backdrop : {}\n\n return {\n stroke: {\n enabled: stroke.enabled === true,\n color: this._supportedTextColor(stroke.color, DEFAULT_TEXT_EFFECTS.stroke.color),\n width: this._clampNumber(stroke.width, 0, 80, DEFAULT_TEXT_EFFECTS.stroke.width),\n },\n shadow: {\n enabled: shadow.enabled === true,\n color: this._supportedTextColor(shadow.color, DEFAULT_TEXT_EFFECTS.shadow.color),\n alpha: this._clampNumber(shadow.alpha, 0, 1, DEFAULT_TEXT_EFFECTS.shadow.alpha),\n blur: this._clampNumber(shadow.blur, 0, 120, DEFAULT_TEXT_EFFECTS.shadow.blur),\n distance: this._clampNumber(shadow.distance, 0, 200, DEFAULT_TEXT_EFFECTS.shadow.distance),\n angle: this._normalizeDegrees(shadow.angle ?? DEFAULT_TEXT_EFFECTS.shadow.angle),\n },\n glow: {\n enabled: glow.enabled === true,\n color: this._supportedTextColor(glow.color, DEFAULT_TEXT_EFFECTS.glow.color),\n alpha: this._clampNumber(glow.alpha, 0, 1, DEFAULT_TEXT_EFFECTS.glow.alpha),\n blur: this._clampNumber(glow.blur, 0, 120, DEFAULT_TEXT_EFFECTS.glow.blur),\n },\n backdrop: {\n enabled: backdrop.enabled === true,\n color: this._supportedTextColor(backdrop.color, DEFAULT_TEXT_EFFECTS.backdrop.color),\n opacity: this._clampNumber(backdrop.opacity, 0, 1, DEFAULT_TEXT_EFFECTS.backdrop.opacity),\n padding: this._clampNumber(backdrop.padding, 0, 200, DEFAULT_TEXT_EFFECTS.backdrop.padding),\n radius: this._clampNumber(backdrop.radius, 0, 200, DEFAULT_TEXT_EFFECTS.backdrop.radius),\n blur: this._clampNumber(backdrop.blur, 0, 80, DEFAULT_TEXT_EFFECTS.backdrop.blur),\n },\n }\n }\n\n _createTextBlurFilter(PIXI, amount) {\n if (amount <= 0 || typeof PIXI?.BlurFilter !== 'function') return null\n\n try {\n let filter\n try {\n filter = new PIXI.BlurFilter({ strength: amount, quality: 4 })\n } catch (_e) {\n filter = new PIXI.BlurFilter(amount)\n }\n\n if (typeof filter.blur === 'number') filter.blur = amount\n if (typeof filter.strength === 'number') filter.strength = amount\n filter.padding = Math.max(Number(filter.padding) || 0, Math.ceil(amount * 2))\n\n return filter\n } catch (_e) {\n return null\n }\n }\n\n _textEffectPadding(effects, scale) {\n const stroke = effects.stroke.enabled ? effects.stroke.width * scale : 0\n const shadow = effects.shadow.enabled\n ? (effects.shadow.distance + effects.shadow.blur * 2) * scale\n : 0\n const glow = effects.glow.enabled ? effects.glow.blur * 2 * scale : 0\n const backdrop = effects.backdrop.enabled\n ? (effects.backdrop.padding + effects.backdrop.blur * 2) * scale\n : 0\n\n return Math.ceil(Math.max(stroke, shadow, glow, backdrop, 0))\n }\n\n _createTextDisplayObjectForLayer(PIXI, layer) {\n if (!this._isTextLayer(layer) || typeof PIXI?.Text !== 'function') return null\n\n const textObject = this._createTextPrimitive(PIXI, layer)\n if (!textObject) return null\n\n if (typeof PIXI.Container !== 'function' || typeof PIXI.Graphics !== 'function') {\n return textObject\n }\n\n const container = new PIXI.Container()\n const mask = new PIXI.Graphics()\n const effectMask = new PIXI.Graphics()\n const backdrop = new PIXI.Graphics()\n const shadow = this._createTextPrimitive(PIXI, layer)\n const glow = this._createTextPrimitive(PIXI, layer)\n container.__isTextLayerContainer = true\n container.__textObject = textObject\n container.__textMask = mask\n container.__textEffectMask = effectMask\n container.__textBackdrop = backdrop\n container.__textShadow = shadow\n container.__textGlow = glow\n backdrop.visible = false\n if (shadow) shadow.visible = false\n if (glow) glow.visible = false\n textObject.mask = mask\n container.addChild(backdrop)\n if (shadow) container.addChild(shadow)\n if (glow) container.addChild(glow)\n container.addChild(textObject)\n container.addChild(effectMask)\n container.addChild(mask)\n\n return container\n }\n\n _createTextPrimitive(PIXI, layer) {\n try {\n return new PIXI.Text({\n text: this._textContentForLayer(layer),\n style: this._textStyleForLayer(layer),\n })\n } catch (_e) {\n try {\n return new PIXI.Text(this._textContentForLayer(layer), this._textStyleForLayer(layer))\n } catch (_ignored) {\n return null\n }\n }\n }\n\n async _createSubjectDisplayObjectForLayer(PIXI, layer) {\n const sourceUrl = this._sourceUrlForLayerSource(layer?.source)\n if (!sourceUrl || sourceUrl === this._loadedTextureUrl) return null\n\n return this._createImageDisplayObjectForLayer(PIXI, layer)\n }\n\n async _createImageDisplayObjectForLayer(PIXI, layer) {\n if (layer?.type !== 'image') return null\n\n const texture = await this._textureForLayerSource(PIXI, layer.source)\n if (!texture || typeof PIXI.Sprite !== 'function') return null\n\n const displayObject = new PIXI.Sprite(texture)\n displayObject.__sourceWidth = Number(layer.source?.originalWidth) || texture.width || 1\n displayObject.__sourceHeight = Number(layer.source?.originalHeight) || texture.height || 1\n this._applyLayerEffects(PIXI, displayObject, layer)\n this._applyLayerFilterInstances(displayObject, layer)\n\n return displayObject\n }\n\n _backgroundFillForLayer(layer) {\n const fill = layer?.fill\n if (fill && typeof fill === 'object') return fill\n\n const source = layer?.source\n if (source?.kind === 'color' && typeof source.color === 'string') {\n return { kind: 'color', value: source.color }\n }\n\n return null\n }\n\n _backgroundColorForLayer(layer) {\n const source = layer?.source\n if (source?.kind === 'color' && typeof source.color === 'string') {\n return source.color\n }\n\n const fill = layer?.fill\n if (fill?.kind === 'color' && typeof fill.value === 'string') {\n return fill.value\n }\n\n return null\n }\n\n _sourceUrlForLayerSource(source) {\n if (!source || typeof source !== 'object') return null\n\n for (const key of ['previewUrl', 'sourceUrl', 'url']) {\n const value = source[key]\n if (typeof value === 'string' && value.trim() !== '') {\n return value\n }\n }\n\n return null\n }\n\n async _textureForLayerSource(PIXI, source) {\n const url = this._sourceUrlForLayerSource(source)\n if (!url) return null\n\n const image = await this._loadImageElement(url)\n if (!image) return null\n\n return PIXI.Texture.from(image)\n }\n\n _createBackgroundSprite(PIXI, texture, fit = 'cover') {\n if (fit === 'tile' && typeof PIXI.TilingSprite === 'function') {\n try {\n const tiled = new PIXI.TilingSprite({ texture, width: 1, height: 1 })\n tiled.__isTilingBackground = true\n\n return tiled\n } catch (_e) {\n try {\n const tiled = new PIXI.TilingSprite(texture, 1, 1)\n tiled.__isTilingBackground = true\n\n return tiled\n } catch (_ignored) {\n // Fall through to a normal sprite if this PIXI build has a\n // different TilingSprite constructor signature.\n }\n }\n }\n\n return new PIXI.Sprite(texture)\n }\n\n _createGradientTexture(PIXI, fill, width, height) {\n if (typeof document === 'undefined' || !PIXI?.Texture?.from) return null\n\n const canvas = document.createElement('canvas')\n canvas.width = Math.max(1, Math.round(width))\n canvas.height = Math.max(1, Math.round(height))\n const context = canvas.getContext('2d')\n if (!context) return null\n\n const stops = Array.isArray(fill?.stops) && fill.stops.length > 0\n ? fill.stops\n : [\n { offset: 0, color: '#111827' },\n { offset: 1, color: '#f59e0b' },\n ]\n\n let gradient\n if (fill?.gradientType === 'radial') {\n const radius = Math.max(canvas.width, canvas.height) / 2\n gradient = context.createRadialGradient(\n canvas.width / 2,\n canvas.height / 2,\n 0,\n canvas.width / 2,\n canvas.height / 2,\n radius\n )\n } else {\n const radians = (Number(fill?.angle) || 0) * Math.PI / 180\n const centerX = canvas.width / 2\n const centerY = canvas.height / 2\n const length = Math.sqrt(canvas.width ** 2 + canvas.height ** 2) / 2\n const dx = Math.cos(radians) * length\n const dy = Math.sin(radians) * length\n gradient = context.createLinearGradient(centerX - dx, centerY - dy, centerX + dx, centerY + dy)\n }\n\n for (const stop of stops) {\n if (!stop || typeof stop.color !== 'string') continue\n const offset = Number.isFinite(Number(stop.offset)) ? Math.max(0, Math.min(1, Number(stop.offset))) : 0\n gradient.addColorStop(offset, stop.color)\n }\n\n context.fillStyle = gradient\n context.fillRect(0, 0, canvas.width, canvas.height)\n\n return PIXI.Texture.from(canvas)\n }\n\n _backgroundBlurAmount(layer) {\n const effects = Array.isArray(layer?.effects) ? layer.effects : []\n const effect = effects.find(candidate => candidate?.id === 'background-blur' || candidate?.type === 'blur')\n const amount = Number(effect?.params?.amount)\n\n return Number.isFinite(amount) ? Math.max(0, amount) : 0\n }\n\n _applyLayerEffects(PIXI, displayObject, layer) {\n const blur = this._backgroundBlurAmount(layer)\n if (blur <= 0 || typeof PIXI?.BlurFilter !== 'function') return\n\n try {\n let filter\n try {\n filter = new PIXI.BlurFilter({ strength: blur, quality: 4 })\n } catch (_e) {\n filter = new PIXI.BlurFilter(blur)\n }\n\n if (typeof filter.blur === 'number') filter.blur = blur\n if (typeof filter.strength === 'number') filter.strength = blur\n filter.padding = Math.max(Number(filter.padding) || 0, Math.ceil(blur * 2))\n displayObject.filters = Array.isArray(displayObject.filters)\n ? [...displayObject.filters, filter]\n : [filter]\n } catch (_e) {\n // Older PIXI bundles may omit BlurFilter. Background blur is optional\n // at render time and remains persisted in the layer document.\n }\n }\n\n _syncLayerDisplayObjects() {\n if (!this.sprite) return\n\n for (const { layer, displayObject } of this._layerDisplayObjects.values()) {\n this._syncLayerDisplayObject(layer, displayObject)\n }\n }\n\n _syncLayerDisplayObject(layer, displayObject) {\n if (!this.sprite || !displayObject) return\n\n if (this._isTextLayer(layer)) {\n this._fitTextDisplayObjectToRect(displayObject, layer, {\n x: this.sprite.x,\n y: this.sprite.y,\n width: this.sprite.width,\n height: this.sprite.height,\n })\n return\n }\n\n const backgroundColor = this._backgroundColorForLayer(layer)\n if (backgroundColor && typeof displayObject.clear === 'function') {\n displayObject.clear()\n displayObject\n .rect(this.sprite.x, this.sprite.y, this.sprite.width, this.sprite.height)\n .fill(backgroundColor)\n return\n }\n\n this._fitDisplayObjectToRect(displayObject, layer, {\n x: this.sprite.x,\n y: this.sprite.y,\n width: this.sprite.width,\n height: this.sprite.height,\n })\n }\n\n _fillTextEffectGraphics(graphics, color, alpha = 1) {\n if (!graphics || typeof graphics.fill !== 'function') return\n\n try {\n graphics.fill({ color, alpha })\n } catch (_e) {\n graphics.fill(color)\n graphics.alpha = alpha\n }\n }\n\n _syncTextEffectPrimitive(PIXI, primitive, layer, effects, kind, scale, width, effectMask) {\n if (!primitive) return\n\n const effect = effects[kind]\n primitive.visible = effect?.enabled === true\n primitive.filters = null\n primitive.mask = primitive.visible ? effectMask : null\n\n if (!primitive.visible) return\n\n primitive.text = this._textContentForLayer(layer)\n primitive.rotation = 0\n\n if (kind === 'shadow') {\n const radians = effect.angle * Math.PI / 180\n primitive.x = Math.cos(radians) * effect.distance * scale\n primitive.y = Math.sin(radians) * effect.distance * scale\n primitive.alpha = effect.alpha\n primitive.style = this._textStyleForLayer(layer, scale, width, {\n fill: effect.color,\n stroke: false,\n })\n const filter = this._createTextBlurFilter(PIXI, effect.blur * scale)\n primitive.filters = filter ? [filter] : null\n return\n }\n\n if (kind === 'glow') {\n primitive.x = 0\n primitive.y = 0\n primitive.alpha = effect.alpha\n primitive.style = this._textStyleForLayer(layer, scale, width, {\n fill: effect.color,\n stroke: false,\n })\n const filter = this._createTextBlurFilter(PIXI, effect.blur * scale)\n primitive.filters = filter ? [filter] : null\n }\n }\n\n _syncTextBackdrop(PIXI, backdrop, effects, scale, width, height, effectMask) {\n if (!backdrop) return\n\n const effect = effects.backdrop\n backdrop.visible = effect.enabled === true\n backdrop.filters = null\n backdrop.mask = backdrop.visible ? effectMask : null\n backdrop.alpha = 1\n\n if (typeof backdrop.clear === 'function') backdrop.clear()\n if (!backdrop.visible) return\n\n const padding = effect.padding * scale\n const radius = effect.radius * scale\n const x = -padding\n const y = -padding\n const backdropWidth = width + padding * 2\n const backdropHeight = height + padding * 2\n\n if (radius > 0 && typeof backdrop.roundRect === 'function') {\n backdrop.roundRect(x, y, backdropWidth, backdropHeight, radius)\n } else if (typeof backdrop.rect === 'function') {\n backdrop.rect(x, y, backdropWidth, backdropHeight)\n }\n this._fillTextEffectGraphics(backdrop, effect.color, effect.opacity)\n\n const filter = this._createTextBlurFilter(PIXI, effect.blur * scale)\n backdrop.filters = filter ? [filter] : null\n }\n\n _fitTextDisplayObjectToRect(displayObject, layer, rect) {\n const box = this._normalizeTextBox(layer)\n const x = rect.x + box.x * rect.width\n const y = rect.y + box.y * rect.height\n const width = box.width * rect.width\n const height = box.height * rect.height\n const sourceWidth = Number(this.originalTexture?.width) || rect.width\n const scale = sourceWidth > 0 ? rect.width / sourceWidth : 1\n const effects = this._textEffectsForLayer(layer)\n\n displayObject.x = x\n displayObject.y = y\n displayObject.rotation = this._normalizeDegrees(Number(layer?.transform?.rotation) || 0) * Math.PI / 180\n displayObject.blendMode = this._textBlendMode(layer)\n displayObject.__textBox = box\n displayObject.__textLayout = { x, y, width, height, scale }\n\n const textObject = displayObject.__textObject || displayObject\n const mask = displayObject.__textMask || null\n const effectMask = displayObject.__textEffectMask || null\n textObject.text = this._textContentForLayer(layer)\n textObject.style = this._textStyleForLayer(layer, scale, width)\n textObject.x = 0\n textObject.y = 0\n textObject.rotation = 0\n\n if (mask && typeof mask.clear === 'function' && typeof mask.rect === 'function' && typeof mask.fill === 'function') {\n mask.clear()\n mask.rect(0, 0, width, height).fill(0xffffff)\n textObject.mask = mask\n }\n\n if (effectMask && typeof effectMask.clear === 'function' && typeof effectMask.rect === 'function' && typeof effectMask.fill === 'function') {\n const padding = this._textEffectPadding(effects, scale)\n effectMask.clear()\n effectMask.rect(-padding, -padding, width + padding * 2, height + padding * 2).fill(0xffffff)\n }\n\n this._syncTextBackdrop(window.PIXI, displayObject.__textBackdrop, effects, scale, width, height, effectMask)\n this._syncTextEffectPrimitive(window.PIXI, displayObject.__textShadow, layer, effects, 'shadow', scale, width, effectMask)\n this._syncTextEffectPrimitive(window.PIXI, displayObject.__textGlow, layer, effects, 'glow', scale, width, effectMask)\n }\n\n _fitDisplayObjectToRect(displayObject, layer, rect) {\n const fill = this._backgroundFillForLayer(layer)\n const fit = fill?.fit || 'cover'\n\n if (displayObject.__isTilingBackground) {\n displayObject.x = rect.x\n displayObject.y = rect.y\n displayObject.width = rect.width\n displayObject.height = rect.height\n displayObject.tilePosition?.set?.(0, 0)\n displayObject.tileScale?.set?.(1, 1)\n return\n }\n\n if (fit === 'stretch' || fill?.kind === 'gradient' || fill?.kind === 'color') {\n displayObject.x = rect.x\n displayObject.y = rect.y\n displayObject.width = rect.width\n displayObject.height = rect.height\n return\n }\n\n const sourceWidth = Number(displayObject.__sourceWidth || fill?.source?.originalWidth || displayObject.texture?.width || rect.width)\n const sourceHeight = Number(displayObject.__sourceHeight || fill?.source?.originalHeight || displayObject.texture?.height || rect.height)\n if (!Number.isFinite(sourceWidth) || !Number.isFinite(sourceHeight) || sourceWidth <= 0 || sourceHeight <= 0) {\n displayObject.x = rect.x\n displayObject.y = rect.y\n displayObject.width = rect.width\n displayObject.height = rect.height\n return\n }\n\n const scale = fit === 'contain'\n ? Math.min(rect.width / sourceWidth, rect.height / sourceHeight)\n : Math.max(rect.width / sourceWidth, rect.height / sourceHeight)\n const width = sourceWidth * scale\n const height = sourceHeight * scale\n\n displayObject.x = rect.x + (rect.width - width) / 2\n displayObject.y = rect.y + (rect.height - height) / 2\n displayObject.width = width\n displayObject.height = height\n }\n\n _destroyLayerDisplayObjects() {\n if (!this._layerDisplayObjects?.size) {\n return\n }\n\n for (const { displayObject } of this._layerDisplayObjects.values()) {\n try {\n this.app?.stage?.removeChild?.(displayObject)\n } catch (_e) {\n // Ignore stale PIXI parent references during reload.\n }\n const ownsTexture = displayObject?.__ownsTexture === true\n displayObject?.destroy?.({ children: true, texture: ownsTexture, textureSource: ownsTexture })\n }\n\n this._layerDisplayObjects.clear()\n this._renderableLayerIds = []\n }\n\n _destroyTransientDisplayObject(displayObject) {\n if (!displayObject) return\n\n try {\n this.app?.stage?.removeChild?.(displayObject)\n } catch (_e) {\n // Ignore stale PIXI parent references during render races.\n }\n\n const ownsTexture = displayObject?.__ownsTexture === true\n displayObject?.destroy?.({ children: true, texture: ownsTexture, textureSource: ownsTexture })\n }\n\n /**\n * Normalize degrees to the 0..360 range.\n * @param {number} degrees\n * @returns {number}\n */\n _normalizeDegrees(degrees) {\n if (!Number.isFinite(degrees)) return 0\n const normalized = degrees % 360\n return normalized < 0 ? normalized + 360 : normalized\n }\n\n /**\n * Calculate the axis-aligned bounds for a rotated rectangle.\n * @param {number} width\n * @param {number} height\n * @param {number} degrees\n * @returns {{ width: number, height: number }}\n */\n getRotatedBounds(width, height, degrees) {\n const rotation = this._normalizeDegrees(degrees)\n if (rotation === 0) {\n return {\n width: Math.max(1, Math.round(width)),\n height: Math.max(1, Math.round(height)),\n }\n }\n\n const radians = rotation * Math.PI / 180\n const rawCos = Math.abs(Math.cos(radians))\n const rawSin = Math.abs(Math.sin(radians))\n const cos = rawCos < 1e-10 ? 0 : rawCos\n const sin = rawSin < 1e-10 ? 0 : rawSin\n\n return {\n width: Math.max(1, Math.ceil(width * cos + height * sin)),\n height: Math.max(1, Math.ceil(width * sin + height * cos)),\n }\n }\n\n /**\n * Permanently rotate the working texture. This keeps crop math simple:\n * after rotation, sprite coordinates and texture dimensions both describe\n * the visible image exactly.\n *\n * @param {number} degrees Clockwise degrees to add to the working texture.\n * @returns {boolean}\n */\n rotateBy(degrees) {\n const rotation = this._normalizeDegrees(degrees)\n if (rotation === 0) return true\n\n if (!this.app?.renderer || !this.originalTexture || !this.sprite) {\n return false\n }\n\n const PIXI = window.PIXI\n const srcW = Math.round(this.originalTexture.width)\n const srcH = Math.round(this.originalTexture.height)\n if (srcW <= 0 || srcH <= 0) return false\n\n const { width: outW, height: outH } = this.getRotatedBounds(srcW, srcH, rotation)\n const radians = rotation * Math.PI / 180\n\n const container = new PIXI.Container()\n const src = new PIXI.Sprite(this.originalTexture)\n src.anchor?.set?.(0.5)\n src.x = outW / 2\n src.y = outH / 2\n src.rotation = radians\n container.addChild(src)\n\n const rt = PIXI.RenderTexture.create({ width: outW, height: outH, resolution: 1 })\n\n try {\n this.app.renderer.render({\n container,\n target: rt,\n clear: true,\n clearColor: [0, 0, 0, 0],\n })\n } catch (e) {\n console.error('[PixiRenderer.rotateBy] Rotation render failed:', e)\n rt.destroy(true)\n return false\n } finally {\n container.removeChildren()\n src.destroy({ children: false, texture: false, textureSource: false })\n container.destroy({ children: false })\n }\n\n const prevTexture = this.originalTexture\n const previousFilters = Array.isArray(this.sprite.filters)\n ? [...this.sprite.filters]\n : null\n\n if (prevTexture && prevTexture !== this.baseTexture) {\n prevTexture.destroy(true)\n }\n\n this.originalTexture = rt\n this._lastExportDimensions = null\n\n this.app.stage.removeChild(this.sprite)\n this.sprite.destroy({ children: false, texture: false, textureSource: false })\n\n const newSprite = new PIXI.Sprite(rt)\n if (previousFilters?.length) {\n newSprite.filters = previousFilters\n }\n this.app.stage.addChild(newSprite)\n this.sprite = newSprite\n\n this.fitScale = this.getFitScaleFor(rt)\n this.zoom = 1\n this.applyViewTransform({ keepCenter: false })\n this.render()\n this.emit('textureRotated', { width: outW, height: outH, degrees: rotation })\n\n return true\n }\n\n /**\n * Export the current image with filters applied.\n *\n * @param {string} format - 'png' or 'jpeg'\n * @param {number} quality - Quality for jpeg (0-1)\n * @param {number} maxEdge - Maximum edge size in pixels (0 for original)\n * @param {boolean} dontUpscale - Don't upscale smaller images\n * @param {number} [maxPixels=0] - Max total pixel count (width*height). 0 disables.\n * Backend API often limits payload size (blowglass caps at 8M pixels); set this\n * to keep the export under that cap while preserving aspect ratio.\n * @returns {string|null} Data URL\n */\n exportImage(format = 'png', quality = 0.92, maxEdge = 0, dontUpscale = true, maxPixels = 0) {\n if (!this.app?.renderer || !this.originalTexture || !this.sprite) {\n console.error('[PixiRenderer.exportImage] Missing app/renderer, texture, or sprite', {\n hasApp: !!this.app,\n hasRenderer: !!this.app?.renderer,\n hasTexture: !!this.originalTexture,\n hasSprite: !!this.sprite\n })\n return null\n }\n\n const PIXI = window.PIXI\n const srcW = Math.round(this.originalTexture.width)\n const srcH = Math.round(this.originalTexture.height)\n\n if (srcW <= 0 || srcH <= 0) {\n console.error('[PixiRenderer.exportImage] Invalid texture dimensions', { srcW, srcH })\n return null\n }\n\n const previewW = this.sprite.width\n const previewH = this.sprite.height\n\n if (previewW <= 0 || previewH <= 0) {\n console.error('[PixiRenderer.exportImage] Sprite has zero dimensions')\n return null\n }\n\n // Filter parameters (blur, distance, outline thickness, etc.) are in\n // render-target pixel units. The user tunes them while looking at the\n // preview, where the image is downscaled to fit the viewport. To make\n // the export visually match the preview at native resolution, scale\n // those size-dependent params by (preview pixels) / (source pixels).\n //\n // Filters opt in by exposing a createExportFilter({previewToNativeScale})\n // method that returns a fresh, scaled instance. Filters that don't have\n // size-dependent params (color-matrix, alpha, hue) just pass through.\n const previewSourceScale = previewW / srcW // e.g. 0.186\n const previewToNativeScale = previewSourceScale > 0\n ? 1 / previewSourceScale // e.g. 5.37\n : 1\n\n const layerDocumentForExport = this._layerDocument\n const hasLayerDocumentForExport = layerDocumentForExport && Array.isArray(layerDocumentForExport.layers)\n\n const exportFilters = []\n if (!hasLayerDocumentForExport && Array.isArray(this.sprite.filters)) {\n for (const f of this.sprite.filters) {\n if (!f) continue\n if (typeof f.createExportFilter === 'function') {\n const cloned = f.createExportFilter({ previewToNativeScale })\n if (cloned) exportFilters.push(cloned)\n } else {\n // Filter doesn't need scaling — reuse the live instance.\n exportFilters.push(f)\n }\n }\n }\n\n // PIXI's filter pipeline SUMS the padding of each filter in a chain\n // (each filter's framebuffer must contain the previous filter's bleed).\n // Use sum, not max, to size the render target margin.\n let totalPadding = 0\n for (const f of exportFilters) {\n const p = typeof f.getExportPadding === 'function'\n ? Number(f.getExportPadding()) || 0\n : (typeof f._exportPadding === 'number'\n ? f._exportPadding\n : (typeof f.padding === 'number' ? f.padding : 0))\n if (p > 0) totalPadding += p\n }\n // Floor at 64px so small/default filters with no reported padding still\n // have room to bleed. Doubling on retry covers anything bigger.\n const initialMargin = Math.max(Math.ceil(totalPadding), 64)\n\n // Render an isolated export-only container. We DON'T render app.stage —\n // it carries preview transforms, the editor's viewport, and possibly\n // crop overlays. Building a clean container with a fresh sprite using\n // the SAME originalTexture (no copy) gives us deterministic coordinates\n // and lets us scale + filter without mutating the live preview sprite.\n const renderWithMargin = (margin) => {\n const renderW = Math.ceil(srcW + 2 * margin)\n const renderH = Math.ceil(srcH + 2 * margin)\n\n const exportRoot = new PIXI.Container()\n const exportSprites = []\n\n const addSubjectSprite = (layer = null) => {\n const exportSprite = new PIXI.Sprite(this.originalTexture)\n exportSprite.x = margin\n exportSprite.y = margin\n exportSprite.scale.set(1, 1)\n exportSprite.alpha = layer ? this._layerOpacity(layer) : 1\n if (layer) {\n this._applyLayerFilterInstances(exportSprite, layer, {\n forExport: true,\n previewToNativeScale,\n sourceWidth: srcW,\n sourceHeight: srcH,\n })\n }\n\n if (exportFilters.length > 0) {\n exportSprite.filters = exportFilters\n // Force PIXI's filter measurement to use the sprite's local geometry\n // bounds (0,0,srcW,srcH). Without this, PIXI computes filter area\n // from the global render-target rectangle, which can clip on certain\n // configs.\n exportSprite.filterArea = new PIXI.Rectangle(0, 0, srcW, srcH)\n }\n\n exportRoot.addChild(exportSprite)\n exportSprites.push(exportSprite)\n }\n\n const addImageLayer = (layer) => {\n const liveDisplayObject = this._layerDisplayObjects.get(layer.id)?.displayObject\n const texture = liveDisplayObject?.texture || (this._isSubjectLayer(layer) ? this.originalTexture : null)\n if (!texture || typeof PIXI.Sprite !== 'function') return false\n\n const exportSprite = new PIXI.Sprite(texture)\n exportSprite.__sourceWidth = Number(layer.source?.originalWidth || liveDisplayObject?.__sourceWidth || texture.width || srcW)\n exportSprite.__sourceHeight = Number(layer.source?.originalHeight || liveDisplayObject?.__sourceHeight || texture.height || srcH)\n this._fitDisplayObjectToRect(exportSprite, layer, { x: margin, y: margin, width: srcW, height: srcH })\n exportSprite.alpha = this._layerOpacity(layer)\n\n if (!hasLayerDocumentForExport && this._isSubjectLayer(layer) && exportFilters.length > 0) {\n exportSprite.filters = exportFilters\n exportSprite.filterArea = new PIXI.Rectangle(0, 0, srcW, srcH)\n }\n\n this._applyLayerEffects(PIXI, exportSprite, layer)\n this._applyLayerFilterInstances(exportSprite, layer, {\n forExport: true,\n previewToNativeScale,\n sourceWidth: srcW,\n sourceHeight: srcH,\n })\n exportRoot.addChild(exportSprite)\n exportSprites.push(exportSprite)\n\n return true\n }\n\n const addBackgroundLayer = (layer) => {\n const fill = this._backgroundFillForLayer(layer)\n const backgroundColor = this._backgroundColorForLayer(layer)\n const rect = { x: margin, y: margin, width: srcW, height: srcH }\n let background = null\n\n if (backgroundColor && typeof PIXI.Graphics === 'function') {\n background = new PIXI.Graphics()\n background.rect(margin, margin, srcW, srcH).fill(backgroundColor)\n } else if (fill?.kind === 'gradient' && typeof PIXI.Sprite === 'function') {\n const texture = this._createGradientTexture(PIXI, fill, srcW, srcH)\n if (!texture) return false\n background = new PIXI.Sprite(texture)\n background.__ownsTexture = true\n background.__sourceWidth = srcW\n background.__sourceHeight = srcH\n this._fitDisplayObjectToRect(background, layer, rect)\n } else if (fill?.kind === 'media') {\n const liveDisplayObject = this._layerDisplayObjects.get(layer.id)?.displayObject\n const texture = liveDisplayObject?.texture\n if (!texture) return false\n\n background = this._createBackgroundSprite(PIXI, texture, fill.fit)\n background.__sourceWidth = Number(fill.source?.originalWidth || liveDisplayObject.__sourceWidth || texture.width || 1)\n background.__sourceHeight = Number(fill.source?.originalHeight || liveDisplayObject.__sourceHeight || texture.height || 1)\n this._fitDisplayObjectToRect(background, layer, rect)\n }\n\n if (!background) return false\n\n background.alpha = this._layerOpacity(layer)\n this._applyLayerEffects(PIXI, background, layer)\n this._applyLayerFilterInstances(background, layer, {\n forExport: true,\n previewToNativeScale,\n sourceWidth: srcW,\n sourceHeight: srcH,\n })\n exportRoot.addChild(background)\n exportSprites.push(background)\n\n return true\n }\n\n const addTextLayer = (layer) => {\n const textDisplayObject = this._createTextDisplayObjectForLayer(PIXI, layer)\n if (!textDisplayObject) return false\n\n this._fitTextDisplayObjectToRect(textDisplayObject, layer, { x: margin, y: margin, width: srcW, height: srcH })\n textDisplayObject.alpha = this._layerOpacity(layer)\n exportRoot.addChild(textDisplayObject)\n exportSprites.push(textDisplayObject)\n\n return true\n }\n\n const layerDocument = layerDocumentForExport\n const hasLayerDocument = hasLayerDocumentForExport\n if (hasLayerDocument) {\n for (const layer of layerDocument.layers) {\n if (!layer || layer.visible === false) continue\n\n if (this._isTextLayer(layer)) {\n addTextLayer(layer)\n continue\n }\n\n if (layer.type === 'image') {\n if (!addImageLayer(layer) && this._isSubjectLayer(layer)) {\n addSubjectSprite(layer)\n }\n continue\n }\n\n if (this._isSubjectLayer(layer)) {\n if (!addImageLayer(layer)) addSubjectSprite(layer)\n continue\n }\n\n addBackgroundLayer(layer)\n }\n } else {\n addSubjectSprite()\n }\n\n if (!hasLayerDocument && exportRoot.children?.length === 0) {\n addSubjectSprite()\n }\n\n const rt = PIXI.RenderTexture.create({\n width: renderW,\n height: renderH,\n resolution: 1,\n })\n const childCount = exportRoot.children?.length ?? 0\n\n try {\n this.app.renderer.render({\n container: exportRoot,\n target: rt,\n clear: true,\n clearColor: [0, 0, 0, 0],\n })\n const canvas = this.app.renderer.extract?.canvas?.(rt)\n if (!canvas) return null\n return { canvas, width: renderW, height: renderH, margin, childCount }\n } finally {\n // Filter instances are owned by exportFilters; don't destroy them\n // here (some are clones we'll discard, others are shared with\n // preview). Destroy the temp sprite/container/rt only.\n for (const exportSprite of exportSprites) {\n exportSprite.filters = null\n }\n exportRoot.removeChildren()\n for (const exportSprite of exportSprites) {\n const ownsTexture = exportSprite?.__ownsTexture === true\n exportSprite.destroy({ children: false, texture: ownsTexture, textureSource: ownsTexture })\n }\n exportRoot.destroy({ children: false })\n rt.destroy(true)\n }\n }\n\n // Scan the alpha channel for the non-transparent bounding box. Returns\n // {x, y, width, height, touchesEdge} where touchesEdge indicates the\n // bbox reaches the outer boundary of the canvas (i.e. we probably\n // clipped bleed and should re-render with a bigger margin).\n const scanAlphaBounds = (canvas, w, h) => {\n const ctx = typeof canvas.getContext === 'function' ? canvas.getContext('2d') : null\n if (!ctx) return null\n let pixels\n try {\n pixels = ctx.getImageData(0, 0, w, h).data\n } catch (e) {\n console.error('[PixiRenderer.exportImage] getImageData failed:', e)\n return null\n }\n const threshold = 1 // ignore alpha noise from dithering/interpolation\n let minX = w, minY = h, maxX = -1, maxY = -1\n const stride = 4\n const rowStride = w * stride\n for (let y = 0; y < h; y++) {\n const rowStart = y * rowStride\n for (let x = 0; x < w; x++) {\n if (pixels[rowStart + x * stride + 3] >= threshold) {\n if (x < minX) minX = x\n if (x > maxX) maxX = x\n if (y < minY) minY = y\n if (y > maxY) maxY = y\n }\n }\n }\n if (maxX < 0) return null // fully transparent\n const width = maxX - minX + 1\n const height = maxY - minY + 1\n const touchesEdge = minX === 0 || minY === 0 || maxX === w - 1 || maxY === h - 1\n return { x: minX, y: minY, width, height, touchesEdge }\n }\n\n let result = null\n try {\n let rendered = renderWithMargin(initialMargin)\n if (!rendered) {\n console.error('[PixiRenderer.exportImage] Initial render failed')\n return null\n }\n\n let bounds = scanAlphaBounds(rendered.canvas, rendered.width, rendered.height)\n if (!bounds) {\n if (hasLayerDocumentForExport && rendered.childCount === 0) {\n bounds = {\n x: rendered.margin,\n y: rendered.margin,\n width: srcW,\n height: srcH,\n touchesEdge: false,\n }\n } else {\n console.warn('[PixiRenderer.exportImage] No non-transparent content to export')\n return null\n }\n }\n\n // If the bbox reached an edge, a filter bled further than we planned.\n // Grow the margin and retry once (double is enough for any sane filter;\n // more than that and we'd hit the pixel budget anyway).\n if (bounds.touchesEdge && initialMargin < srcW && initialMargin < srcH) {\n const retryMargin = Math.max(initialMargin * 4, 256)\n const retried = renderWithMargin(retryMargin)\n if (retried) {\n const retriedBounds = scanAlphaBounds(retried.canvas, retried.width, retried.height)\n if (retriedBounds && !retriedBounds.touchesEdge) {\n rendered = retried\n bounds = retriedBounds\n } else {\n // Second attempt still clipped — accept the larger result even if\n // still touching the edge. Better than losing the first pass.\n rendered = retried\n bounds = retriedBounds ?? bounds\n }\n }\n }\n\n // Apply maxEdge / maxPixels to the trimmed bbox.\n let outW = bounds.width\n let outH = bounds.height\n if (maxEdge > 0) {\n const edge = Math.max(outW, outH)\n let s = maxEdge / edge\n if (dontUpscale) s = Math.min(1, s)\n outW = Math.max(1, Math.round(outW * s))\n outH = Math.max(1, Math.round(outH * s))\n }\n if (maxPixels > 0 && outW * outH > maxPixels) {\n const s = Math.sqrt(maxPixels / (outW * outH))\n outW = Math.max(1, Math.floor(outW * s))\n outH = Math.max(1, Math.floor(outH * s))\n }\n\n // Copy the bbox region from the large canvas onto a correctly-sized\n // output canvas, scaling if the cap forced a downscale.\n const outCanvas = document.createElement('canvas')\n outCanvas.width = outW\n outCanvas.height = outH\n const outCtx = outCanvas.getContext('2d')\n if (!outCtx) {\n console.error('[PixiRenderer.exportImage] Failed to get 2d context for output canvas')\n return null\n }\n outCtx.drawImage(\n rendered.canvas,\n bounds.x, bounds.y, bounds.width, bounds.height,\n 0, 0, outW, outH\n )\n\n result = outCanvas.toDataURL(`image/${format}`, quality)\n\n // Record the post-trim export dimensions so getExportDimensions()\n // returns what was actually saved (including or excluding bleed).\n this._lastExportDimensions = { width: outW, height: outH }\n\n return result || null\n } catch (e) {\n console.error('[PixiRenderer.exportImage] Export failed:', e)\n return null\n }\n // No live sprite mutation — export uses an isolated container/sprite\n // that's destroyed inside renderWithMargin's own finally block.\n }\n\n /**\n * Get the actual export dimensions at the current filter state.\n *\n * When an export has been performed, returns the exact trimmed dimensions\n * from that export. Otherwise falls back to source texture size plus a\n * rough filter-padding estimate — the real dimensions are only known for\n * certain after exportImage() runs the bbox scan.\n *\n * @returns {{ width: number, height: number }}\n */\n getExportDimensions() {\n if (this._lastExportDimensions) {\n return { ...this._lastExportDimensions }\n }\n\n const tex = this.originalTexture\n const w = Math.round(tex?.width || 0)\n const h = Math.round(tex?.height || 0)\n\n let padding = 0\n const filters = this.sprite?.filters\n if (Array.isArray(filters)) {\n for (const f of filters) {\n if (!f) continue\n const p = typeof f._exportPadding === 'number'\n ? f._exportPadding\n : (typeof f.padding === 'number' ? f.padding : 0)\n if (p > padding) padding = p\n }\n }\n\n return {\n width: w + 2 * padding,\n height: h + 2 * padding,\n }\n }\n\n /**\n * Export the current image as a Blob with correct dimensions.\n * @param {string} format - 'png' or 'jpeg'\n * @param {number} quality - Quality for jpeg (0-1)\n * @param {{ maxPixels?: number, maxEdge?: number, dontUpscale?: boolean }} [options]\n * @returns {Promise<{ blob: Blob, width: number, height: number } | null>}\n */\n async exportBlob(format = 'png', quality = 0.92, options = {}) {\n const dataUrl = this.exportImage(\n format,\n quality,\n options.maxEdge ?? 0,\n options.dontUpscale !== false,\n options.maxPixels ?? 0\n )\n if (!dataUrl) return null\n\n const response = await fetch(dataUrl)\n const blob = await response.blob()\n const dims = this.getExportDimensions()\n\n return { blob, width: dims.width, height: dims.height }\n }\n\n /**\n * Resize renderer to container\n * @param {HTMLElement} container\n */\n resizeTo(container) {\n if (!this.app?.renderer || !container) return\n\n const w = container.clientWidth\n const h = container.clientHeight\n\n // Skip if dimensions haven't changed (prevents ResizeObserver loop)\n if (w === Math.round(this.app.screen.width) && h === Math.round(this.app.screen.height)) return\n // Skip zero-size containers (modal not yet laid out)\n if (w <= 0 || h <= 0) return\n\n this.app.renderer.resize(w, h)\n\n if (this.originalTexture && this.sprite) {\n const prevCenter = {\n x: this.sprite.x + this.sprite.width / 2,\n y: this.sprite.y + this.sprite.height / 2\n }\n this.fitScale = this.getFitScaleFor(this.originalTexture)\n this.applyViewTransform({ center: prevCenter })\n }\n }\n\n /**\n * Set background color\n * @param {number} color - Hex color\n */\n setBackgroundColor(color) {\n if (this.app?.renderer?.background) {\n this.app.renderer.background.color = color\n }\n }\n\n /**\n * Load an image URL into an HTMLImageElement.\n * Handles blob:, data:, and http(s): URLs.\n * @param {string} url\n * @returns {Promise<HTMLImageElement|null>}\n */\n _loadImageElement(url) {\n return new Promise((resolve) => {\n const img = new Image()\n // Set crossOrigin for http(s) URLs to avoid tainted canvas on export\n if (typeof url === 'string' && /^https?:\\/\\//.test(url)) {\n img.crossOrigin = 'anonymous'\n }\n img.onload = () => resolve(img)\n img.onerror = () => {\n console.error('[PixiRenderer] Image load failed:', url?.substring(0, 100))\n resolve(null)\n }\n img.src = url\n })\n }\n\n /**\n * Clean up and destroy\n */\n destroy() {\n this._destroyed = true\n this._mountToken += 1\n this._mountPromise = null\n this._destroyLayerDisplayObjects()\n if (this.app) {\n try {\n this.app.destroy(true, { children: true, texture: true })\n } catch (_e) {\n // PIXI may throw if textures were already released during export.\n // Safe to ignore — we're destroying the renderer anyway.\n }\n this.app = null\n }\n this.sprite = null\n this.originalTexture = null\n this.baseTexture = null\n this._container = null\n this.removeAllListeners()\n }\n}\n","/**\n * SVG Icons for the vanilla image editor\n * Uses inline SVG for zero dependencies\n */\n\n// Navigation icons\nexport const chevronLeft = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"48\" d=\"M328 112L184 256l144 144\"/></svg>`\n\nexport const chevronRight = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"48\" d=\"M184 112l144 144-144 144\"/></svg>`\n\n// Zoom icons\nexport const zoomIn = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M221.09 64a157.09 157.09 0 10157.09 157.09A157.1 157.1 0 00221.09 64z\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M338.29 338.29L448 448M256 184v74m-37-37h74\"/></svg>`\n\nexport const zoomOut = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M221.09 64a157.09 157.09 0 10157.09 157.09A157.1 157.1 0 00221.09 64z\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M338.29 338.29L448 448M184 221h74\"/></svg>`\n\nexport const expand = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M432 320v112H320M80 192V80h112M320 80h112v112M192 432H80V320\"/></svg>`\n\n// Editor mode icons\nexport const settings = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M262.29 192.31a64 64 0 1057.4 57.4 64.13 64.13 0 00-57.4-57.4z\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M416.39 256a154.34 154.34 0 01-1.53 20.79l45.21 35.46a10.81 10.81 0 012.45 13.75l-42.77 74a10.81 10.81 0 01-13.14 4.59l-44.9-18.08a16.11 16.11 0 00-15.17 1.75A164.48 164.48 0 01325 400.8a15.94 15.94 0 00-8.82 12.14l-6.73 47.89a11.08 11.08 0 01-10.68 9.17h-85.54a11.11 11.11 0 01-10.69-8.87l-6.72-47.82a16.07 16.07 0 00-9-12.22 155.3 155.3 0 01-21.46-12.57 16 16 0 00-15.11-1.71l-44.89 18.07a10.81 10.81 0 01-13.14-4.58l-42.77-74a10.8 10.8 0 012.45-13.75l38.21-30a16.05 16.05 0 006-14.08c-.36-4.17-.58-8.33-.58-12.5s.21-8.27.58-12.35a16 16 0 00-6.07-13.94l-38.19-30A10.81 10.81 0 0149.48 186l42.77-74a10.81 10.81 0 0113.14-4.59l44.9 18.08a16.11 16.11 0 0015.17-1.75A164.48 164.48 0 01187 111.2a15.94 15.94 0 008.82-12.14l6.73-47.89A11.08 11.08 0 01213.23 42h85.54a11.11 11.11 0 0110.69 8.87l6.72 47.82a16.07 16.07 0 009 12.22 155.3 155.3 0 0121.46 12.57 16 16 0 0015.11 1.71l44.89-18.07a10.81 10.81 0 0113.14 4.58l42.77 74a10.8 10.8 0 01-2.45 13.75l-38.21 30a16.05 16.05 0 00-6.05 14.08c.33 4.14.55 8.3.55 12.47z\"/></svg>`\n\nexport const water = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M400 320c0 88.37-55.63 144-144 144s-144-55.63-144-144c0-94.83 103.23-222.85 134.89-259.88a12 12 0 0118.23 0C296.77 97.15 400 225.17 400 320z\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M344 328a72 72 0 01-72 72\"/></svg>`\n\nexport const colorPalette = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-miterlimit=\"10\" stroke-width=\"32\" d=\"M430.11 347.9c-6.6-6.1-16.3-7.6-24.6-9-11.5-1.9-15.9-4-22.6-10-14.3-12.7-14.3-31.1 0-43.8l30.3-26.9c46.4-41 46.4-108.2 0-149.2-34.2-30.1-80.1-45-127.8-45-55.7 0-113.9 20.3-158.8 60.1-83.5 73.8-83.5 194.7 0 268.5 41.5 36.7 97.5 55 152.9 55.4h1.7c55.4 0 110-17.9 148.8-52.4 14.4-12.7 11.99-36.6.1-47.7z\"/><circle cx=\"144\" cy=\"208\" r=\"32\"/><circle cx=\"152\" cy=\"311\" r=\"32\"/><circle cx=\"224\" cy=\"144\" r=\"32\"/><circle cx=\"256\" cy=\"367\" r=\"32\"/><circle cx=\"328\" cy=\"144\" r=\"32\"/></svg>`\n\nexport const sparkles = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M259.92 262.91L216.4 149.77a9 9 0 00-16.8 0l-43.52 113.14a9 9 0 01-5.17 5.17L37.77 311.6a9 9 0 000 16.8l113.14 43.52a9 9 0 015.17 5.17l43.52 113.14a9 9 0 0016.8 0l43.52-113.14a9 9 0 015.17-5.17l113.14-43.52a9 9 0 000-16.8l-113.14-43.52a9 9 0 01-5.17-5.17zM108 68L88 16 68 68 16 88l52 20 20 52 20-52 52-20-52-20zM426.67 117.33L400 48l-26.67 69.33L304 144l69.33 26.67L400 240l26.67-69.33L496 144l-69.33-26.67z\"/></svg>`\n\nexport const move = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M176 112l80-80 80 80M255.98 32l.02 448M176 400l80 80 80-80M400 176l80 80-80 80M112 176l-80 80 80 80M32 256h448\"/></svg>`\n\nexport const brush = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M452.37 59.63h0a40.49 40.49 0 00-57.26 0L184 294.74c23.08 4.7 46.12 27.29 49.26 49.26l219.11-227.11a40.49 40.49 0 000-57.26z\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M138 336c-29.88 0-54 24.5-54 54.86 0 23.95-20.67 36.57-34 44.78 15.08 8.08 32.23 12.36 50 12.36 48.49 0 88-38.89 88-88 0-30.36-24.12-54-50-54z\"/></svg>`\n\nexport const flash = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M315.27 33L96 304h128l-31.51 173.23a2.36 2.36 0 002.33 2.77h0a2.36 2.36 0 001.89-.95L416 208H288l31.66-173.25a2.45 2.45 0 00-2.44-2.75h0a2.42 2.42 0 00-1.95 1z\"/></svg>`\n\nexport const crop = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M144 48v272a48 48 0 0048 48h272\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M368 464V192a48 48 0 00-48-48H48\"/></svg>`\n\n// Action icons\nexport const save = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M380.93 57.37A32 32 0 00358.3 48H94.22A46.21 46.21 0 0048 94.22v323.56A46.21 46.21 0 0094.22 464h323.56A46.36 46.36 0 00464 417.78V153.7a32 32 0 00-9.37-22.63zM256 416a64 64 0 1164-64 63.92 63.92 0 01-64 64zm48-224H112a16 16 0 01-16-16v-64a16 16 0 0116-16h192a16 16 0 0116 16v64a16 16 0 01-16 16z\"/></svg>`\n\nexport const close = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M368 368L144 144M368 144L144 368\"/></svg>`\n\nexport const refresh = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M320 146s24.36-12-64-12a160 160 0 10160 160\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M256 58l80 80-80 80\"/></svg>`\n\nexport const rotateCcw = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M176 112l-64 64 64 64\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M112 176h176a112 112 0 110 224H160\"/></svg>`\n\nexport const rotateCw = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M336 112l64 64-64 64\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M400 176H224a112 112 0 100 224h128\"/></svg>`\n\nexport const trash = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M112 112l20 320c.95 18.49 14.4 32 32 32h184c17.67 0 30.87-13.51 32-32l20-320\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-miterlimit=\"10\" stroke-width=\"32\" d=\"M80 112h352\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M192 112V72h0a23.93 23.93 0 0124-24h80a23.93 23.93 0 0124 24h0v40M256 176v224M184 176l8 224M328 176l-8 224\"/></svg>`\n\nexport const openFolder = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M64 192v-72a40 40 0 0140-40h75.89a40 40 0 0122.19 6.72l27.84 18.56a40 40 0 0022.19 6.72H408a40 40 0 0140 40v40\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M479.9 226.55L463.68 392a40 40 0 01-39.93 40H88.25a40 40 0 01-39.93-40L32.1 226.55A32 32 0 0164 192h384.1a32 32 0 0131.8 34.55z\"/></svg>`\n\nexport const checkmark = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M416 128L192 384l-96-96\"/></svg>`\n\nexport const layers = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M48 256l208 112 208-112-208-112L48 256z\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M48 336l208 112 208-112M48 176L256 64l208 112\"/></svg>`\n\nexport const eye = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M48 256s80-144 208-144 208 144 208 144-80 144-208 144S48 256 48 256z\"/><circle cx=\"256\" cy=\"256\" r=\"64\" fill=\"none\" stroke=\"currentColor\" stroke-linejoin=\"round\" stroke-width=\"32\"/></svg>`\n\nexport const eyeOff = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M432 448L80 64M160 160c-67.2 36.8-112 96-112 96s80 144 208 144c35.8 0 68.4-11.3 96-28.1M224.4 113.8A199.6 199.6 0 01256 112c128 0 208 144 208 144a334.2 334.2 0 01-65.1 78.6\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M213.3 213.3A64 64 0 10298.7 298.7\"/></svg>`\n\nexport const lockClosed = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><rect x=\"96\" y=\"208\" width=\"320\" height=\"240\" rx=\"32\" fill=\"none\" stroke=\"currentColor\" stroke-linejoin=\"round\" stroke-width=\"32\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M160 208v-64a96 96 0 01192 0v64\"/></svg>`\n\nexport const lockOpen = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><rect x=\"96\" y=\"208\" width=\"320\" height=\"240\" rx=\"32\" fill=\"none\" stroke=\"currentColor\" stroke-linejoin=\"round\" stroke-width=\"32\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M160 208v-64a96 96 0 01168-63.7\"/></svg>`\n\nexport const pencil = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M96 352l-32 96 96-32L421.5 154.5a45.3 45.3 0 000-64h0a45.3 45.3 0 00-64 0L96 352z\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M320 128l64 64\"/></svg>`\n\nexport const duplicate = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><rect x=\"144\" y=\"144\" width=\"288\" height=\"288\" rx=\"32\" fill=\"none\" stroke=\"currentColor\" stroke-linejoin=\"round\" stroke-width=\"32\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M80 368V112a32 32 0 0132-32h256\"/></svg>`\n\nexport const text = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M96 112V80h320v32M256 80v352M184 432h144\"/></svg>`\n\n// Shape icons\nexport const square = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><rect x=\"64\" y=\"64\" width=\"384\" height=\"384\" rx=\"48\" fill=\"none\" stroke=\"currentColor\" stroke-linejoin=\"round\" stroke-width=\"32\"/></svg>`\n\nexport const circle = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><circle cx=\"256\" cy=\"256\" r=\"208\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\"/></svg>`\n\nexport const freeform = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M80 96h64l64 320h64l64-160h96\"/></svg>`\n\n// Theme icons\nexport const sunny = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-miterlimit=\"10\" stroke-width=\"32\" d=\"M256 48v48M256 416v48M403.08 108.92l-33.94 33.94M142.86 369.14l-33.94 33.94M464 256h-48M96 256H48M403.08 403.08l-33.94-33.94M142.86 142.86l-33.94-33.94\"/><circle cx=\"256\" cy=\"256\" r=\"80\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-miterlimit=\"10\" stroke-width=\"32\"/></svg>`\n\nexport const moon = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M160 136c0-30.62 4.51-61.61 16-88C99.57 81.27 48 159.32 48 248c0 119.29 96.71 216 216 216 88.68 0 166.73-51.57 200-128-26.39 11.49-57.38 16-88 16-119.29 0-216-96.71-216-216z\"/></svg>`\n\n// Category-specific icons\nexport const film = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M436 80H76a44.05 44.05 0 00-44 44v264a44.05 44.05 0 0044 44h360a44.05 44.05 0 0044-44V124a44.05 44.05 0 00-44-44z\"/><circle cx=\"256\" cy=\"256\" r=\"80\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\"/><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M220 80v56M292 80v56M220 376v56M292 376v56M80 144h56M80 224h56M80 304h56M376 144h56M376 224h56M376 304h56\"/></svg>`\n\nexport const contrast = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><circle cx=\"256\" cy=\"256\" r=\"208\" fill=\"none\" stroke=\"currentColor\" stroke-linejoin=\"round\" stroke-width=\"32\"/><path fill=\"currentColor\" d=\"M256 464c-114.88 0-208-93.12-208-208S141.12 48 256 48z\"/></svg>`\n\n// Filter mode icon (for toolbar)\nexport const filter = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M32 144h448M112 256h288M208 368h96\"/></svg>`\n\n// Person/portrait icon (for background removal)\nexport const personOutline = `<svg viewBox=\"0 0 512 512\" width=\"20\" height=\"20\"><path fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"32\" d=\"M344 144c-3.92 52.87-44 96-88 96s-84.15-43.12-88-96c-4-55 35-96 88-96s92 42 88 96z\"/><path fill=\"none\" stroke=\"currentColor\" stroke-miterlimit=\"10\" stroke-width=\"32\" d=\"M256 304c-87 0-175.3 48-191.64 138.6C62.39 453.52 68.57 464 80 464h352c11.44 0 17.62-10.48 15.65-21.4C431.3 352 343 304 256 304z\"/></svg>`\n","/**\n * Editor category single source of truth.\n *\n * UI categories (what the user sees) are fewer than registry categories\n * (what the filter registry uses internally). For example, Color Matrix\n * lives in the 'advanced' registry category but appears under the 'adjust'\n * UI category. Keep these two lookups in sync by deriving both from the\n * EDITOR_CATEGORIES array below.\n */\nimport * as icons from '../icons/icons.js'\n\n/**\n * Ordered list of UI categories shown in the editor.\n * Each entry may include a list of registry categories that feed it.\n * Categories without a `registryCategories` field (e.g. 'crop') don't\n * consume filters from the registry.\n *\n * @typedef {Object} EditorCategory\n * @property {string} id - UI category id\n * @property {string} name - Display name\n * @property {string} [icon] - SVG icon markup\n * @property {string[]} [registryCategories] - Registry categories aggregated under this UI category\n */\n\n/** @type {EditorCategory[]} */\nexport const EDITOR_CATEGORIES = [\n {\n id: 'adjust',\n name: 'Adjust',\n icon: icons.settings,\n registryCategories: ['adjust', 'advanced'], // adjustment, adjustmentAdvanced, alpha, colorMatrix\n },\n {\n id: 'blur',\n name: 'Blur',\n icon: icons.water,\n registryCategories: ['blur'],\n },\n {\n id: 'color',\n name: 'Color',\n icon: icons.colorPalette,\n registryCategories: ['color'],\n },\n {\n id: 'effects',\n name: 'Effects',\n icon: icons.sparkles,\n registryCategories: ['effects'],\n },\n {\n id: 'distortion',\n name: 'Distortion',\n icon: icons.move,\n registryCategories: ['distortion'],\n },\n {\n id: 'light',\n name: 'Light',\n icon: icons.flash,\n registryCategories: ['light'],\n },\n {\n id: 'stylize',\n name: 'Stylize',\n icon: icons.film,\n registryCategories: ['stylize'],\n },\n {\n id: 'text',\n name: 'Text',\n icon: icons.text,\n },\n {\n id: 'background',\n name: 'Background',\n icon: icons.brush,\n },\n {\n id: 'layers',\n name: 'Layers',\n icon: icons.layers,\n },\n {\n id: 'crop',\n name: 'Crop',\n icon: icons.crop,\n },\n]\n\n/**\n * UI category id -> list of registry categories it aggregates.\n * Used by FilterManager.getFiltersByCategory.\n */\nexport const UI_TO_REGISTRY = Object.fromEntries(\n EDITOR_CATEGORIES\n .filter(c => Array.isArray(c.registryCategories) && c.registryCategories.length > 0)\n .map(c => [c.id, c.registryCategories])\n)\n\n/**\n * Registry category id -> UI category id.\n * Used when we have a filter's registry category and need to highlight the\n * right UI chip (e.g. colorMatrix.category === 'advanced' -> UI 'adjust').\n */\nexport const REGISTRY_TO_UI = Object.fromEntries(\n EDITOR_CATEGORIES\n .filter(c => Array.isArray(c.registryCategories))\n .flatMap(c => c.registryCategories.map(rc => [rc, c.id]))\n)\n\n/**\n * Resolve a UI category id to its list of registry categories.\n * Falls back to treating the id as a registry category if not found.\n *\n * @param {string} uiCategoryId\n * @returns {string[]}\n */\nexport function getRegistryCategoriesForUi(uiCategoryId) {\n return UI_TO_REGISTRY[uiCategoryId] || [uiCategoryId]\n}\n\n/**\n * Resolve a filter's registry category to its UI category id.\n * Falls back to the registry category itself if no mapping exists.\n *\n * @param {string} registryCategoryId\n * @returns {string}\n */\nexport function getUiCategoryForRegistry(registryCategoryId) {\n return REGISTRY_TO_UI[registryCategoryId] || registryCategoryId\n}\n","/**\n * Filter Manager for the vanilla image editor\n * Manages filter creation, application, and state\n */\nimport { EventEmitter } from '../core/EventEmitter.js'\nimport { getRegistryCategoriesForUi } from '../filters/categories.js'\n\nexport class FilterManager extends EventEmitter {\n constructor(state, pixiRenderer) {\n super()\n this.state = state\n this.renderer = pixiRenderer\n this.instances = {} // Filter instances by ID\n this._filterRegistry = null\n }\n\n /**\n * Set the filter registry (from mediables/filters)\n * @param {Object} registry - { getFilter, getAllFilters, getFiltersByCategory }\n */\n setRegistry(registry) {\n this._filterRegistry = registry\n this.renderer?.setLayerFilterFactory?.((layer, context) => this.createFiltersForLayer(layer, context))\n }\n\n /**\n * Get filter definition by ID\n * @param {string} filterId\n * @returns {Object|null}\n */\n getFilterDef(filterId) {\n if (!this._filterRegistry) {\n console.warn('[FilterManager] No filter registry set')\n return null\n }\n return this._filterRegistry.getFilter(filterId)\n }\n\n /**\n * Get all filters\n * @returns {Array}\n */\n getAllFilters() {\n return this._filterRegistry?.getAllFilters() || []\n }\n\n /**\n * Get filters by category\n * Maps UI category names to registry category names\n * @param {string} category - UI category ID\n * @returns {Array}\n */\n getFiltersByCategory(category) {\n if (!this._filterRegistry) return []\n\n // Resolve the UI category to its list of registry categories\n const registryCategories = getRegistryCategoriesForUi(category)\n\n // Collect filters from all matching registry categories\n const filters = []\n const seenIds = new Set()\n\n for (const registryCat of registryCategories) {\n const catFilters = this._filterRegistry.getFiltersByCategory(registryCat) || []\n for (const filter of catFilters) {\n // Avoid duplicates\n if (!seenIds.has(filter.id)) {\n seenIds.add(filter.id)\n filters.push(filter)\n }\n }\n }\n\n return filters\n }\n\n /**\n * Normalize UI values from various input types\n * @param {*} value\n * @returns {*}\n */\n _normalizeValue(value) {\n if (Array.isArray(value)) {\n // Array of objects (like colorStops) - keep as-is\n if (value.length > 0 && typeof value[0] === 'object') return value\n // Range value - take first number\n return Number(value[0] ?? 0)\n }\n if (typeof value === 'string') {\n // Hex color - keep as string\n if (value.startsWith('#')) return value\n // Numeric string\n return value.trim() === '' ? 0 : Number(value)\n }\n return value\n }\n\n /**\n * Get a deep property from an object.\n * Returns undefined if any segment along the path is missing.\n * @param {Object} target\n * @param {string} path\n * @returns {*}\n */\n _getDeepProp(target, path) {\n if (!target || !path) return undefined\n\n if (!path.includes('.') && !path.includes('[')) {\n return target[path]\n }\n\n const tokens = path.replace(/\\[(\\d+)\\]/g, '.$1').split('.').filter(Boolean)\n let obj = target\n for (const key of tokens) {\n if (obj == null) return undefined\n obj = obj[key]\n }\n return obj\n }\n\n /**\n * Set a deep property on an object\n * @param {Object} target\n * @param {string} path\n * @param {*} value\n */\n _setDeepProp(target, path, value) {\n if (!target || !path) return\n\n if (!path.includes('.') && !path.includes('[')) {\n target[path] = value\n return\n }\n\n const tokens = path.replace(/\\[(\\d+)\\]/g, '.$1').split('.').filter(Boolean)\n let obj = target\n for (let i = 0; i < tokens.length - 1; i++) {\n const key = tokens[i]\n if (!(key in obj)) return\n obj = obj[key]\n if (obj == null) return\n }\n const last = tokens[tokens.length - 1]\n obj[last] = value\n }\n\n /**\n * Initialize filter values from definition defaults.\n * Seeds BOTH defaultParams (e.g. colorStops) and control defaults so that\n * filters with dynamic parameters not backed by a static control still have\n * their state populated.\n * @param {string} filterId\n */\n initializeValues(filterId, expectedLayerId = undefined) {\n const def = this.getFilterDef(filterId)\n if (!def) return\n\n const values = this.state.getFilterValues(filterId)\n const controlIds = new Set((def.controls || []).map(c => c.id))\n\n // Seed defaultParams entries not covered by any control (e.g. colorStops)\n if (def.defaultParams) {\n for (const [key, value] of Object.entries(def.defaultParams)) {\n if (!controlIds.has(key) && !(key in values)) {\n this.state.setFilterValue(filterId, key, value, expectedLayerId)\n }\n }\n }\n\n // Seed static control defaults\n if (def.controls) {\n def.controls.forEach(ctl => {\n if (!(ctl.id in values)) {\n this.state.setFilterValue(filterId, ctl.id, ctl.default, expectedLayerId)\n }\n })\n }\n }\n\n /**\n * Reset all filter values to their defaults (unconditionally).\n * Resets BOTH defaultParams and control defaults.\n * @param {string} filterId\n */\n resetValues(filterId, expectedLayerId = undefined) {\n const def = this.getFilterDef(filterId)\n if (!def) return\n\n const controlIds = new Set((def.controls || []).map(c => c.id))\n\n // Reset defaultParams entries (including dynamic state like colorStops)\n if (def.defaultParams) {\n for (const [key, value] of Object.entries(def.defaultParams)) {\n if (!controlIds.has(key)) {\n this.state.setFilterValue(filterId, key, value, expectedLayerId)\n }\n }\n }\n\n // Reset static controls\n if (def.controls) {\n def.controls.forEach(ctl => {\n this.state.setFilterValue(filterId, ctl.id, ctl.default, expectedLayerId)\n })\n }\n }\n\n /**\n * Toggle a filter on/off\n * @param {string} filterId\n * @param {boolean} enabled\n */\n toggle(filterId, enabled, expectedLayerId = undefined) {\n const toggled = this.state.toggleFilter(filterId, enabled, expectedLayerId)\n if (toggled === false) return false\n\n if (enabled) {\n this.initializeValues(filterId, expectedLayerId)\n } else {\n delete this.instances[filterId]\n }\n\n this.applyFilters()\n this.emit('filterToggled', { filterId, enabled })\n\n return true\n }\n\n /**\n * Update a filter control value.\n *\n * The `controlId` may be either a static control id (from def.controls) or a\n * dynamic path (e.g. 'colorStops[0].color') produced by a filter's\n * getDynamicControls(). Static control ids are stored in state keyed by the\n * control id. Dynamic paths are NOT stored as flat path keys — instead, we\n * apply them to the live instance and then sync the filter's serializable\n * params back into state, so recreation via applyFilters() rebuilds from\n * the canonical shape (e.g. a colorStops array, not 'colorStops[0].color').\n *\n * Returns true when the filter was updated in-place. Returns false when the\n * caller should invoke applyFilters() to recreate the filter from state —\n * this happens when the property target is a method (which would be\n * silently corrupted by a direct write) or when the property doesn't exist.\n *\n * @param {string} filterId\n * @param {string} controlId - Static control id or dynamic property path\n * @param {*} value\n * @returns {boolean} True if updated in-place, false if caller must recreate\n */\n updateValue(filterId, controlId, value, expectedLayerId = undefined) {\n const normalized = this._normalizeValue(value)\n\n if (!this._matchesExpectedFilterLayer(expectedLayerId)) return false\n\n const def = this.getFilterDef(filterId)\n const ctl = def?.controls.find(c => c.id === controlId)\n const isDynamicPath = !ctl && (controlId.includes('[') || controlId.includes('.'))\n\n // Store static control values in state keyed by control id. Skip dynamic\n // paths here — we sync state via getSerializableParams after the update.\n if (!isDynamicPath) {\n const stored = this.state.setFilterValue(filterId, controlId, normalized, expectedLayerId)\n if (stored === false) return false\n }\n\n const inst = this.instances[filterId]\n if (!inst) return false\n\n // If controlId matches a static control, use its property; otherwise\n // treat controlId itself as the path (covers dynamic controls).\n const path = ctl?.property || controlId\n\n // 1. Prefer updateUIParam if the filter defines one\n if (typeof inst.updateUIParam === 'function') {\n inst.updateUIParam(path, normalized)\n // After a dynamic-path update, sync the filter's canonical params back\n // to state so recreation sees the edit. We skip this for static paths\n // (cheap tight-loop updates don't need the round-trip).\n if (isDynamicPath && typeof inst.getSerializableParams === 'function') {\n const synced = inst.getSerializableParams()\n for (const [key, val] of Object.entries(synced)) {\n this.state.setFilterValue(filterId, key, val, expectedLayerId)\n }\n }\n return true\n }\n\n // 2. Never overwrite a method on the filter with a value —\n // PIXI's ColorMatrixFilter.brightness etc. are methods, not properties.\n // Silently writing a number there would corrupt the filter.\n const existing = this._getDeepProp(inst, path)\n if (typeof existing === 'function') {\n return false\n }\n\n // 3. Uniforms (WebGL shader inputs)\n if (inst.uniforms && path in inst.uniforms) {\n inst.uniforms[path] = normalized\n return true\n }\n\n // 4. Direct write if the path is a writable non-function property\n if (existing !== undefined) {\n this._setDeepProp(inst, path, normalized)\n return true\n }\n\n // 5. Unknown path — let the caller recreate\n return false\n }\n\n /**\n * Perform a filter action (e.g. addColorStop, removeColorStop) and sync\n * any resulting state changes back into State.js before recreating the\n * filter. Without this sync, recreation via applyFilters() would rebuild\n * from stale state and undo the action.\n *\n * @param {string} filterId\n * @param {string} action - Action name understood by filter.updateUIParam\n */\n performFilterAction(filterId, action, expectedLayerId = undefined) {\n if (!this._matchesExpectedFilterLayer(expectedLayerId)) return false\n\n const inst = this.instances[filterId]\n if (!inst || typeof inst.updateUIParam !== 'function') return false\n\n // 1. Apply the action to the live instance\n inst.updateUIParam(action, true)\n\n // 2. Serialize resulting params back to state so recreation sees them\n if (typeof inst.getSerializableParams === 'function') {\n const params = inst.getSerializableParams()\n for (const [key, value] of Object.entries(params)) {\n this.state.setFilterValue(filterId, key, value, expectedLayerId)\n }\n }\n\n // 3. Re-apply filters (recreation now uses the updated state)\n this.applyFilters()\n this.emit('filterActionPerformed', { filterId, action })\n\n return true\n }\n\n _matchesExpectedFilterLayer(expectedLayerId) {\n if (expectedLayerId === undefined) return true\n\n const document = this._layerDocument()\n if (!document) return true\n\n if (expectedLayerId === null) {\n if (typeof this.state.getActiveFilterLayerId === 'function') {\n return this.state.getActiveFilterLayerId() === null\n }\n\n return this.state.get('activeLayerId') === null\n }\n\n const expected = typeof expectedLayerId === 'string' ? expectedLayerId.trim() : ''\n if (expected === '') return false\n\n if (typeof this.state.getActiveFilterLayerId === 'function') {\n return this.state.getActiveFilterLayerId() === expected\n }\n\n return this.state.get('activeLayerId') === expected\n }\n\n _layerDocument() {\n const document = this.state.getSerializableDocument?.()\n return document && document.version === 2 && Array.isArray(document.layers) ? document : null\n }\n\n _sourceDimensionsForLayer(layer, context = {}) {\n const source = layer?.source || layer?.fill?.source || null\n\n return {\n width: Math.round(Number(context.sourceWidth || source?.originalWidth || this.renderer?.originalTexture?.width || this.renderer?.sprite?.width || 0)),\n height: Math.round(Number(context.sourceHeight || source?.originalHeight || this.renderer?.originalTexture?.height || this.renderer?.sprite?.height || 0)),\n }\n }\n\n _paramsForFilterState(def, filterState, dimensions) {\n const values = filterState?.values && typeof filterState.values === 'object' ? filterState.values : {}\n const params = def.defaultParams ? { ...def.defaultParams } : {}\n\n if (def.controls && Array.isArray(def.controls)) {\n def.controls.forEach(c => {\n const key = c.property || c.id\n params[key] = values[c.id] ?? c.default\n })\n }\n\n const controlIds = new Set((def.controls || []).map(c => c.id))\n for (const [key, value] of Object.entries(values)) {\n if (!controlIds.has(key)) {\n params[key] = value\n }\n }\n\n params._sourceWidth = dimensions.width\n params._sourceHeight = dimensions.height\n\n return params\n }\n\n createFiltersForLayer(layer, context = {}) {\n if (!Array.isArray(layer?.filters) || layer.filters.length === 0) return []\n\n const filters = []\n const activeLayerId = this.state.get('activeLayerId')\n const trackInstances = layer.id === activeLayerId && context.forExport !== true\n const dimensions = this._sourceDimensionsForLayer(layer, context)\n\n for (const filterState of layer.filters) {\n if (!filterState || filterState.enabled === false || typeof filterState.id !== 'string') continue\n\n try {\n const def = this.getFilterDef(filterState.id)\n if (!def?.createFilter || typeof def.createFilter !== 'function') continue\n\n const filter = def.createFilter(this._paramsForFilterState(def, filterState, dimensions))\n if (!filter) continue\n\n let renderFilter = filter\n if (context.forExport === true && typeof filter.createExportFilter === 'function') {\n renderFilter = filter.createExportFilter({\n previewToNativeScale: context.previewToNativeScale || 1,\n }) || filter\n }\n\n filters.push(renderFilter)\n if (trackInstances) {\n this.instances[filterState.id] = filter\n }\n } catch (error) {\n console.error(`[FilterManager] Error creating layer filter ${filterState.id}:`, error)\n }\n }\n\n return filters\n }\n\n /**\n * Apply all active filters to the sprite\n *\n * This method recreates all filter instances on each call to ensure\n * PIXI v8's filter pipeline correctly processes changes.\n */\n applyFilters() {\n const layerDocument = this._layerDocument()\n if (layerDocument) {\n for (const key in this.instances) {\n delete this.instances[key]\n }\n\n this.renderer?.setLayerFilterFactory?.((layer, context) => this.createFiltersForLayer(layer, context))\n const applied = this.renderer?.applyLayerFilters?.(layerDocument)\n this.emit('filtersApplied', {\n count: this.state.get('activeFilters')?.size ?? 0,\n failed: [],\n scopedToLayer: true,\n })\n if (!applied) {\n this.renderer?.render?.()\n }\n return\n }\n\n const sprite = this.renderer.sprite\n if (!sprite) return\n\n // Clear old instances (matches Vue version behavior)\n // PIXI v8 requires fresh instances for reliable filter pipeline processing\n for (const key in this.instances) {\n delete this.instances[key]\n }\n\n const filters = []\n const failedFilters = []\n const activeFilters = this.state.get('activeFilters')\n\n activeFilters.forEach(id => {\n try {\n const def = this.getFilterDef(id)\n if (!def) {\n console.warn(`[FilterManager] Filter definition not found: ${id}`)\n return\n }\n\n if (!def.createFilter || typeof def.createFilter !== 'function') {\n console.warn(`[FilterManager] Filter has no createFilter method: ${id}`)\n return\n }\n\n const values = this.state.getFilterValues(id)\n\n // Start with defaultParams (lowest priority)\n const params = def.defaultParams ? { ...def.defaultParams } : {}\n\n // Overlay static control values, keyed by property (falls back to control id)\n if (def.controls && Array.isArray(def.controls)) {\n def.controls.forEach(c => {\n const key = c.property || c.id\n params[key] = values[c.id] ?? c.default\n })\n }\n\n // Overlay any additional state values that aren't backed by a static\n // control (e.g. colorStops, cssGradient). These are seeded into state\n // by initializeValues and mutated by performFilterAction so they must\n // participate in recreation.\n const controlIds = new Set((def.controls || []).map(c => c.id))\n for (const [key, value] of Object.entries(values)) {\n if (!controlIds.has(key)) {\n params[key] = value\n }\n }\n\n // Provide source-image dimensions as reserved params so filters that\n // accept normalized (0-1) coordinate inputs in their UI can convert\n // to render-target pixel units (pixi-filters' TwistFilter, for\n // example, wants offset/radius in pixels). Filters that don't need\n // this just ignore the fields.\n const sprite = this.renderer.sprite\n const tex = this.renderer.originalTexture\n params._sourceWidth = Math.round(tex?.width || sprite?.width || 0)\n params._sourceHeight = Math.round(tex?.height || sprite?.height || 0)\n\n // Create the filter instance\n const f = def.createFilter(params)\n if (f) {\n filters.push(f)\n this.instances[id] = f\n }\n } catch (error) {\n console.error(`[FilterManager] Error creating filter ${id}:`, error)\n failedFilters.push(id)\n }\n })\n\n // CRITICAL FIX: Clear filters first for PIXI v8 compatibility\n // IMPORTANT: PIXI v8 doesn't recognize filter changes unless we clear first.\n // This forces PIXI to re-process the filter pipeline on the next render.\n try {\n sprite.filters = null\n sprite.filters = filters.length ? filters : null\n this.renderer.render()\n this.emit('filtersApplied', { count: filters.length, failed: failedFilters })\n } catch (error) {\n console.error('[FilterManager] Error applying filters to sprite:', error)\n\n // Try to recover by removing all filters\n try {\n sprite.filters = null\n this.renderer.render()\n } catch (recoveryError) {\n console.error('[FilterManager] Recovery failed:', recoveryError)\n }\n\n this.emit('filtersError', { error, failedFilters })\n }\n\n // Notify about failed filters\n if (failedFilters.length > 0) {\n console.warn(`[FilterManager] ${failedFilters.length} filter(s) failed to create:`, failedFilters)\n }\n }\n\n /**\n * Reset all filters.\n * IMPORTANT: Only clears filter instances and sprite.filters.\n * Does NOT touch baseTexture/originalTexture/sprite — that would\n * destroy any applied crop, which is a separate concern.\n */\n resetAll(expectedLayerId = undefined) {\n // Clear instances\n for (const key in this.instances) {\n delete this.instances[key]\n }\n\n // Reset state. In a layer document this clears the active layer only.\n const reset = this.state.resetFilters(expectedLayerId)\n if (reset === false) return false\n\n const layerDocument = this._layerDocument()\n if (layerDocument) {\n this.applyFilters()\n this.emit('filtersReset')\n return true\n }\n\n // Clear sprite filters only — preserve the current texture/sprite\n if (this.renderer.sprite) {\n this.renderer.sprite.filters = null\n }\n\n this.renderer.render()\n this.emit('filtersReset')\n\n return true\n }\n\n /**\n * Get a filter instance\n * @param {string} filterId\n * @returns {Object|null}\n */\n getInstance(filterId) {\n return this.instances[filterId] || null\n }\n}\n","/**\n * Crop Manager for the vanilla image editor\n * Handles crop mode, overlay rendering, and crop application\n */\nimport { EventEmitter } from '../core/EventEmitter.js'\n\nexport class CropManager extends EventEmitter {\n /**\n * Padding factor for auto-zoom: image will be ~91% of crop size (1/1.1).\n * Higher values = more aggressive zoom-out, more padding around image.\n */\n static AUTO_ZOOM_PADDING = 1.1\n\n /**\n * Minimum interval (ms) between auto-zoom adjustments during drag.\n */\n static AUTO_ZOOM_THROTTLE_MS = 100\n\n constructor(state, pixiRenderer) {\n super()\n this.state = state\n this.renderer = pixiRenderer\n\n // Internal state\n this._overlayCanvas = null\n this._isDragging = false\n this._dragStart = null\n this._dragMode = null\n this._startRect = null\n this._hoverMode = null\n\n // Auto-zoom throttle timestamp\n this._lastAutoZoomCheck = 0\n\n // UI constants\n this.HANDLE_SIZE = 14\n this.EDGE_HIT_PAD = 10\n\n // Bound handlers\n this._onPointerDown = this._handlePointerDown.bind(this)\n this._onPointerMove = this._handlePointerMove.bind(this)\n this._onPointerUp = this._handlePointerUp.bind(this)\n }\n\n /**\n * Set the overlay canvas element\n * @param {HTMLCanvasElement} canvas\n */\n setOverlayCanvas(canvas) {\n this._overlayCanvas = canvas\n }\n\n /**\n * Get aspect ratio value from string\n * @param {string} aspect\n * @returns {number|null}\n */\n _getAspectRatio(aspect) {\n if (!aspect || aspect === 'free') return null\n\n const aspectMap = {\n '1:1': 1,\n '4:3': 4/3,\n '16:9': 16/9,\n '3:2': 3/2,\n '2:3': 2/3\n }\n\n if (aspectMap[aspect] !== undefined) return aspectMap[aspect]\n\n // Parse custom ratio strings like '3:1', '21:9'\n const parts = aspect.split(':')\n if (parts.length === 2) {\n const w = parseFloat(parts[0])\n const h = parseFloat(parts[1])\n if (Number.isFinite(w) && Number.isFinite(h) && h > 0) {\n return w / h\n }\n }\n\n return null\n }\n\n /**\n * Return the numeric aspect ratio that the crop rect must obey, accounting\n * for both the shape (circle/square force 1:1) and the explicit aspect\n * setting. Returns null when the crop is free-form.\n */\n _getEffectiveTargetAspect() {\n const shape = this.state.get('crop.shape')\n if (shape === 'circle' || shape === 'square') return 1\n const aspect = this.state.get('crop.aspect')\n if (!aspect || aspect === 'free') return null\n return this._getAspectRatio(aspect)\n }\n\n /**\n * Apply aspect ratio constraint to crop rect. Honours shape (circle/square\n * force 1:1) so the post-drag correction agrees with the effective ratio\n * the edge-handle drag code targets.\n */\n applyAspectRatio() {\n const rect = this.state.get('crop.rect')\n if (!rect) return\n\n const targetAspect = this._getEffectiveTargetAspect()\n if (!targetAspect) return\n\n const currentAspect = rect.width / rect.height\n if (Math.abs(currentAspect - targetAspect) < 0.01) return\n\n if (currentAspect > targetAspect) {\n rect.width = rect.height * targetAspect\n } else {\n rect.height = rect.width / targetAspect\n }\n\n this.state.set('crop.rect', rect)\n }\n\n /**\n * Constrain crop rect to bounds\n */\n constrainCropRect() {\n const rect = this.state.get('crop.rect')\n if (!rect) return\n\n const sprite = this.renderer.sprite\n const app = this.renderer.app\n if (!sprite || !app) return\n\n const shape = this.state.get('crop.shape')\n\n // Keep crop bounds in screen space. This preserves the established editor\n // behavior where filters can intentionally render beyond the source image.\n const bounds = { x: 0, y: 0, w: app.screen.width, h: app.screen.height }\n\n rect.x = Math.max(bounds.x, Math.min(rect.x, bounds.x + bounds.w - rect.width))\n rect.y = Math.max(bounds.y, Math.min(rect.y, bounds.y + bounds.h - rect.height))\n rect.width = Math.min(rect.width, bounds.w)\n rect.height = Math.min(rect.height, bounds.h)\n\n // Keep circle/square 1:1\n if (shape !== 'free' || this.state.get('crop.aspect') === '1:1') {\n const side = Math.min(rect.width, rect.height)\n rect.width = side\n rect.height = side\n }\n\n this.state.set('crop.rect', rect)\n }\n\n /**\n * Calculate the target zoom level for auto-zoom when crop exceeds image bounds.\n * Pure function — returns the new zoom level, or null if no adjustment is needed.\n *\n * @param {{ width: number, height: number }} cropRect - Current crop dimensions\n * @param {number} spriteWidth - Current sprite width on screen\n * @param {number} spriteHeight - Current sprite height on screen\n * @param {number} texWidth - Original texture width (pixels)\n * @param {number} texHeight - Original texture height (pixels)\n * @param {number} fitScale - Current fit-to-screen scale\n * @param {number} currentZoom - Current zoom level\n * @returns {number|null} Target zoom level, or null if no zoom needed\n */\n static calcAutoZoom(cropRect, spriteWidth, spriteHeight, texWidth, texHeight, fitScale, currentZoom) {\n if (!cropRect || !texWidth || !texHeight || !fitScale) return null\n\n const overflowW = cropRect.width > spriteWidth\n const overflowH = cropRect.height > spriteHeight\n\n if (!overflowW && !overflowH) return null\n\n const pad = CropManager.AUTO_ZOOM_PADDING\n let targetZoom = currentZoom\n\n // Only zoom out for the dimension(s) where overflow occurs\n if (overflowW) {\n const zoomForW = cropRect.width / (pad * texWidth * fitScale)\n targetZoom = Math.min(targetZoom, zoomForW)\n }\n if (overflowH) {\n const zoomForH = cropRect.height / (pad * texHeight * fitScale)\n targetZoom = Math.min(targetZoom, zoomForH)\n }\n\n // Clamp to minimum zoom\n targetZoom = Math.max(0.1, targetZoom)\n\n // Only return if this would actually zoom out (threshold avoids micro-adjustments)\n if (targetZoom >= currentZoom - 0.01) return null\n\n return targetZoom\n }\n\n /**\n * Check if auto-zoom is needed and apply it (throttled).\n * Called during crop resize drags when autoZoomOnCropOverflow is enabled.\n */\n _checkAutoZoom() {\n if (!this.state.get('autoZoomOnCropOverflow')) return\n\n // Throttle to avoid excessive zoom adjustments during drag\n const now = Date.now()\n if (now - this._lastAutoZoomCheck < CropManager.AUTO_ZOOM_THROTTLE_MS) return\n this._lastAutoZoomCheck = now\n\n const rect = this.state.get('crop.rect')\n const sprite = this.renderer.sprite\n const tex = this.renderer.originalTexture\n if (!rect || !sprite || !tex) return\n\n const targetZoom = CropManager.calcAutoZoom(\n rect,\n sprite.width,\n sprite.height,\n tex.width,\n tex.height,\n this.renderer.fitScale,\n this.renderer.zoom\n )\n\n if (targetZoom !== null) {\n this.renderer.setZoom(targetZoom, { keepCenter: true })\n }\n }\n\n /**\n * Draw crop overlay on canvas\n */\n drawOverlay() {\n const overlay = this._overlayCanvas\n if (!overlay) return\n\n const app = this.renderer.app\n if (!app) return\n\n const cv = app.canvas\n const cssW = cv.clientWidth\n const cssH = cv.clientHeight\n const dpr = window.devicePixelRatio || 1\n\n // DPR-aware canvas sizing\n if (overlay.width !== Math.max(1, Math.floor(cssW * dpr)) ||\n overlay.height !== Math.max(1, Math.floor(cssH * dpr))) {\n overlay.width = Math.max(1, Math.floor(cssW * dpr))\n overlay.height = Math.max(1, Math.floor(cssH * dpr))\n overlay.style.width = cssW + 'px'\n overlay.style.height = cssH + 'px'\n }\n\n const ctx = overlay.getContext('2d')\n if (!ctx) return\n\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0)\n ctx.clearRect(0, 0, cssW, cssH)\n\n // Dark overlay\n ctx.fillStyle = 'rgba(0, 0, 0, 0.5)'\n ctx.fillRect(0, 0, cssW, cssH)\n\n const rect = this.state.get('crop.rect')\n if (!rect) return\n\n const shape = this.state.get('crop.shape')\n\n // Clear crop area\n ctx.save()\n if (shape === 'circle') {\n const centerX = rect.x + rect.width / 2\n const centerY = rect.y + rect.height / 2\n const radius = Math.min(rect.width, rect.height) / 2\n ctx.beginPath()\n ctx.arc(centerX, centerY, radius, 0, Math.PI * 2)\n ctx.clip()\n } else {\n ctx.beginPath()\n ctx.rect(rect.x, rect.y, rect.width, rect.height)\n ctx.clip()\n }\n ctx.clearRect(rect.x, rect.y, rect.width, rect.height)\n ctx.restore()\n\n // Draw border\n ctx.strokeStyle = '#ffffff'\n ctx.lineWidth = 2\n ctx.setLineDash([5, 5])\n\n if (shape === 'circle') {\n ctx.beginPath()\n ctx.arc(\n rect.x + rect.width / 2,\n rect.y + rect.height / 2,\n Math.min(rect.width, rect.height) / 2,\n 0,\n Math.PI * 2\n )\n ctx.stroke()\n } else {\n ctx.strokeRect(rect.x, rect.y, rect.width, rect.height)\n }\n\n // Grid (rule-of-thirds)\n ctx.setLineDash([])\n ctx.strokeStyle = 'rgba(255,255,255,0.3)'\n ctx.lineWidth = 1\n const thirdW = rect.width / 3\n const thirdH = rect.height / 3\n\n for (let i = 1; i <= 2; i++) {\n ctx.beginPath()\n ctx.moveTo(rect.x + thirdW * i, rect.y)\n ctx.lineTo(rect.x + thirdW * i, rect.y + rect.height)\n ctx.stroke()\n\n ctx.beginPath()\n ctx.moveTo(rect.x, rect.y + thirdH * i)\n ctx.lineTo(rect.x + rect.width, rect.y + thirdH * i)\n ctx.stroke()\n }\n\n // Draw 8 resize handles\n const hs = this.HANDLE_SIZE\n const corners = [\n { x: rect.x, y: rect.y, m: 'resize-nw' },\n { x: rect.x + rect.width, y: rect.y, m: 'resize-ne' },\n { x: rect.x, y: rect.y + rect.height, m: 'resize-sw' },\n { x: rect.x + rect.width, y: rect.y + rect.height, m: 'resize-se' }\n ]\n const edges = [\n { x: rect.x + rect.width/2, y: rect.y, m: 'n' },\n { x: rect.x + rect.width/2, y: rect.y + rect.height, m: 's' },\n { x: rect.x, y: rect.y + rect.height/2, m: 'w' },\n { x: rect.x + rect.width, y: rect.y + rect.height/2, m: 'e' }\n ]\n\n const all = [...corners, ...edges]\n for (const h of all) {\n const hover = this._hoverMode === h.m\n const size = hover ? hs + 4 : hs\n ctx.beginPath()\n ctx.rect(h.x - size/2, h.y - size/2, size, size)\n ctx.fillStyle = hover ? '#4da3ff' : '#ffffff'\n ctx.strokeStyle = 'rgba(0,0,0,0.6)'\n ctx.lineWidth = 1\n ctx.fill()\n ctx.stroke()\n }\n }\n\n /**\n * Hit test for handles\n * @param {number} px\n * @param {number} py\n * @returns {string|null}\n */\n _hitHandle(px, py) {\n const rect = this.state.get('crop.rect')\n if (!rect) return null\n\n const near = (ax, ay, bx, by, pad) =>\n Math.abs(ax - bx) <= pad && Math.abs(ay - by) <= pad\n\n // Corners\n if (near(px, py, rect.x, rect.y, this.HANDLE_SIZE)) return 'resize-nw'\n if (near(px, py, rect.x + rect.width, rect.y, this.HANDLE_SIZE)) return 'resize-ne'\n if (near(px, py, rect.x, rect.y + rect.height, this.HANDLE_SIZE)) return 'resize-sw'\n if (near(px, py, rect.x + rect.width, rect.y + rect.height, this.HANDLE_SIZE)) return 'resize-se'\n\n // Edges\n if (Math.abs(py - rect.y) <= this.EDGE_HIT_PAD && px >= rect.x && px <= rect.x + rect.width) return 'n'\n if (Math.abs(py - (rect.y + rect.height)) <= this.EDGE_HIT_PAD && px >= rect.x && px <= rect.x + rect.width) return 's'\n if (Math.abs(px - rect.x) <= this.EDGE_HIT_PAD && py >= rect.y && py <= rect.y + rect.height) return 'w'\n if (Math.abs(px - (rect.x + rect.width)) <= this.EDGE_HIT_PAD && py >= rect.y && py <= rect.y + rect.height) return 'e'\n\n // Interior = move\n if (px >= rect.x && px <= rect.x + rect.width && py >= rect.y && py <= rect.y + rect.height) return 'move'\n\n return null\n }\n\n /**\n * Handle pointer down event\n */\n _handlePointerDown(event) {\n const rect = this.state.get('crop.rect')\n if (!rect) return\n\n const pos = event.global\n this._dragMode = this._hitHandle(pos.x, pos.y)\n\n if (this._dragMode) {\n this._isDragging = true\n this._dragStart = { x: pos.x, y: pos.y }\n this._startRect = { ...rect }\n // Reset auto-zoom throttle so first check in new drag is immediate\n this._lastAutoZoomCheck = 0\n }\n }\n\n /**\n * Handle pointer move event\n */\n _handlePointerMove(event) {\n const app = this.renderer.app\n if (!app) return\n\n const pos = event.global\n\n // Hover feedback when not dragging\n if (!this._isDragging || !this._dragStart || !this._startRect) {\n this._hoverMode = this._hitHandle(pos.x, pos.y)\n app.stage.cursor =\n this._hoverMode === 'move' ? 'move' :\n this._hoverMode === 'n' || this._hoverMode === 's' ? 'ns-resize' :\n this._hoverMode === 'e' || this._hoverMode === 'w' ? 'ew-resize' :\n this._hoverMode?.endsWith('nw') || this._hoverMode?.endsWith('se') ? 'nwse-resize' :\n this._hoverMode?.endsWith('ne') || this._hoverMode?.endsWith('sw') ? 'nesw-resize' :\n 'crosshair'\n this.drawOverlay()\n return\n }\n\n const rect = this.state.get('crop.rect')\n if (!rect) return\n\n const dx = pos.x - this._dragStart.x\n const dy = pos.y - this._dragStart.y\n\n switch (this._dragMode) {\n case 'move':\n rect.x = this._startRect.x + dx\n rect.y = this._startRect.y + dy\n break\n case 'n':\n rect.y = this._startRect.y + dy\n rect.height = this._startRect.height - dy\n break\n case 's':\n rect.height = this._startRect.height + dy\n break\n case 'w':\n rect.x = this._startRect.x + dx\n rect.width = this._startRect.width - dx\n break\n case 'e':\n rect.width = this._startRect.width + dx\n break\n case 'resize-nw':\n rect.x = this._startRect.x + dx\n rect.y = this._startRect.y + dy\n rect.width = this._startRect.width - dx\n rect.height = this._startRect.height - dy\n break\n case 'resize-ne':\n rect.y = this._startRect.y + dy\n rect.width = this._startRect.width + dx\n rect.height = this._startRect.height - dy\n break\n case 'resize-sw':\n rect.x = this._startRect.x + dx\n rect.width = this._startRect.width - dx\n rect.height = this._startRect.height + dy\n break\n case 'resize-se':\n rect.width = this._startRect.width + dx\n rect.height = this._startRect.height + dy\n break\n }\n\n // Minimum size\n rect.width = Math.max(50, rect.width)\n rect.height = Math.max(50, rect.height)\n\n this.state.set('crop.rect', rect)\n this.state.set('crop.dirty', true)\n\n // Enforce circle to 1:1\n const shape = this.state.get('crop.shape')\n if (shape === 'circle' && this.state.get('crop.aspect') !== '1:1') {\n this.state.set('crop.aspect', '1:1')\n }\n\n this.applyAspectRatio()\n\n // Auto-zoom when crop exceeds image bounds (only for resize, not move)\n if (this._dragMode !== 'move') {\n this._checkAutoZoom()\n }\n\n this.constrainCropRect()\n this.drawOverlay()\n }\n\n /**\n * Handle pointer up event\n */\n _handlePointerUp() {\n this._isDragging = false\n this._dragMode = null\n this._dragStart = null\n this._startRect = null\n }\n\n /**\n * Enable crop mode\n */\n enable() {\n const app = this.renderer.app\n const sprite = this.renderer.sprite\n if (!app || !sprite) return\n\n // Initialize crop rect if needed\n let rect = this.state.get('crop.rect')\n if (!rect) {\n const shape = this.state.get('crop.shape')\n if (shape === 'circle') {\n this.state.set('crop.aspect', '1:1')\n }\n\n const aspect = this.state.get('crop.aspect')\n const targetAspect = this._getAspectRatio(aspect)\n\n let width, height\n\n if (targetAspect) {\n // For a known aspect ratio, calculate the maximum rect that fits\n // within the sprite bounds while maintaining the ratio.\n // Use 90% of sprite dimensions to leave visible padding.\n const maxW = sprite.width * 0.9\n const maxH = sprite.height * 0.9\n\n if (maxW / maxH > targetAspect) {\n // Height is the constraint\n height = maxH\n width = height * targetAspect\n } else {\n // Width is the constraint\n width = maxW\n height = width / targetAspect\n }\n } else {\n // Free or square: use the classic 70%-of-screen square\n const size = Math.min(app.screen.width, app.screen.height) * 0.7\n width = size\n height = size\n }\n\n const x = sprite.x + (sprite.width - width) / 2\n const y = sprite.y + (sprite.height - height) / 2\n\n rect = { x, y, width, height }\n this.state.set('crop.rect', rect)\n\n if (aspect !== 'free') {\n this.constrainCropRect()\n }\n }\n\n this.state.set('crop.dirty', false)\n\n // Make stage interactive\n const stage = app.stage\n stage.eventMode = 'static'\n stage.hitArea = app.screen\n stage.cursor = 'crosshair'\n\n // Attach handlers\n stage.on('pointerdown', this._onPointerDown)\n stage.on('pointermove', this._onPointerMove)\n stage.on('pointerup', this._onPointerUp)\n stage.on('pointerupoutside', this._onPointerUp)\n\n this.state.set('mode', 'crop')\n this.drawOverlay()\n this.emit('enabled')\n }\n\n /**\n * Disable crop mode\n */\n disable() {\n const app = this.renderer.app\n if (!app) return\n\n const stage = app.stage\n stage.off('pointerdown', this._onPointerDown)\n stage.off('pointermove', this._onPointerMove)\n stage.off('pointerup', this._onPointerUp)\n stage.off('pointerupoutside', this._onPointerUp)\n\n stage.eventMode = 'auto'\n stage.cursor = 'default'\n\n this.state.set('crop.rect', null)\n this.state.set('crop.dirty', false)\n\n // Clear overlay\n if (this._overlayCanvas) {\n const ctx = this._overlayCanvas.getContext('2d')\n ctx?.clearRect(0, 0, this._overlayCanvas.width, this._overlayCanvas.height)\n }\n\n this.state.set('mode', 'filters')\n this.emit('disabled')\n }\n\n /**\n * Apply the crop\n * @returns {{ texture: PIXI.Texture, preservedZoom: number }|null}\n */\n apply() {\n const app = this.renderer.app\n const sprite = this.renderer.sprite\n const tex = this.renderer.originalTexture\n const rect = this.state.get('crop.rect')\n\n if (!rect || !sprite || !app || !tex) return null\n\n const PIXI = window.PIXI\n const prevZoom = this.renderer.zoom\n\n // Map view rect to output pixels\n const scaleX = tex.width / sprite.width\n const scaleY = tex.height / sprite.height\n const rawTx = (rect.x - sprite.x) * scaleX\n const rawTy = (rect.y - sprite.y) * scaleY\n\n let outW = Math.round(Math.max(1, rect.width * scaleX))\n let outH = Math.round(Math.max(1, rect.height * scaleY))\n let offsetX = Math.round(rawTx)\n let offsetY = Math.round(rawTy)\n\n if (outW <= 0 || outH <= 0) return null\n\n // Build off-screen container for crop\n const container = new PIXI.Container()\n const src = new PIXI.Sprite(tex)\n\n // Handle circle crop\n const shape = this.state.get('crop.shape')\n if (shape === 'circle') {\n const side = Math.round(Math.max(outW, outH))\n const cx = offsetX + outW / 2\n const cy = offsetY + outH / 2\n offsetX = Math.round(cx - side / 2)\n offsetY = Math.round(cy - side / 2)\n outW = outH = side\n\n const g = new PIXI.Graphics()\n if (typeof g.circle === 'function' && typeof g.fill === 'function') {\n g.circle(outW / 2, outH / 2, outW / 2).fill(0xffffff)\n } else {\n g.beginFill(0xffffff, 1)\n g.drawCircle(outW / 2, outH / 2, outW / 2)\n g.endFill()\n }\n src.mask = g\n container.addChild(g)\n }\n\n src.x = -offsetX\n src.y = -offsetY\n container.addChild(src)\n\n const rt = PIXI.RenderTexture.create({ width: outW, height: outH })\n app.renderer.render({\n container,\n target: rt,\n clear: true\n })\n\n container.destroy({ children: true })\n\n // Destroy the previous derived render texture to free GPU memory.\n // Only destroy if it's a derived texture (not the original base texture).\n const prevTexture = this.renderer.originalTexture\n if (prevTexture && prevTexture !== this.renderer.baseTexture) {\n prevTexture.destroy(true)\n }\n\n // Update textures\n this.renderer.originalTexture = rt\n\n // Replace sprite\n app.stage.removeChild(sprite)\n sprite.destroy()\n\n const newSprite = new PIXI.Sprite(rt)\n app.stage.addChild(newSprite)\n this.renderer.sprite = newSprite\n\n // Recalculate fit and apply transform\n this.renderer.fitScale = this.renderer.getFitScaleFor(rt)\n this.renderer.setZoom(prevZoom, { keepCenter: false })\n this.renderer.applyViewTransform()\n this.renderer.render()\n\n // Save the applied crop in ROOT-SOURCE pixel coordinates.\n // When a user reopens the editor and crops again, `offsetX/offsetY` are relative\n // to the already-cropped working texture. Compose them with the previously applied\n // rect so the serialized state can be replayed against the original source image.\n const previousAppliedRect = this.state.get('crop.appliedRect')\n const hasPreviousAppliedRect = previousAppliedRect\n && Number.isFinite(previousAppliedRect.x)\n && Number.isFinite(previousAppliedRect.y)\n && Number.isFinite(previousAppliedRect.width)\n && Number.isFinite(previousAppliedRect.height)\n const appliedPixelRect = hasPreviousAppliedRect\n ? {\n x: Math.round(previousAppliedRect.x + offsetX),\n y: Math.round(previousAppliedRect.y + offsetY),\n width: outW,\n height: outH,\n }\n : { x: offsetX, y: offsetY, width: outW, height: outH }\n const appliedShape = this.state.get('crop.shape') || 'free'\n const appliedAspect = this.state.get('crop.aspect') || 'free'\n\n // Disable crop mode (clears crop.rect for the interactive overlay)\n this.disable()\n\n // Persist the applied crop for serialization/reopen fidelity.\n // These are the texture-pixel coordinates that were baked into the texture.\n this.state.set('crop.appliedRect', appliedPixelRect)\n this.state.set('crop.appliedShape', appliedShape)\n this.state.set('crop.appliedAspect', appliedAspect)\n this.state.set('crop.dirty', false)\n\n this.emit('applied', { width: outW, height: outH })\n return { texture: rt, preservedZoom: prevZoom }\n }\n\n /**\n * Apply a crop from saved texture-pixel coordinates (for state rehydration).\n * Unlike apply(), this does not read from crop.rect or require the interactive overlay.\n * @param {{ x: number, y: number, width: number, height: number }} pixelRect\n * @param {'free'|'circle'} shape\n * @returns {{ texture: Object, preservedZoom: number }|null}\n */\n applyFromPixelRect(pixelRect, shape = 'free') {\n const app = this.renderer.app\n const sprite = this.renderer.sprite\n const tex = this.renderer.originalTexture\n\n if (!pixelRect || !sprite || !app || !tex) return null\n\n const PIXI = window.PIXI\n const prevZoom = this.renderer.zoom\n\n let outW = Math.round(Math.max(1, pixelRect.width))\n let outH = Math.round(Math.max(1, pixelRect.height))\n let offsetX = Math.round(pixelRect.x)\n let offsetY = Math.round(pixelRect.y)\n\n if (outW <= 0 || outH <= 0) return null\n\n // Build off-screen container for crop\n const container = new PIXI.Container()\n const src = new PIXI.Sprite(tex)\n\n // Handle circle crop\n if (shape === 'circle') {\n const side = Math.round(Math.max(outW, outH))\n const cx = offsetX + outW / 2\n const cy = offsetY + outH / 2\n offsetX = Math.round(cx - side / 2)\n offsetY = Math.round(cy - side / 2)\n outW = outH = side\n\n const g = new PIXI.Graphics()\n if (typeof g.circle === 'function' && typeof g.fill === 'function') {\n g.circle(outW / 2, outH / 2, outW / 2).fill(0xffffff)\n } else {\n g.beginFill(0xffffff, 1)\n g.drawCircle(outW / 2, outH / 2, outW / 2)\n g.endFill()\n }\n src.mask = g\n container.addChild(g)\n }\n\n src.x = -offsetX\n src.y = -offsetY\n container.addChild(src)\n\n const rt = PIXI.RenderTexture.create({ width: outW, height: outH })\n app.renderer.render({ container, target: rt, clear: true })\n container.destroy({ children: true })\n\n // Destroy the previous derived render texture to free GPU memory.\n const prevTexture = this.renderer.originalTexture\n if (prevTexture && prevTexture !== this.renderer.baseTexture) {\n prevTexture.destroy(true)\n }\n\n // Update textures\n this.renderer.originalTexture = rt\n\n // Replace sprite\n app.stage.removeChild(sprite)\n sprite.destroy()\n\n const newSprite = new PIXI.Sprite(rt)\n app.stage.addChild(newSprite)\n this.renderer.sprite = newSprite\n\n // Recalculate fit and apply transform\n this.renderer.fitScale = this.renderer.getFitScaleFor(rt)\n this.renderer.setZoom(prevZoom, { keepCenter: false })\n this.renderer.applyViewTransform()\n this.renderer.render()\n\n return { texture: rt, preservedZoom: prevZoom }\n }\n\n /**\n * Cancel crop\n */\n cancel() {\n this.disable()\n this.emit('cancelled')\n }\n\n /**\n * Set crop shape\n * @param {'free'|'square'|'circle'} shape\n */\n setShape(shape) {\n // Respect lockCropShape from preset — ignore shape changes when locked\n if (this.state.get('lockCropShape')) return\n\n this.state.set('crop.shape', shape)\n this.state.set('crop.dirty', true)\n if (shape === 'circle' || shape === 'square') {\n this.state.set('crop.aspect', '1:1')\n }\n this.applyAspectRatio()\n this.constrainCropRect()\n this.drawOverlay()\n }\n\n /**\n * Set aspect ratio\n * @param {'free'|'1:1'|'4:3'|'16:9'|'3:2'|'2:3'|string} aspect\n */\n setAspect(aspect) {\n // Respect lockAspectRatio from preset — ignore changes when locked\n if (this.state.get('lockAspectRatio')) return\n\n const shape = this.state.get('crop.shape')\n if (shape !== 'free') {\n // Manual aspect selection switches back to rectangular crop mode.\n if (this.state.get('lockCropShape')) return\n\n this.state.set('crop.shape', 'free')\n }\n\n this.state.set('crop.aspect', aspect)\n this.state.set('crop.dirty', true)\n this.applyAspectRatio()\n this.constrainCropRect()\n this.drawOverlay()\n }\n}\n","/**\n * Background Removal Manager for the vanilla image editor\n * Handles communication with the background removal service\n */\n\nexport class RemoveBgManager {\n /**\n * Create a new RemoveBgManager\n * @param {Object} options\n * @param {string} [options.endpoint] - API endpoint for uploaded preview background removal\n * @param {string} [options.optionsEndpoint] - API endpoint for background-removal capability/options\n * @param {string} [options.savedEndpoint] - API endpoint template for saved-media removal\n * @param {string} [options.fallbackEndpoint] - Fallback endpoint if primary fails\n */\n constructor(options = {}) {\n this._endpoint = options.endpoint || '/api/v1/media/background-removal/preview'\n this._optionsEndpoint = options.optionsEndpoint || '/api/v1/media/background-removal/options'\n this._savedEndpoint = options.savedEndpoint || '/api/v1/media/{media}/background-removal'\n this._fallbackEndpoint = options.fallbackEndpoint || null\n this._activeControllers = new Set()\n }\n\n _createAbortController() {\n if (typeof AbortController === 'undefined') {\n return null\n }\n\n const controller = new AbortController()\n this._activeControllers.add(controller)\n return controller\n }\n\n _releaseAbortController(controller) {\n if (controller) {\n this._activeControllers.delete(controller)\n }\n }\n\n cancelActiveRequests(reason = 'cancelled') {\n const controllers = Array.from(this._activeControllers)\n this._activeControllers.clear()\n\n for (const controller of controllers) {\n if (!controller.signal.aborted) {\n controller.abort(reason)\n }\n }\n\n return controllers.length\n }\n\n /**\n * Build a stable preview request so callers can stale-guard the response.\n * @param {string|Blob} imageData\n * @param {Object} options\n * @returns {Promise<Object>}\n */\n async preparePreviewRequest(imageData, options = {}) {\n const blob = typeof imageData === 'string'\n ? await this._dataUrlToBlob(imageData)\n : imageData\n\n return {\n ...options,\n blob,\n operationId: options.operationId || this._uuid(),\n sessionKey: options.sessionKey || this._uuid(),\n sourceHash: options.sourceHash || await this._sha256Hex(blob),\n targetRef: options.targetRef || null,\n tier: options.tier || 'balanced',\n }\n }\n\n /**\n * @param {string|Blob} imageData\n * @returns {Promise<string>}\n */\n async computeImageDataHash(imageData) {\n const blob = typeof imageData === 'string'\n ? await this._dataUrlToBlob(imageData)\n : imageData\n\n return this._sha256Hex(blob)\n }\n\n /**\n * Remove background from an image\n * @param {string} imageData - Data URL or Blob of the image\n * @param {Object} options\n * @param {string} [options.tier='balanced'] - Quality tier: 'fast', 'balanced', 'best'\n * @param {string} [options.model] - Explicit model name (overrides tier)\n * @param {boolean} [options.alpha_matting=false] - Enable alpha matting for edge refinement\n * @param {number} [options.alpha_f=10] - Alpha matting foreground threshold\n * @param {number} [options.alpha_fr=15] - Alpha matting foreground radius\n * @param {number} [options.alpha_erode_size=10] - Alpha matting erode size\n * @returns {Promise<{dataUrl: string, model: string, processMs: string}>}\n */\n async removeBackground(imageData, options = {}) {\n const request = typeof Blob !== 'undefined' && options.blob instanceof Blob\n ? options\n : await this.preparePreviewRequest(imageData, options)\n const blob = request.blob\n const operationId = request.operationId\n const sessionKey = request.sessionKey\n const sourceHash = request.sourceHash\n\n const formData = new FormData()\n formData.append('image_file', blob, 'image.png')\n formData.append('operation_id', operationId)\n formData.append('session_key', sessionKey)\n formData.append('source_hash', sourceHash)\n formData.append('tier', request.tier || 'balanced')\n\n if (request.targetRef) {\n formData.append('target_ref', request.targetRef)\n }\n\n if (request.model) {\n formData.append('model', request.model)\n }\n\n if (request.alpha_matting) {\n formData.append('alpha_matting', 'true')\n formData.append('alpha_f', String(request.alpha_f ?? 10))\n formData.append('alpha_fr', String(request.alpha_fr ?? 15))\n formData.append('alpha_erode_size', String(request.alpha_erode_size ?? 10))\n }\n\n if (request.max_megapixels !== undefined) {\n formData.append('max_megapixels', String(request.max_megapixels))\n }\n\n let response\n let error\n const controller = this._createAbortController()\n const withSignal = (init) => controller ? { ...init, signal: controller.signal } : init\n\n try {\n // Try primary endpoint\n try {\n response = await fetch(this._endpoint, withSignal({\n method: 'POST',\n body: formData,\n credentials: 'include' // For Sanctum auth\n }))\n } catch (e) {\n if (controller?.signal.aborted) {\n throw e\n }\n error = e\n }\n\n // Try fallback if primary failed\n if ((!response || !response.ok) && this._fallbackEndpoint) {\n try {\n response = await fetch(this._fallbackEndpoint, withSignal({\n method: 'POST',\n body: formData\n }))\n } catch (e) {\n if (controller?.signal.aborted) {\n throw e\n }\n // Keep original error if fallback also fails\n if (!error) error = e\n }\n }\n\n // Check for network error\n if (!response) {\n throw error || new Error('Network error: Unable to connect to background removal service')\n }\n\n // Check for HTTP error\n if (!response.ok) {\n let errorMessage = `Background removal failed (HTTP ${response.status})`\n try {\n const errorText = await response.text()\n if (errorText) errorMessage += `: ${errorText}`\n } catch (_) {}\n throw new Error(errorMessage)\n }\n\n // Read response\n const resultBlob = await response.blob()\n const dataUrl = await this._blobToDataUrl(resultBlob)\n\n return {\n dataUrl,\n model: response.headers.get('X-Model-Used') || 'unknown',\n processMs: response.headers.get('X-Process-Ms') || '0',\n operationId: response.headers.get('X-Operation-Id') || operationId,\n sessionKey: response.headers.get('X-Session-Key') || sessionKey,\n sourceHash: response.headers.get('X-Source-Hash') || sourceHash,\n targetRef: response.headers.get('X-Target-Ref') || request.targetRef || null,\n }\n } finally {\n this._releaseAbortController(controller)\n }\n }\n\n /**\n * Remove the background from a persisted editor-document layer.\n * @param {Object} options\n * @param {string} options.targetMediaUuid\n * @param {string} options.operationId\n * @param {string} options.documentId\n * @param {string} options.documentRevisionId\n * @param {string} options.targetLayerId\n * @param {string} options.expectedSourceHash\n * @param {string} [options.tier]\n * @returns {Promise<Object>}\n */\n async removeSavedMediaBackground(options) {\n const endpoint = this._savedEndpoint.replace('{media}', encodeURIComponent(options.targetMediaUuid))\n const payload = {\n operation_id: options.operationId,\n document_id: options.documentId,\n document_revision_id: options.documentRevisionId,\n target_layer_id: options.targetLayerId,\n expected_source_hash: options.expectedSourceHash,\n tier: options.tier || 'balanced',\n }\n\n if (options.model) payload.model = options.model\n if (options.alpha_matting !== undefined) payload.alpha_matting = !!options.alpha_matting\n if (options.max_megapixels !== undefined) payload.max_megapixels = Number(options.max_megapixels)\n\n const controller = this._createAbortController()\n let response\n try {\n response = await fetch(endpoint, {\n method: 'POST',\n body: JSON.stringify(payload),\n credentials: 'include',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n ...(controller ? { signal: controller.signal } : {}),\n })\n\n if (!response.ok) {\n let errorMessage = `Background removal failed (HTTP ${response.status})`\n try {\n const data = await response.json()\n if (data?.message) errorMessage += `: ${data.message}`\n } catch (_) {}\n throw new Error(errorMessage)\n }\n\n return await response.json()\n } finally {\n this._releaseAbortController(controller)\n }\n }\n\n /**\n * Convert a data URL to a Blob\n * @param {string} dataUrl\n * @returns {Promise<Blob>}\n */\n async _dataUrlToBlob(dataUrl) {\n // Handle both fetch-able URLs and data URLs\n if (dataUrl.startsWith('data:')) {\n const [header, data] = dataUrl.split(',')\n const mimeMatch = header.match(/:(.*?);/)\n const mime = mimeMatch ? mimeMatch[1] : 'image/png'\n const binary = atob(data)\n const array = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) {\n array[i] = binary.charCodeAt(i)\n }\n return new Blob([array], { type: mime })\n }\n\n // Fetch URL and convert to blob\n const response = await fetch(dataUrl)\n return response.blob()\n }\n\n /**\n * Convert a Blob to a data URL\n * @param {Blob} blob\n * @returns {Promise<string>}\n */\n _blobToDataUrl(blob) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(reader.result)\n reader.onerror = () => reject(new Error('Failed to read blob as data URL'))\n reader.readAsDataURL(blob)\n })\n }\n\n /**\n * Check if the background removal service is available\n * @returns {Promise<boolean>}\n */\n async isAvailable() {\n const options = await this.getOptions()\n\n return options.enabled === true && options.service?.available === true\n }\n\n /**\n * @returns {Promise<Object>}\n */\n async getOptions() {\n try {\n const response = await fetch(this._optionsEndpoint, {\n method: 'GET',\n credentials: 'include'\n })\n if (!response.ok) {\n return { enabled: false, service: { available: false } }\n }\n\n const payload = await response.json()\n\n return payload && typeof payload === 'object'\n ? payload\n : { enabled: false, service: { available: false } }\n } catch (_) {\n // Try fallback\n if (this._fallbackEndpoint) {\n try {\n const response = await fetch(this._fallbackEndpoint, {\n method: 'OPTIONS'\n })\n return {\n enabled: response.ok,\n service: { available: response.ok },\n }\n } catch (_) {}\n }\n return { enabled: false, service: { available: false } }\n }\n }\n\n _uuid() {\n if (globalThis.crypto?.randomUUID) {\n return globalThis.crypto.randomUUID()\n }\n\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {\n const random = Math.floor(Math.random() * 16)\n const value = char === 'x' ? random : (random & 0x3) | 0x8\n return value.toString(16)\n })\n }\n\n async _sha256Hex(blob) {\n const buffer = await blob.arrayBuffer()\n const digest = await globalThis.crypto.subtle.digest('SHA-256', buffer)\n return [...new Uint8Array(digest)]\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join('')\n }\n}\n","/**\n * UI Builder - DOM helpers and component factories\n * Creates UI elements without external dependencies\n */\n\n/**\n * Create an element with attributes and children\n * @param {string} tag - HTML tag name\n * @param {Object} attrs - Attributes and properties\n * @param {...(string|HTMLElement)} children - Child elements or text\n * @returns {HTMLElement}\n */\nexport function el(tag, attrs = {}, ...children) {\n const element = document.createElement(tag)\n\n for (const [key, value] of Object.entries(attrs)) {\n // Skip undefined and null values\n if (value === undefined || value === null) continue\n\n if (key === 'className') {\n element.className = value\n } else if (key === 'style' && typeof value === 'object') {\n Object.assign(element.style, value)\n } else if (key.startsWith('on') && typeof value === 'function') {\n const event = key.slice(2).toLowerCase()\n element.addEventListener(event, value)\n } else if (key === 'dataset' && typeof value === 'object') {\n Object.assign(element.dataset, value)\n } else {\n element.setAttribute(key, value)\n }\n }\n\n for (const child of children) {\n if (typeof child === 'string') {\n element.appendChild(document.createTextNode(child))\n } else if (child instanceof HTMLElement) {\n element.appendChild(child)\n }\n }\n\n return element\n}\n\n/**\n * Create a slider with label and value display\n * @param {Object} options\n * @returns {HTMLElement}\n */\nexport function createSlider({ id, label, min = 0, max = 1, step = 0.01, value = 0.5, onChange }) {\n // Extract control ID from the id (format: \"filterId-controlId\")\n const controlId = id.includes('-') ? id.split('-').slice(1).join('-') : id\n const wrapper = el('div', {\n className: 'slider-control slider-wrapper',\n 'data-control': controlId,\n 'data-testid': `slider-${controlId}`\n })\n\n const header = el('div', { className: 'slider-header' },\n el('label', { for: id, className: 'slider-label' }, label),\n el('span', { className: 'slider-value', id: `${id}-value` }, formatValue(value))\n )\n\n const input = el('input', {\n type: 'range',\n id,\n className: 'slider-input',\n min: String(min),\n max: String(max),\n step: String(step),\n value: String(value),\n onInput: (e) => {\n const val = parseFloat(e.target.value)\n const display = wrapper.querySelector('.slider-value')\n if (display) display.textContent = formatValue(val)\n onChange?.(val)\n }\n })\n\n wrapper.appendChild(header)\n wrapper.appendChild(input)\n\n // Method to update value programmatically\n wrapper.setValue = (val) => {\n input.value = String(val)\n const display = wrapper.querySelector('.slider-value')\n if (display) display.textContent = formatValue(val)\n }\n\n return wrapper\n}\n\n/**\n * Format a numeric value for display\n */\nfunction formatValue(val) {\n if (Number.isInteger(val)) return String(val)\n return val.toFixed(2)\n}\n\n/**\n * Create a toggle switch\n * @param {Object} options\n * @returns {HTMLElement}\n */\nexport function createToggle({ id, label, checked = false, onChange }) {\n const wrapper = el('div', { className: 'toggle-control' })\n\n const labelEl = el('label', { className: 'toggle-label', for: id }, label)\n\n const input = el('input', {\n type: 'checkbox',\n id,\n className: 'toggle-input',\n checked: checked ? 'checked' : undefined,\n onChange: (e) => onChange?.(e.target.checked)\n })\n\n // Click handler for the visual toggle element\n const toggle = el('div', {\n className: 'toggle-switch',\n onClick: (e) => {\n // Prevent double-triggering if clicking directly on the input\n if (e.target === input) return\n input.checked = !input.checked\n onChange?.(input.checked)\n }\n })\n\n const slider = el('span', { className: 'toggle-slider' })\n toggle.appendChild(input)\n toggle.appendChild(slider)\n\n wrapper.appendChild(labelEl)\n wrapper.appendChild(toggle)\n\n wrapper.setChecked = (val) => {\n input.checked = val\n }\n\n return wrapper\n}\n\n/**\n * Create a color picker\n * @param {Object} options\n * @returns {HTMLElement}\n */\nexport function createColorPicker({ id, label, value = '#000000', onChange }) {\n const wrapper = el('div', { className: 'color-control' })\n\n const labelEl = el('label', { className: 'color-label', for: id }, label)\n\n const input = el('input', {\n type: 'color',\n id,\n className: 'color-input',\n value,\n onInput: (e) => onChange?.(e.target.value)\n })\n\n wrapper.appendChild(labelEl)\n wrapper.appendChild(input)\n\n wrapper.setValue = (val) => {\n input.value = val\n }\n\n return wrapper\n}\n\n/**\n * Create a text input that commits on Enter or blur (not on every keystroke).\n * This deferred-commit behavior is important for inputs that trigger expensive\n * parsing on change (e.g. the CSS gradient input, which creates a full PIXI\n * filter on each commit).\n *\n * @param {Object} options\n * @param {string} options.id\n * @param {string} options.label\n * @param {string} [options.value]\n * @param {string} [options.placeholder]\n * @param {Function} [options.onCommit] - Called with trimmed value on Enter or blur when value changed\n * @returns {HTMLElement}\n */\nexport function createTextInput({ id, label, value = '', placeholder = '', onCommit }) {\n const wrapper = el('div', {\n className: 'text-control',\n 'data-control': id,\n 'data-testid': `text-${id}`\n })\n\n const labelEl = el('label', { className: 'text-label', for: id }, label)\n\n let lastCommitted = value\n\n const input = el('input', {\n type: 'text',\n id,\n className: 'text-input',\n value,\n placeholder,\n onKeyDown: (e) => {\n if (e.key === 'Enter') {\n e.preventDefault()\n commit()\n input.blur()\n }\n },\n onBlur: () => commit()\n })\n\n function commit() {\n const next = input.value\n if (next === lastCommitted) return\n lastCommitted = next\n onCommit?.(next)\n }\n\n wrapper.appendChild(labelEl)\n wrapper.appendChild(input)\n\n wrapper.setValue = (val) => {\n const next = val == null ? '' : String(val)\n input.value = next\n lastCommitted = next\n }\n\n return wrapper\n}\n\n/**\n * Create a select dropdown\n * @param {Object} options\n * @returns {HTMLElement}\n */\nexport function createSelect({ id, label, options = [], value, onChange }) {\n const wrapper = el('div', { className: 'select-control' })\n\n const labelEl = el('label', { className: 'select-label', for: id }, label)\n\n const select = el('select', {\n id,\n className: 'select-input',\n onChange: (e) => onChange?.(e.target.value)\n })\n\n for (const opt of options) {\n const option = el('option', { value: opt.value }, opt.label)\n if (opt.value === value) option.selected = true\n select.appendChild(option)\n }\n\n wrapper.appendChild(labelEl)\n wrapper.appendChild(select)\n\n wrapper.setValue = (val) => {\n select.value = val\n }\n\n return wrapper\n}\n\n/**\n * Create a button\n * @param {Object} options\n * @returns {HTMLElement}\n */\nexport function createButton({ label, className = '', onClick, icon = null, disabled = false }) {\n const button = el('button', {\n type: 'button',\n className: `btn ${className}`.trim(),\n onClick,\n disabled: disabled ? 'disabled' : undefined\n })\n\n if (icon) {\n const iconEl = el('span', { className: 'btn-icon' })\n iconEl.innerHTML = icon\n // Mark decorative icon SVG as hidden from screen readers\n const svg = iconEl.querySelector('svg')\n if (svg) svg.setAttribute('aria-hidden', 'true')\n button.appendChild(iconEl)\n }\n\n if (label) {\n button.appendChild(document.createTextNode(label))\n }\n\n return button\n}\n\n/**\n * Create an icon button\n * @param {Object} options\n * @returns {HTMLElement}\n */\nexport function createIconButton({ icon, title, className = '', onClick, disabled = false, testId = null, ariaLabel = null }) {\n const attrs = {\n type: 'button',\n className: `icon-btn ${className}`.trim(),\n title,\n 'aria-label': ariaLabel || title,\n onClick,\n disabled: disabled ? 'disabled' : undefined\n }\n\n if (testId) {\n attrs.dataset = { testid: testId }\n }\n\n const button = el('button', attrs)\n\n button.innerHTML = icon\n // Mark decorative icon SVG as hidden from screen readers\n const svg = button.querySelector('svg')\n if (svg) svg.setAttribute('aria-hidden', 'true')\n return button\n}\n\n/**\n * Create a chip/tag\n * @param {Object} options\n * @returns {HTMLElement}\n */\nexport function createChip({ label, icon, active = false, onClick }) {\n const chip = el('button', {\n type: 'button',\n className: `chip ${active ? 'active' : ''}`.trim(),\n onClick\n })\n\n if (icon) {\n const iconEl = el('span', { className: 'chip-icon' })\n iconEl.innerHTML = icon\n // Mark decorative icon SVG as hidden from screen readers\n const svg = iconEl.querySelector('svg')\n if (svg) svg.setAttribute('aria-hidden', 'true')\n chip.appendChild(iconEl)\n }\n\n chip.appendChild(el('span', { className: 'chip-label' }, label))\n\n chip.setActive = (isActive) => {\n chip.classList.toggle('active', isActive)\n }\n\n return chip\n}\n\n/**\n * Create a card component\n * @param {Object} options\n * @returns {HTMLElement}\n */\nexport function createCard({ className = '', children = [] }) {\n const card = el('div', { className: `card ${className}`.trim() })\n\n for (const child of children) {\n card.appendChild(child)\n }\n\n return card\n}\n\n/**\n * Create scrollable container\n * @param {Object} options\n * @returns {HTMLElement}\n */\nexport function createScrollContainer({ className = '', horizontal = false, children = [] }) {\n const container = el('div', {\n className: `scroll-container ${horizontal ? 'horizontal' : 'vertical'} ${className}`.trim()\n })\n\n for (const child of children) {\n container.appendChild(child)\n }\n\n return container\n}\n","/**\n * Toolbar component for the vanilla image editor\n * Provides zoom controls, mode switching, and action buttons\n */\nimport { el, createIconButton } from './UIBuilder.js'\nimport * as icons from '../icons/icons.js'\n\nexport class Toolbar {\n constructor(state, editor) {\n this.state = state\n this.editor = editor\n this.element = null\n this._removeBgBtn = null\n this._removeBgTierSelect = null\n this._removeBgStatus = null\n this._removeBgTier = 'balanced'\n this._unsubscribers = []\n }\n\n /**\n * Create and render the toolbar\n * @returns {HTMLElement}\n */\n render() {\n this.element = el('div', { className: 'editor-toolbar' })\n\n // Left section - Open image\n const leftSection = el('div', { className: 'toolbar-section toolbar-left' })\n\n const openBtn = createIconButton({\n icon: icons.openFolder,\n title: 'Open Image',\n className: 'toolbar-btn',\n testId: 'btn-open-image',\n ariaLabel: 'Open image file',\n onClick: () => this.editor.openFilePicker()\n })\n leftSection.appendChild(openBtn)\n\n // Zoom controls were moved to a floating overlay on the canvas\n // (see SplitPanelLayout → canvas-zoom-overlay) so the toolbar has room\n // for the labeled Save button on narrow viewports. This component no\n // longer owns a zoom label element.\n\n // Right section - Theme toggle, Reset, Save, Close\n const rightSection = el('div', { className: 'toolbar-section toolbar-right' })\n\n this._themeBtn = createIconButton({\n icon: this.state.get('isDarkMode') ? icons.sunny : icons.moon,\n title: 'Toggle Theme',\n className: 'toolbar-btn toolbar-btn-theme',\n testId: 'btn-toggle-theme',\n ariaLabel: 'Toggle theme',\n onClick: () => this.editor.toggleTheme()\n })\n\n // Crop button (mobile only — hidden via CSS on desktop)\n this._cropBtn = createIconButton({\n icon: icons.crop,\n title: 'Crop',\n className: 'toolbar-btn toolbar-btn-crop',\n testId: 'btn-crop',\n ariaLabel: 'Crop image',\n onClick: () => {\n const currentMode = this.state.get('mode')\n if (currentMode === 'crop') {\n this.editor.setMode('filters')\n } else {\n this.editor.setMode('crop')\n }\n }\n })\n\n const removeBgGroup = el('div', { className: 'remove-bg-control' })\n this._removeBgTierSelect = el('select', {\n className: 'remove-bg-tier-select',\n title: 'Background removal quality',\n 'aria-label': 'Background removal quality',\n dataset: { testid: 'select-remove-background-tier' },\n onChange: (event) => {\n this._removeBgTier = event.target.value\n },\n })\n for (const option of [\n ['fast', 'Fast'],\n ['balanced', 'Balanced'],\n ['best', 'Best'],\n ]) {\n const optionEl = el('option', { value: option[0] }, option[1])\n if (option[0] === this._removeBgTier) optionEl.selected = true\n this._removeBgTierSelect.appendChild(optionEl)\n }\n\n this._removeBgBtn = createIconButton({\n icon: icons.personOutline,\n title: 'Remove Background',\n className: 'toolbar-btn toolbar-btn-remove-bg',\n testId: 'btn-remove-background',\n ariaLabel: 'Remove background',\n disabled: !this._canRemoveBackground(),\n onClick: async () => {\n if (!this._canRemoveBackground()) return\n this._setRemoveBgStatus('Removing background...')\n try {\n await this.editor.removeBackground({ tier: this._removeBgTier })\n this._setRemoveBgStatus('Background removed')\n } catch (_error) {\n this._setRemoveBgStatus('Background removal failed')\n } finally {\n this._updateRemoveBgButton()\n }\n }\n })\n\n this._removeBgStatus = el('span', {\n className: 'sr-only',\n role: 'status',\n 'aria-live': 'polite',\n 'aria-atomic': 'true',\n })\n\n removeBgGroup.appendChild(this._removeBgTierSelect)\n removeBgGroup.appendChild(this._removeBgBtn)\n removeBgGroup.appendChild(this._removeBgStatus)\n\n const resetBtn = createIconButton({\n icon: icons.refresh,\n title: 'Reset All',\n className: 'toolbar-btn',\n testId: 'btn-reset-all',\n ariaLabel: 'Reset all changes',\n onClick: () => this.editor.resetAll()\n })\n\n const saveBtn = el('button', {\n type: 'button',\n className: 'icon-btn toolbar-btn toolbar-btn-primary toolbar-save-labeled',\n title: 'Save Image',\n 'aria-label': 'Save image',\n dataset: { testid: 'btn-save-edit' },\n onClick: () => this.editor.save()\n })\n const saveIconEl = el('span', { className: 'btn-icon' })\n saveIconEl.innerHTML = icons.save\n const saveSvg = saveIconEl.querySelector('svg')\n if (saveSvg) saveSvg.setAttribute('aria-hidden', 'true')\n saveBtn.appendChild(saveIconEl)\n saveBtn.appendChild(el('span', { className: 'toolbar-save-label' }, 'Save'))\n\n const closeBtn = createIconButton({\n icon: icons.close,\n title: 'Close',\n className: 'toolbar-btn',\n testId: 'btn-cancel-edit',\n ariaLabel: 'Close editor',\n onClick: () => this.editor.close()\n })\n\n rightSection.appendChild(this._themeBtn)\n rightSection.appendChild(this._cropBtn)\n rightSection.appendChild(removeBgGroup)\n rightSection.appendChild(resetBtn)\n rightSection.appendChild(saveBtn)\n rightSection.appendChild(closeBtn)\n\n // Assemble toolbar\n this.element.appendChild(leftSection)\n this.element.appendChild(rightSection)\n\n // Subscribe to state changes\n this._subscribeToState()\n\n return this.element\n }\n\n /**\n * Subscribe to state changes\n */\n _subscribeToState() {\n // Theme changes\n const unsubTheme = this.state.on('change:isDarkMode', ({ value }) => {\n this._themeBtn.innerHTML = value ? icons.sunny : icons.moon\n })\n this._unsubscribers.push(unsubTheme)\n\n // Mode changes — update crop button active state\n const unsubMode = this.state.on('change:mode', ({ value }) => {\n if (this._cropBtn) {\n this._cropBtn.classList.toggle('toolbar-btn-primary', value === 'crop')\n }\n })\n this._unsubscribers.push(unsubMode)\n\n const unsubHasImage = this.state.on('change:hasImage', () => this._updateRemoveBgButton())\n this._unsubscribers.push(unsubHasImage)\n\n const unsubProcessing = this.state.on('change:isProcessing', () => this._updateRemoveBgButton())\n this._unsubscribers.push(unsubProcessing)\n\n const unsubAvailability = this.state.on('change:backgroundRemovalAvailable', () => this._updateRemoveBgButton())\n this._unsubscribers.push(unsubAvailability)\n\n const unsubDocument = this.state.on('change:editorDocument', () => this._updateRemoveBgButton())\n this._unsubscribers.push(unsubDocument)\n\n const unsubLayers = this.state.on('change:layers', () => this._updateRemoveBgButton())\n this._unsubscribers.push(unsubLayers)\n\n const unsubActiveLayer = this.state.on('change:activeLayerId', () => this._updateRemoveBgButton())\n this._unsubscribers.push(unsubActiveLayer)\n }\n\n _canRemoveBackground() {\n if (typeof this.editor.canRemoveBackground === 'function') {\n return this.editor.canRemoveBackground()\n }\n\n return !!this.state.get('hasImage') && typeof this.editor.removeBackground === 'function'\n }\n\n _updateRemoveBgButton() {\n if (!this._removeBgBtn) return\n const busy = this.state.get('isProcessing') === true\n const disabled = !this._canRemoveBackground() || busy\n this._removeBgBtn.disabled = disabled\n this._removeBgBtn.setAttribute('aria-disabled', disabled ? 'true' : 'false')\n this._removeBgBtn.setAttribute('aria-busy', busy ? 'true' : 'false')\n if (this._removeBgTierSelect) {\n this._removeBgTierSelect.disabled = disabled\n this._removeBgTierSelect.setAttribute('aria-disabled', disabled ? 'true' : 'false')\n }\n }\n\n _setRemoveBgStatus(message) {\n if (this._removeBgStatus) {\n this._removeBgStatus.textContent = message\n }\n }\n\n /**\n * Clean up\n */\n destroy() {\n this._unsubscribers.forEach(unsub => unsub())\n this._unsubscribers = []\n this.element?.remove()\n this.element = null\n }\n}\n","/**\n * Category Carousel component for the vanilla image editor\n * Displays filter categories as scrollable chips\n */\nimport { el, createIconButton, createChip } from './UIBuilder.js'\nimport * as icons from '../icons/icons.js'\nimport {\n EDITOR_CATEGORIES,\n REGISTRY_TO_UI,\n getUiCategoryForRegistry,\n} from '../filters/categories.js'\n\nconst CATEGORIES = EDITOR_CATEGORIES\n\n// Back-compat exports for any downstream code still importing these names.\n// The canonical source now lives in filters/categories.js.\nexport { REGISTRY_TO_UI as REGISTRY_CATEGORY_MAP }\nexport { getUiCategoryForRegistry }\n\nexport class CategoryCarousel {\n constructor(state, editor) {\n this.state = state\n this.editor = editor\n this.element = null\n this._chips = new Map()\n this._scrollIndex = 0\n this._unsubscribers = []\n }\n\n /**\n * Create and render the category carousel\n * @param {Function} onSelect - Callback when category is selected\n * @returns {HTMLElement}\n */\n render(onSelect) {\n this._onSelect = onSelect\n this.element = el('div', {\n className: 'category-carousel-container',\n 'data-testid': 'category-carousel'\n })\n\n // Left navigation button\n this._leftBtn = createIconButton({\n icon: icons.chevronLeft,\n title: 'Previous categories',\n className: 'carousel-nav carousel-nav-left',\n onClick: () => this._scrollLeft()\n })\n\n // Carousel container\n this._carousel = el('div', { className: 'category-carousel' })\n\n // Create category chips\n CATEGORIES.forEach(cat => {\n const chip = createChip({\n label: cat.name,\n icon: cat.icon,\n active: this.state.get('selectedCategory') === cat.id,\n onClick: () => this._selectCategory(cat.id)\n })\n chip.dataset.categoryId = cat.id\n chip.dataset.category = cat.id\n chip.dataset.testid = `category-${cat.id}`\n this._chips.set(cat.id, chip)\n this._carousel.appendChild(chip)\n })\n\n // Right navigation button\n this._rightBtn = createIconButton({\n icon: icons.chevronRight,\n title: 'Next categories',\n className: 'carousel-nav carousel-nav-right',\n onClick: () => this._scrollRight()\n })\n\n // Pagination dots\n this._pagination = el('div', {\n className: 'carousel-pagination',\n role: 'tablist',\n 'aria-label': 'Category pages'\n })\n const pageCount = Math.ceil(CATEGORIES.length / 3)\n for (let i = 0; i < pageCount; i++) {\n const dot = el('button', {\n type: 'button',\n className: `pagination-dot ${i === 0 ? 'active' : ''}`,\n role: 'tab',\n 'aria-label': `Page ${i + 1} of ${pageCount}`,\n 'aria-selected': i === 0 ? 'true' : 'false',\n onClick: () => this._scrollToPage(i)\n })\n this._pagination.appendChild(dot)\n }\n\n // Assemble\n this.element.appendChild(this._leftBtn)\n this.element.appendChild(this._carousel)\n this.element.appendChild(this._rightBtn)\n this.element.appendChild(this._pagination)\n\n // Subscribe to state changes\n this._subscribeToState()\n\n // Update button states\n this._updateNavButtons()\n\n return this.element\n }\n\n /**\n * Subscribe to state changes\n */\n _subscribeToState() {\n const unsub = this.state.on('change:selectedCategory', ({ value }) => {\n this._chips.forEach((chip, id) => {\n chip.setActive(id === value)\n })\n })\n this._unsubscribers.push(unsub)\n }\n\n /**\n * Select a category\n * @param {string} categoryId\n */\n _selectCategory(categoryId) {\n this.state.set('selectedCategory', categoryId)\n\n // Only change mode when switching between crop ↔ filters.\n // Avoid calling setMode('filters') when already in filters mode —\n // it unnecessarily triggers cropManager.disable() + double applyFilters().\n const currentMode = this.state.get('mode')\n if (categoryId === 'crop') {\n if (currentMode !== 'crop') {\n this.editor.setMode('crop')\n }\n } else if (currentMode !== 'filters') {\n this.editor.setMode('filters')\n }\n\n // Scroll the selected chip into view\n const selectedChip = this._chips.get(categoryId)\n if (selectedChip && this._carousel) {\n selectedChip.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' })\n }\n\n this._onSelect?.(categoryId)\n }\n\n /**\n * Scroll carousel left\n */\n _scrollLeft() {\n if (this._scrollIndex > 0) {\n this._scrollIndex--\n this._scrollCarousel()\n }\n }\n\n /**\n * Scroll carousel right\n */\n _scrollRight() {\n if (this._scrollIndex < CATEGORIES.length - 3) {\n this._scrollIndex++\n this._scrollCarousel()\n }\n }\n\n /**\n * Scroll to a specific page\n * @param {number} pageIndex\n */\n _scrollToPage(pageIndex) {\n this._scrollIndex = pageIndex * 3\n this._scrollCarousel()\n }\n\n /**\n * Animate carousel scroll\n */\n _scrollCarousel() {\n if (this._carousel) {\n const chip = this._carousel.querySelector('.chip')\n const itemWidth = chip?.clientWidth || 100\n this._carousel.scrollTo({\n left: this._scrollIndex * (itemWidth + 8), // 8px gap\n behavior: 'smooth'\n })\n }\n this._updateNavButtons()\n this._updatePagination()\n }\n\n /**\n * Update navigation button states\n */\n _updateNavButtons() {\n if (this._leftBtn) {\n this._leftBtn.disabled = this._scrollIndex === 0\n }\n if (this._rightBtn) {\n this._rightBtn.disabled = this._scrollIndex >= CATEGORIES.length - 3\n }\n }\n\n /**\n * Update pagination dot states\n */\n _updatePagination() {\n if (this._pagination) {\n const dots = this._pagination.querySelectorAll('.pagination-dot')\n const activePage = Math.floor(this._scrollIndex / 3)\n dots.forEach((dot, i) => {\n const isActive = i === activePage\n dot.classList.toggle('active', isActive)\n dot.setAttribute('aria-selected', isActive ? 'true' : 'false')\n })\n }\n }\n\n /**\n * Set the selected category\n * @param {string} categoryId\n */\n setSelected(categoryId) {\n this._selectCategory(categoryId)\n }\n\n /**\n * Clean up\n */\n destroy() {\n this._unsubscribers.forEach(unsub => unsub())\n this._unsubscribers = []\n this._chips.clear()\n this.element?.remove()\n this.element = null\n }\n}\n","/**\n * Filter Carousel component for the vanilla image editor\n * Displays filters in the current category as scrollable cards\n */\nimport { el, createIconButton } from './UIBuilder.js'\nimport * as icons from '../icons/icons.js'\n\nexport class FilterCarousel {\n constructor(state, filterManager) {\n this.state = state\n this.filterManager = filterManager\n this.element = null\n this._filterCards = new Map()\n this._scrollIndex = 0\n this._unsubscribers = []\n this._onToggle = null\n this._onSelect = null\n }\n\n /**\n * Create and render the filter carousel\n * @param {Object} options - { onToggle, onSelect }\n * @returns {HTMLElement}\n */\n render({ onToggle, onSelect }) {\n this._onToggle = onToggle\n this._onSelect = onSelect\n\n this.element = el('div', { className: 'filter-carousel-container' })\n\n // Left navigation button\n this._leftBtn = createIconButton({\n icon: icons.chevronLeft,\n title: 'Previous filters',\n className: 'carousel-nav carousel-nav-left',\n onClick: () => this._scrollLeft()\n })\n\n // Carousel container\n this._carousel = el('div', { className: 'filter-carousel' })\n\n // Right navigation button\n this._rightBtn = createIconButton({\n icon: icons.chevronRight,\n title: 'Next filters',\n className: 'carousel-nav carousel-nav-right',\n onClick: () => this._scrollRight()\n })\n\n // Assemble\n this.element.appendChild(this._leftBtn)\n this.element.appendChild(this._carousel)\n this.element.appendChild(this._rightBtn)\n\n // Subscribe to state changes\n this._subscribeToState()\n\n // Initial render\n this._renderFilters()\n\n return this.element\n }\n\n /**\n * Subscribe to state changes\n */\n _subscribeToState() {\n // Re-render when category changes\n const unsubCat = this.state.on('change:selectedCategory', () => {\n this._renderFilters()\n })\n this._unsubscribers.push(unsubCat)\n\n // Update active states\n const unsubActive = this.state.on('change:activeFilters', () => {\n this._updateActiveStates()\n })\n this._unsubscribers.push(unsubActive)\n\n // Update selected state\n const unsubSelected = this.state.on('change:selectedFilter', () => {\n this._updateSelectedState()\n })\n this._unsubscribers.push(unsubSelected)\n }\n\n /**\n * Render filters for current category\n */\n _renderFilters() {\n // Clear existing\n this._carousel.innerHTML = ''\n this._filterCards.clear()\n this._scrollIndex = 0\n\n const category = this.state.get('selectedCategory')\n if (category === 'crop') return\n\n const filters = this.filterManager.getFiltersByCategory(category)\n const activeFilters = this.state.get('activeFilters')\n const selectedFilter = this.state.get('selectedFilter')\n\n filters.forEach(filter => {\n const card = this._createFilterCard(filter, {\n isActive: activeFilters.has(filter.id),\n isSelected: selectedFilter === filter.id\n })\n this._filterCards.set(filter.id, card)\n this._carousel.appendChild(card)\n })\n\n this._updateNavButtons()\n }\n\n /**\n * Create a filter card\n * @param {Object} filter\n * @param {Object} options\n * @returns {HTMLElement}\n */\n _createFilterCard(filter, { isActive, isSelected }) {\n const card = el('div', {\n className: `filter-card ${isActive ? 'active' : ''} ${isSelected ? 'selected' : ''}`,\n 'data-filter': filter.id,\n 'data-testid': `filter-${filter.id}`,\n onClick: () => this._handleCardClick(filter.id)\n })\n\n // Thumbnail/preview area\n const preview = el('div', { className: 'filter-preview' })\n const previewText = el('span', { className: 'filter-preview-text' }, filter.name.charAt(0))\n preview.appendChild(previewText)\n\n // Filter name\n const name = el('span', { className: 'filter-name', title: filter.name }, filter.name)\n\n // Toggle button (checkbox visual)\n const toggle = el('button', {\n className: `filter-toggle ${isActive ? 'active' : ''}`,\n onClick: (e) => {\n e.stopPropagation()\n // Check current state at click time, not creation time\n const currentlyActive = this.state.get('activeFilters').has(filter.id)\n const willBeActive = !currentlyActive\n this._handleToggle(filter.id, willBeActive)\n\n // UX improvement: When toggling a filter ON, also select it to show settings\n if (willBeActive) {\n this.state.set('selectedFilter', filter.id)\n this._onSelect?.(filter.id)\n }\n }\n })\n toggle.innerHTML = isActive ? icons.checkmark : ''\n\n card.appendChild(preview)\n card.appendChild(name)\n card.appendChild(toggle)\n\n // Store references for updates\n card._toggle = toggle\n card._isActive = isActive\n\n return card\n }\n\n /**\n * Handle filter card click (select)\n * @param {string} filterId\n */\n _handleCardClick(filterId) {\n const activeFilters = this.state.get('activeFilters')\n\n // If not active, toggle it on first\n if (!activeFilters.has(filterId)) {\n this._handleToggle(filterId, true)\n }\n\n this.state.set('selectedFilter', filterId)\n this._onSelect?.(filterId)\n }\n\n /**\n * Handle filter toggle\n * @param {string} filterId\n * @param {boolean} enabled\n */\n _handleToggle(filterId, enabled) {\n this._onToggle?.(filterId, enabled)\n }\n\n /**\n * Update active states based on state\n */\n _updateActiveStates() {\n const activeFilters = this.state.get('activeFilters')\n\n this._filterCards.forEach((card, filterId) => {\n const isActive = activeFilters.has(filterId)\n card.classList.toggle('active', isActive)\n card._toggle.classList.toggle('active', isActive)\n card._toggle.innerHTML = isActive ? icons.checkmark : ''\n card._isActive = isActive\n })\n }\n\n /**\n * Update selected state based on state\n */\n _updateSelectedState() {\n const selectedFilter = this.state.get('selectedFilter')\n\n this._filterCards.forEach((card, filterId) => {\n card.classList.toggle('selected', filterId === selectedFilter)\n })\n }\n\n /**\n * Scroll carousel left\n */\n _scrollLeft() {\n if (this._scrollIndex > 0) {\n this._scrollIndex--\n this._scrollCarousel()\n }\n }\n\n /**\n * Scroll carousel right\n */\n _scrollRight() {\n const maxIndex = Math.max(0, this._filterCards.size - 3)\n if (this._scrollIndex < maxIndex) {\n this._scrollIndex++\n this._scrollCarousel()\n }\n }\n\n /**\n * Animate carousel scroll\n */\n _scrollCarousel() {\n if (this._carousel) {\n const card = this._carousel.querySelector('.filter-card')\n const itemWidth = card?.clientWidth || 120\n this._carousel.scrollTo({\n left: this._scrollIndex * (itemWidth + 8),\n behavior: 'smooth'\n })\n }\n this._updateNavButtons()\n }\n\n /**\n * Update navigation button states\n */\n _updateNavButtons() {\n const maxIndex = Math.max(0, this._filterCards.size - 3)\n if (this._leftBtn) {\n this._leftBtn.disabled = this._scrollIndex === 0\n }\n if (this._rightBtn) {\n this._rightBtn.disabled = this._scrollIndex >= maxIndex\n }\n }\n\n /**\n * Clean up\n */\n destroy() {\n this._unsubscribers.forEach(unsub => unsub())\n this._unsubscribers = []\n this._filterCards.clear()\n this.element?.remove()\n this.element = null\n }\n}\n","/**\n * Filter Adjustments component for the vanilla image editor\n * Dynamic slider controls for the selected filter\n */\nimport { el, createSlider, createToggle, createColorPicker, createSelect, createButton, createTextInput } from './UIBuilder.js'\n\nexport class FilterAdjustments {\n constructor(state, filterManager) {\n this.state = state\n this.filterManager = filterManager\n this.element = null\n this._controls = new Map()\n this._onChange = null\n this._onReset = null\n this._onAction = null\n this._unsubscribers = []\n }\n\n /**\n * Create and render the filter adjustments panel\n * @param {Object} options - { onChange, onReset, onAction }\n * @returns {HTMLElement}\n */\n render({ onChange, onReset, onAction }) {\n this._onChange = onChange\n this._onReset = onReset\n this._onAction = onAction\n\n this.element = el('div', { className: 'filter-adjustments' })\n\n // Subscribe to state changes\n this._subscribeToState()\n\n // Initial render\n this._renderControls()\n\n return this.element\n }\n\n /**\n * Subscribe to state changes\n */\n _subscribeToState() {\n // Re-render when selected filter changes\n const unsubSelected = this.state.on('change:selectedFilter', () => {\n this._renderControls()\n })\n this._unsubscribers.push(unsubSelected)\n\n // Update values when filter values change\n const unsubValues = this.state.on('change:filterValues', () => {\n this._updateValues()\n })\n this._unsubscribers.push(unsubValues)\n }\n\n /**\n * Render controls for the selected filter.\n *\n * Renders both the static controls from def.controls and any dynamic\n * controls the filter instance exposes via getDynamicControls() (e.g.\n * per-stop color/offset/alpha rows for Color Gradient).\n */\n _renderControls() {\n this.element.innerHTML = ''\n this._controls.clear()\n\n const filterId = this.state.get('selectedFilter')\n if (!filterId) {\n this.element.appendChild(\n el('div', { className: 'no-filter-selected' }, 'Select a filter to adjust')\n )\n return\n }\n\n const def = this.filterManager.getFilterDef(filterId)\n if (!def) {\n this.element.appendChild(\n el('div', { className: 'no-filter-selected' }, 'Filter not found')\n )\n return\n }\n\n // Header with filter name and reset button\n const header = el('div', { className: 'adjustments-header' })\n header.appendChild(el('span', { className: 'adjustments-title' }, def.name))\n header.appendChild(createButton({\n label: 'Reset',\n className: 'btn-text',\n onClick: () => this._handleReset(filterId)\n }))\n this.element.appendChild(header)\n\n // Controls grid\n const grid = el('div', { className: 'adjustments-grid' })\n\n const values = this.state.getFilterValues(filterId)\n\n // 1. Static controls from the filter definition\n ;(def.controls || []).forEach(ctl => {\n const value = values[ctl.id] ?? ctl.default\n const controlEl = this._createControl(filterId, ctl, value)\n if (controlEl) {\n this._controls.set(ctl.id, controlEl)\n grid.appendChild(controlEl)\n }\n })\n\n // 2. Dynamic controls from the filter instance (e.g. color stops)\n const instance = this.filterManager.getInstance(filterId)\n if (instance && typeof instance.getDynamicControls === 'function') {\n let dynamicControls = []\n try {\n dynamicControls = instance.getDynamicControls() || []\n } catch (err) {\n console.error('[FilterAdjustments] getDynamicControls threw:', err)\n }\n dynamicControls.forEach(ctl => {\n // Dynamic controls use their 'property' path as the update key\n // (e.g. 'colorStops[0].color'). Fall back to id.\n const stateKey = ctl.property || ctl.id\n const value = values[stateKey] ?? ctl.default\n const controlEl = this._createControl(filterId, ctl, value)\n if (controlEl) {\n this._controls.set(ctl.id, controlEl)\n grid.appendChild(controlEl)\n }\n })\n }\n\n this.element.appendChild(grid)\n }\n\n /**\n * Create a control element based on type.\n *\n * The update key passed to _handleChange is `ctl.property || ctl.id`.\n * Dynamic controls (e.g. from getDynamicControls) use their property\n * path (e.g. 'colorStops[0].color') so FilterManager.updateValue can\n * route the write correctly. Static controls without a property fall\n * back to the control id, matching the legacy contract.\n *\n * @param {string} filterId\n * @param {Object} ctl - Control definition\n * @param {*} value - Current value\n * @returns {HTMLElement|null}\n */\n _createControl(filterId, ctl, value) {\n // Skip hidden controls\n if (ctl.hidden) return null\n\n // Get the control label (prefer label, fallback to id)\n const label = ctl.label || ctl.id\n\n // Normalize control type (handle aliases)\n const controlType = this._normalizeControlType(ctl.type)\n\n // Update key: dynamic controls pass their property path; static ones\n // use their id (FilterManager.updateValue resolves property via the\n // control definition for static controls).\n const updateKey = ctl.property || ctl.id\n\n switch (controlType) {\n case 'slider':\n return createSlider({\n id: `${filterId}-${ctl.id}`,\n label: label,\n min: ctl.min ?? 0,\n max: ctl.max ?? 1,\n step: ctl.step ?? 0.01,\n value: typeof value === 'number' ? value : (ctl.default ?? 0),\n onChange: (val) => this._handleChange(filterId, updateKey, val)\n })\n\n case 'toggle':\n return createToggle({\n id: `${filterId}-${ctl.id}`,\n label: label,\n checked: !!value,\n onChange: (val) => this._handleChange(filterId, updateKey, val)\n })\n\n case 'color':\n return createColorPicker({\n id: `${filterId}-${ctl.id}`,\n label: label,\n value: typeof value === 'string' && value.startsWith('#') ? value : (ctl.default || '#000000'),\n onChange: (val) => this._handleChange(filterId, updateKey, val)\n })\n\n case 'select':\n // Normalize options format\n const options = this._normalizeOptions(ctl.options)\n return createSelect({\n id: `${filterId}-${ctl.id}`,\n label: label,\n options: options,\n value: value ?? ctl.default,\n onChange: (val) => this._handleChange(filterId, updateKey, val)\n })\n\n case 'text':\n return createTextInput({\n id: `${filterId}-${ctl.id}`,\n label: label,\n value: typeof value === 'string' ? value : (ctl.default ?? ''),\n placeholder: ctl.placeholder || '',\n onCommit: (val) => this._handleChange(filterId, updateKey, val)\n })\n\n case 'button':\n const wrapper = el('div', { className: 'button-control' })\n wrapper.appendChild(createButton({\n label: label,\n className: 'btn-secondary',\n onClick: () => this._handleAction(filterId, ctl.action || ctl.id)\n }))\n return wrapper\n\n default:\n console.warn(`[FilterAdjustments] Unknown control type: ${ctl.type}`)\n return null\n }\n }\n\n /**\n * Normalize control type (handle aliases)\n * @param {string} type\n * @returns {string}\n */\n _normalizeControlType(type) {\n const typeMap = {\n 'slider': 'slider',\n 'range': 'slider',\n 'toggle': 'toggle',\n 'checkbox': 'toggle',\n 'color': 'color',\n 'select': 'select',\n 'dropdown': 'select',\n 'button': 'button',\n 'text': 'text'\n }\n return typeMap[type] || type\n }\n\n /**\n * Normalize options array format\n * @param {Array} options\n * @returns {Array}\n */\n _normalizeOptions(options) {\n if (!options || !Array.isArray(options)) return []\n\n return options.map(opt => {\n // Already in { value, label } format\n if (typeof opt === 'object' && opt.value !== undefined) {\n return { value: opt.value, label: opt.label || String(opt.value) }\n }\n // Simple string value\n if (typeof opt === 'string') {\n return { value: opt, label: opt }\n }\n // Number value\n if (typeof opt === 'number') {\n return { value: opt, label: String(opt) }\n }\n return { value: opt, label: String(opt) }\n })\n }\n\n /**\n * Handle control value change\n * @param {string} filterId\n * @param {string} controlId\n * @param {*} value\n */\n _handleChange(filterId, controlId, value) {\n this._onChange?.(filterId, controlId, value)\n }\n\n /**\n * Handle reset button\n * @param {string} filterId\n */\n _handleReset(filterId) {\n this._onReset?.(filterId)\n this._renderControls() // Re-render to show default values\n }\n\n /**\n * Handle button action. Dynamic controls (e.g. color stop rows) depend on\n * filter state that can change as a result of the action (add/remove stop),\n * so re-render after the action completes.\n *\n * @param {string} filterId\n * @param {string} action\n */\n _handleAction(filterId, action) {\n this._onAction?.(filterId, action)\n this._renderControls()\n }\n\n /**\n * Update control values from state\n */\n _updateValues() {\n const filterId = this.state.get('selectedFilter')\n if (!filterId) return\n\n const values = this.state.getFilterValues(filterId)\n const def = this.filterManager.getFilterDef(filterId)\n if (!def) return\n\n def.controls.forEach(ctl => {\n const controlEl = this._controls.get(ctl.id)\n if (controlEl && typeof controlEl.setValue === 'function') {\n const value = values[ctl.id] ?? ctl.default\n controlEl.setValue(value)\n }\n })\n }\n\n /**\n * Show panel\n */\n show() {\n if (this.element) {\n this.element.style.display = ''\n }\n }\n\n /**\n * Hide panel\n */\n hide() {\n if (this.element) {\n this.element.style.display = 'none'\n }\n }\n\n /**\n * Clean up\n */\n destroy() {\n this._unsubscribers.forEach(unsub => unsub())\n this._unsubscribers = []\n this._controls.clear()\n this.element?.remove()\n this.element = null\n }\n}\n","/**\n * Mobile Filter Drawer - Inline panel that replaces carousels for filter adjustments\n * Sized to match the controls section height, scrollable within that space\n */\nimport { el, createSlider, createToggle, createColorPicker, createSelect, createButton, createIconButton, createTextInput } from './UIBuilder.js'\nimport * as icons from '../icons/icons.js'\n\nexport class MobileFilterDrawer {\n constructor(state, filterManager) {\n this.state = state\n this.filterManager = filterManager\n this._drawer = null\n this._body = null\n this._titleEl = null\n this._isOpen = false\n this._currentFilterId = null\n this._controls = new Map()\n this._onChange = null\n this._onReset = null\n this._onRemove = null\n this._onAction = null\n this._container = null\n }\n\n /**\n * Build the drawer DOM and append to container (should be the controls section)\n * @param {HTMLElement} container - Parent element (controls-section)\n * @param {Object} callbacks - { onChange, onReset, onRemove, onAction }\n */\n build(container, { onChange, onReset, onRemove, onAction }) {\n this._onChange = onChange\n this._onReset = onReset\n this._onRemove = onRemove\n this._onAction = onAction\n this._container = container\n\n // Drawer panel — absolutely positioned within controls section\n this._drawer = el('div', { className: 'mobile-filter-drawer' })\n\n // Header — compact: filter name + reset + close\n const header = el('div', { className: 'drawer-header' })\n this._titleEl = el('span', { className: 'drawer-title' }, 'Filter')\n\n const headerActions = el('div', { className: 'drawer-header-actions' })\n\n const resetBtn = createButton({\n label: 'Reset',\n className: 'btn-text',\n onClick: () => {\n if (this._currentFilterId) {\n this._onReset?.(this._currentFilterId)\n this._renderControls()\n }\n }\n })\n\n const removeBtn = createButton({\n label: 'Remove',\n className: 'btn-text btn-danger',\n onClick: () => {\n if (this._currentFilterId) {\n this._onRemove?.(this._currentFilterId)\n this.close()\n }\n }\n })\n\n const closeBtn = createIconButton({\n icon: icons.close,\n title: 'Close',\n className: 'btn-icon-sm',\n ariaLabel: 'Close drawer',\n onClick: () => this.close()\n })\n\n headerActions.appendChild(resetBtn)\n headerActions.appendChild(removeBtn)\n headerActions.appendChild(closeBtn)\n header.appendChild(this._titleEl)\n header.appendChild(headerActions)\n this._drawer.appendChild(header)\n\n // Body (scrollable controls area)\n this._body = el('div', { className: 'drawer-body' })\n this._drawer.appendChild(this._body)\n\n // Append to container\n container.appendChild(this._drawer)\n }\n\n /**\n * Open the drawer for a specific filter\n * @param {string} filterId\n */\n open(filterId) {\n if (this._isOpen && this._currentFilterId === filterId) return\n\n this._currentFilterId = filterId\n this._renderControls()\n\n if (!this._isOpen) {\n this._isOpen = true\n this._sizeToContainer()\n this._animateOpen()\n }\n }\n\n /**\n * Close the drawer\n */\n close() {\n if (!this._isOpen) return\n this._isOpen = false\n this._animateClose()\n this._currentFilterId = null\n }\n\n /**\n * Check if drawer is open\n * @returns {boolean}\n */\n get isOpen() {\n return this._isOpen\n }\n\n /**\n * Size the drawer to match the container (controls section) height\n */\n _sizeToContainer() {\n if (!this._container || !this._drawer) return\n const h = this._container.offsetHeight\n if (h > 0) {\n this._drawer.style.height = h + 'px'\n }\n }\n\n /**\n * Animate drawer open (CSS transition)\n */\n _animateOpen() {\n this._drawer.style.transition = 'none'\n this._drawer.style.transform = 'translateY(100%)'\n this._drawer.offsetHeight // force reflow\n this._drawer.style.transition = 'transform 0.25s cubic-bezier(0.33, 1, 0.68, 1)'\n this._drawer.style.transform = 'translateY(0)'\n }\n\n /**\n * Animate drawer close (CSS transition)\n */\n _animateClose() {\n this._drawer.style.transition = 'transform 0.2s cubic-bezier(0.32, 0, 0.67, 0)'\n this._drawer.style.transform = 'translateY(100%)'\n }\n\n /**\n * Render controls for the current filter.\n * Renders both static (def.controls) and dynamic (instance.getDynamicControls)\n * controls so filters like Color Gradient show per-stop rows.\n */\n _renderControls() {\n this._body.innerHTML = ''\n this._controls.clear()\n\n if (!this._currentFilterId) return\n\n const def = this.filterManager.getFilterDef(this._currentFilterId)\n if (!def) return\n\n this._titleEl.textContent = def.name\n\n const grid = el('div', { className: 'adjustments-grid' })\n const values = this.state.getFilterValues(this._currentFilterId)\n\n // Static controls\n ;(def.controls || []).forEach(ctl => {\n if (ctl.hidden) return\n const controlEl = this._createControl(this._currentFilterId, ctl, values[ctl.id] ?? ctl.default)\n if (controlEl) {\n this._controls.set(ctl.id, controlEl)\n grid.appendChild(controlEl)\n }\n })\n\n // Dynamic controls (e.g. per-stop color gradient rows)\n const instance = this.filterManager.getInstance(this._currentFilterId)\n if (instance && typeof instance.getDynamicControls === 'function') {\n let dynamicControls = []\n try {\n dynamicControls = instance.getDynamicControls() || []\n } catch (err) {\n console.error('[MobileFilterDrawer] getDynamicControls threw:', err)\n }\n dynamicControls.forEach(ctl => {\n const stateKey = ctl.property || ctl.id\n const value = values[stateKey] ?? ctl.default\n const controlEl = this._createControl(this._currentFilterId, ctl, value)\n if (controlEl) {\n this._controls.set(ctl.id, controlEl)\n grid.appendChild(controlEl)\n }\n })\n }\n\n this._body.appendChild(grid)\n }\n\n /**\n * Create a control element based on type. See FilterAdjustments._createControl\n * for the update-key contract (property path for dynamic controls, id for static).\n * @param {string} filterId\n * @param {Object} ctl\n * @param {*} value\n * @returns {HTMLElement|null}\n */\n _createControl(filterId, ctl, value) {\n if (ctl.hidden) return null\n const label = ctl.label || ctl.id\n const controlType = this._normalizeControlType(ctl.type)\n const updateKey = ctl.property || ctl.id\n\n switch (controlType) {\n case 'slider':\n return createSlider({\n id: `drawer-${filterId}-${ctl.id}`,\n label,\n min: ctl.min ?? 0,\n max: ctl.max ?? 1,\n step: ctl.step ?? 0.01,\n value: typeof value === 'number' ? value : (ctl.default ?? 0),\n onChange: (val) => this._onChange?.(filterId, updateKey, val)\n })\n\n case 'toggle':\n return createToggle({\n id: `drawer-${filterId}-${ctl.id}`,\n label,\n checked: !!value,\n onChange: (val) => this._onChange?.(filterId, updateKey, val)\n })\n\n case 'color':\n return createColorPicker({\n id: `drawer-${filterId}-${ctl.id}`,\n label,\n value: typeof value === 'string' && value.startsWith('#') ? value : (ctl.default || '#000000'),\n onChange: (val) => this._onChange?.(filterId, updateKey, val)\n })\n\n case 'select': {\n const options = this._normalizeOptions(ctl.options)\n return createSelect({\n id: `drawer-${filterId}-${ctl.id}`,\n label,\n options,\n value: value ?? ctl.default,\n onChange: (val) => this._onChange?.(filterId, updateKey, val)\n })\n }\n\n case 'text':\n return createTextInput({\n id: `drawer-${filterId}-${ctl.id}`,\n label,\n value: typeof value === 'string' ? value : (ctl.default ?? ''),\n placeholder: ctl.placeholder || '',\n onCommit: (val) => this._onChange?.(filterId, updateKey, val)\n })\n\n case 'button': {\n const wrapper = el('div', { className: 'button-control' })\n wrapper.appendChild(createButton({\n label,\n className: 'btn-secondary',\n onClick: () => {\n this._onAction?.(filterId, ctl.action || ctl.id)\n // Re-render so dynamic controls pick up state changes from the action\n this._renderControls()\n }\n }))\n return wrapper\n }\n\n default:\n return null\n }\n }\n\n /**\n * Normalize control type\n * @param {string} type\n * @returns {string}\n */\n _normalizeControlType(type) {\n const typeMap = {\n 'slider': 'slider',\n 'range': 'slider',\n 'toggle': 'toggle',\n 'checkbox': 'toggle',\n 'color': 'color',\n 'select': 'select',\n 'dropdown': 'select',\n 'button': 'button',\n 'text': 'text'\n }\n return typeMap[type] || type\n }\n\n /**\n * Normalize options array\n * @param {Array} options\n * @returns {Array}\n */\n _normalizeOptions(options) {\n if (!options || !Array.isArray(options)) return []\n return options.map(opt => {\n if (typeof opt === 'object' && opt.value !== undefined) {\n return { value: opt.value, label: opt.label || String(opt.value) }\n }\n if (typeof opt === 'string') return { value: opt, label: opt }\n if (typeof opt === 'number') return { value: opt, label: String(opt) }\n return { value: opt, label: String(opt) }\n })\n }\n\n /**\n * Clean up\n */\n destroy() {\n this._controls.clear()\n this._drawer?.remove()\n this._drawer = null\n this._isOpen = false\n this._currentFilterId = null\n this._container = null\n }\n}\n","/**\n * Mobile Active Filters Carousel\n * Horizontal scrollable row of active filter chips with checkmark toggles.\n * Shown below the filter carousel on mobile only.\n */\nimport { el } from './UIBuilder.js'\n\nexport class MobileActiveFilters {\n constructor(state, filterManager) {\n this.state = state\n this.filterManager = filterManager\n this.element = null\n this._scrollContainer = null\n this._chips = new Map()\n this._unsubscribers = []\n this._onToggle = null\n this._onSelect = null\n }\n\n /**\n * @param {Object} callbacks - { onToggle(filterId, enabled), onSelect(filterId) }\n * @returns {HTMLElement}\n */\n render({ onToggle, onSelect }) {\n this._onToggle = onToggle\n this._onSelect = onSelect\n\n this.element = el('div', {\n className: 'mobile-active-filters',\n 'data-testid': 'mobile-active-filters'\n })\n\n this._scrollContainer = el('div', { className: 'mobile-active-filters-scroll' })\n this.element.appendChild(this._scrollContainer)\n\n this._subscribeToState()\n this._renderChips()\n\n return this.element\n }\n\n _subscribeToState() {\n const unsubActive = this.state.on('change:activeFilters', () => {\n this._renderChips()\n })\n this._unsubscribers.push(unsubActive)\n\n const unsubSelected = this.state.on('change:selectedFilter', () => {\n this._updateSelectedState()\n })\n this._unsubscribers.push(unsubSelected)\n }\n\n _renderChips() {\n this._scrollContainer.innerHTML = ''\n this._chips.clear()\n\n const activeFilters = this.state.get('activeFilters')\n\n if (!activeFilters || activeFilters.size === 0) {\n const placeholder = el('div', {\n className: 'active-filter-placeholder'\n }, 'No active filters')\n this._scrollContainer.appendChild(placeholder)\n return\n }\n\n activeFilters.forEach(filterId => {\n const def = this.filterManager.getFilterDef(filterId)\n if (!def) return\n\n const chip = this._createChip(filterId, def)\n this._chips.set(filterId, chip)\n this._scrollContainer.appendChild(chip)\n })\n\n this._updateSelectedState()\n }\n\n _createChip(filterId, def) {\n const chip = el('div', {\n className: 'active-filter-chip',\n 'data-filter-id': filterId,\n 'data-testid': `active-chip-${filterId}`\n })\n\n // Filter name — tap to open drawer\n const label = el('span', {\n className: 'active-filter-chip-label',\n onClick: (e) => {\n e.stopPropagation()\n this._onSelect?.(filterId)\n }\n }, def.name)\n\n // Checkmark toggle — tap to remove filter\n const check = el('button', {\n className: 'active-filter-chip-check',\n 'aria-label': `Remove ${def.name} filter`,\n onClick: (e) => {\n e.stopPropagation()\n this._onToggle?.(filterId, false)\n }\n })\n check.innerHTML = '<svg width=\"10\" height=\"10\" viewBox=\"0 0 12 12\"><path d=\"M2 6l3 3 5-5\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>'\n\n chip.appendChild(label)\n chip.appendChild(check)\n\n return chip\n }\n\n _updateSelectedState() {\n const selectedFilter = this.state.get('selectedFilter')\n this._chips.forEach((chip, filterId) => {\n chip.classList.toggle('selected', filterId === selectedFilter)\n })\n }\n\n destroy() {\n this._unsubscribers.forEach(unsub => unsub())\n this._unsubscribers = []\n this._chips.clear()\n this.element?.remove()\n this.element = null\n }\n}\n","/**\n * Crop Controls component for the vanilla image editor\n * Provides shape selection and aspect ratio controls\n */\nimport { el, createButton, createChip } from './UIBuilder.js'\nimport * as icons from '../icons/icons.js'\n\nconst SHAPES = [\n { id: 'free', name: 'Free', icon: icons.freeform },\n { id: 'square', name: 'Square', icon: icons.square },\n { id: 'circle', name: 'Circle', icon: icons.circle }\n]\n\nconst ASPECT_RATIOS = [\n { id: 'free', name: 'Free' },\n { id: '1:1', name: '1:1' },\n { id: '4:3', name: '4:3' },\n { id: '16:9', name: '16:9' },\n { id: '3:2', name: '3:2' },\n { id: '2:3', name: '2:3' }\n]\n\nconst ROTATION_PRESETS = [\n { id: '0', label: '0°', angle: 0 },\n { id: '90', label: '90°', angle: 90 },\n { id: '180', label: '180°', angle: 180 },\n { id: '270', label: '270°', angle: 270 }\n]\n\nfunction normalizeAngle(angle) {\n if (!Number.isFinite(Number(angle))) return 0\n const normalized = Number(angle) % 360\n return normalized < 0 ? normalized + 360 : normalized\n}\n\nexport class CropControls {\n constructor(state, cropManager, editor = null) {\n this.state = state\n this.cropManager = cropManager\n this.editor = editor\n this.element = null\n this._shapeChips = new Map()\n this._aspectChips = new Map()\n this._rotationPresetChips = new Map()\n this._rotationRange = null\n this._rotationNumber = null\n this._rotationValue = null\n this._unsubscribers = []\n }\n\n /**\n * Apply crop and return the editor UI to filter mode.\n * If crop application cannot complete, force-disable crop mode to avoid\n * leaving the editor stuck in crop controls.\n */\n _applyCropAndReturnToFilters() {\n const result = this.cropManager.apply()\n\n if (result === null && typeof this.cropManager.disable === 'function') {\n this.cropManager.disable()\n }\n\n this.state.set('selectedCategory', 'adjust')\n }\n\n /**\n * Create and render the crop controls\n * @returns {HTMLElement}\n */\n render() {\n this.element = el('div', {\n className: 'crop-controls',\n 'data-testid': 'crop-controls'\n })\n\n this.element.appendChild(this._renderRotationSection())\n\n // Shape selection section\n const shapeSection = el('div', { className: 'crop-section' })\n shapeSection.appendChild(el('label', { className: 'section-label' }, 'Shape'))\n\n const shapeRow = el('div', { className: 'chip-row' })\n const currentShape = this.state.get('crop.shape')\n\n SHAPES.forEach(shape => {\n const chip = createChip({\n label: shape.name,\n icon: shape.icon,\n active: currentShape === shape.id,\n onClick: () => this._selectShape(shape.id)\n })\n chip.dataset.shape = shape.id\n chip.dataset.testid = `crop-shape-${shape.id}`\n this._shapeChips.set(shape.id, chip)\n shapeRow.appendChild(chip)\n })\n\n shapeSection.appendChild(shapeRow)\n this.element.appendChild(shapeSection)\n\n // Hide shape section when shape is locked (preset enforces a specific shape)\n if (this.state.get('lockCropShape')) {\n shapeSection.style.display = 'none'\n }\n this._shapeSection = shapeSection\n\n // Aspect ratio section (only for free shape)\n this._aspectSection = el('div', { className: 'crop-section' })\n this._aspectSection.appendChild(el('label', { className: 'section-label' }, 'Aspect Ratio'))\n\n const aspectRow = el('div', { className: 'chip-row aspect-row' })\n const currentAspect = this.state.get('crop.aspect')\n\n ASPECT_RATIOS.forEach(aspect => {\n const chip = createChip({\n label: aspect.name,\n active: currentAspect === aspect.id,\n onClick: () => this._selectAspect(aspect.id)\n })\n chip.dataset.ratio = aspect.id\n chip.dataset.testid = `crop-ratio-${aspect.id}`\n this._aspectChips.set(aspect.id, chip)\n aspectRow.appendChild(chip)\n })\n\n this._aspectSection.appendChild(aspectRow)\n this.element.appendChild(this._aspectSection)\n\n // Show/hide aspect ratios based on shape\n this._updateAspectVisibility()\n\n // Action buttons\n const actionRow = el('div', { className: 'crop-actions' })\n\n const cancelBtn = createButton({\n label: 'Cancel',\n className: 'btn-secondary crop-cancel-btn',\n icon: icons.close,\n onClick: () => this.cropManager.cancel()\n })\n cancelBtn.dataset.testid = 'cancel-crop'\n\n const applyBtn = createButton({\n label: 'Apply Crop',\n className: 'btn-primary crop-apply-btn',\n icon: icons.checkmark,\n onClick: () => this._applyCropAndReturnToFilters()\n })\n applyBtn.dataset.testid = 'apply-crop'\n\n actionRow.appendChild(cancelBtn)\n actionRow.appendChild(applyBtn)\n this.element.appendChild(actionRow)\n\n // Subscribe to state changes\n this._subscribeToState()\n\n return this.element\n }\n\n _renderRotationSection() {\n const section = el('div', { className: 'crop-section rotation-section' })\n section.appendChild(el('label', { className: 'section-label' }, 'Rotate'))\n\n const quickRow = el('div', { className: 'rotation-action-row' })\n const rotateLeft = createButton({\n label: 'Left 90°',\n className: 'btn-secondary rotation-action-btn',\n icon: icons.rotateCcw,\n onClick: () => this._rotateBy(-90)\n })\n rotateLeft.dataset.testid = 'rotate-left-90'\n\n const rotateRight = createButton({\n label: 'Right 90°',\n className: 'btn-secondary rotation-action-btn',\n icon: icons.rotateCw,\n onClick: () => this._rotateBy(90)\n })\n rotateRight.dataset.testid = 'rotate-right-90'\n\n const rotateHalf = createButton({\n label: 'Rotate 180°',\n className: 'btn-secondary rotation-action-btn',\n onClick: () => this._rotateBy(180)\n })\n rotateHalf.dataset.testid = 'rotate-180'\n\n const rotateThreeQuarter = createButton({\n label: 'Rotate 270°',\n className: 'btn-secondary rotation-action-btn',\n onClick: () => this._rotateBy(270)\n })\n rotateThreeQuarter.dataset.testid = 'rotate-270'\n\n quickRow.appendChild(rotateLeft)\n quickRow.appendChild(rotateRight)\n quickRow.appendChild(rotateHalf)\n quickRow.appendChild(rotateThreeQuarter)\n section.appendChild(quickRow)\n\n const presetRow = el('div', { className: 'chip-row rotation-preset-row' })\n const current = normalizeAngle(this.state.get('transform.rotation') ?? 0)\n ROTATION_PRESETS.forEach(preset => {\n const chip = createChip({\n label: preset.label,\n active: Math.round(current) === preset.angle,\n onClick: () => this._setRotation(preset.angle)\n })\n chip.dataset.angle = preset.id\n chip.dataset.testid = `rotation-preset-${preset.id}`\n this._rotationPresetChips.set(preset.id, chip)\n presetRow.appendChild(chip)\n })\n section.appendChild(presetRow)\n\n const arbitrary = el('div', { className: 'rotation-arbitrary' })\n const header = el('div', { className: 'rotation-angle-header' },\n el('label', { className: 'rotation-angle-label', for: 'image-editor-rotation-angle' }, 'Angle'),\n el('span', { className: 'rotation-angle-value' }, this._formatAngle(current))\n )\n this._rotationValue = header.querySelector('.rotation-angle-value')\n\n this._rotationRange = el('input', {\n id: 'image-editor-rotation-angle',\n type: 'range',\n className: 'slider-input rotation-range',\n min: '0',\n max: '360',\n step: '1',\n value: String(Math.round(current)),\n 'aria-label': 'Rotation angle',\n onInput: (event) => this._previewAngleValue(event.target.value),\n onChange: (event) => this._setRotation(Number(event.target.value))\n })\n\n this._rotationNumber = el('input', {\n type: 'number',\n className: 'text-input rotation-number-input',\n min: '-360',\n max: '360',\n step: '1',\n value: this._formatPlainAngle(current),\n 'aria-label': 'Rotation angle in degrees',\n onKeyDown: (event) => {\n if (event.key === 'Enter') {\n event.preventDefault()\n this._setRotation(Number(event.target.value))\n event.target.blur()\n }\n },\n onBlur: (event) => this._setRotation(Number(event.target.value))\n })\n\n const inputRow = el('div', { className: 'rotation-angle-row' },\n this._rotationRange,\n this._rotationNumber\n )\n\n arbitrary.appendChild(header)\n arbitrary.appendChild(inputRow)\n\n const reset = createButton({\n label: 'Reset rotation',\n className: 'btn-text rotation-reset-btn',\n onClick: () => this._setRotation(0)\n })\n reset.dataset.testid = 'reset-rotation'\n arbitrary.appendChild(reset)\n\n section.appendChild(arbitrary)\n\n return section\n }\n\n /**\n * Subscribe to state changes\n */\n _subscribeToState() {\n // Shape changes\n const unsubShape = this.state.on('change:crop.shape', ({ value }) => {\n this._shapeChips.forEach((chip, id) => {\n chip.setActive(id === value)\n })\n this._updateAspectVisibility()\n })\n this._unsubscribers.push(unsubShape)\n\n // Aspect changes\n const unsubAspect = this.state.on('change:crop.aspect', ({ value }) => {\n this._aspectChips.forEach((chip, id) => {\n chip.setActive(id === value)\n })\n })\n this._unsubscribers.push(unsubAspect)\n\n const unsubRotation = this.state.on('change:transform.rotation', ({ value }) => {\n this._syncRotationControls(value)\n })\n this._unsubscribers.push(unsubRotation)\n }\n\n _rotateBy(delta) {\n const rotated = this.editor?.rotateBy?.(delta)\n if (rotated === false) return\n this._syncRotationControls(this.state.get('transform.rotation') ?? 0)\n }\n\n _setRotation(angle) {\n const normalized = normalizeAngle(angle)\n const rotated = this.editor?.setRotationAngle?.(normalized)\n if (rotated === false) {\n this._syncRotationControls(this.state.get('transform.rotation') ?? 0)\n return\n }\n this._syncRotationControls(normalized)\n }\n\n _previewAngleValue(rawValue) {\n const normalized = normalizeAngle(Number(rawValue))\n if (this._rotationValue) {\n this._rotationValue.textContent = this._formatAngle(normalized)\n }\n if (this._rotationNumber) {\n this._rotationNumber.value = this._formatPlainAngle(normalized)\n }\n this._refreshPresetChips(normalized)\n }\n\n _syncRotationControls(value) {\n const normalized = normalizeAngle(value)\n if (this._rotationValue) {\n this._rotationValue.textContent = this._formatAngle(normalized)\n }\n if (this._rotationRange) {\n this._rotationRange.value = String(Math.round(normalized))\n }\n if (this._rotationNumber) {\n this._rotationNumber.value = this._formatPlainAngle(normalized)\n }\n this._refreshPresetChips(normalized)\n }\n\n _refreshPresetChips(angle) {\n const rounded = Math.round(normalizeAngle(angle)) % 360\n this._rotationPresetChips.forEach((chip, id) => {\n chip.setActive(Number(id) === rounded)\n })\n }\n\n _formatAngle(angle) {\n return `${this._formatPlainAngle(angle)}°`\n }\n\n _formatPlainAngle(angle) {\n const normalized = normalizeAngle(angle)\n return Number.isInteger(normalized) ? String(normalized) : normalized.toFixed(1)\n }\n\n /**\n * Select a crop shape\n * @param {string} shapeId\n */\n _selectShape(shapeId) {\n this.cropManager.setShape(shapeId)\n }\n\n /**\n * Select an aspect ratio\n * @param {string} aspectId\n */\n _selectAspect(aspectId) {\n this.cropManager.setAspect(aspectId)\n }\n\n /**\n * Update aspect ratio section visibility\n */\n _updateAspectVisibility() {\n const shape = this.state.get('crop.shape')\n const lockAspect = this.state.get('lockAspectRatio')\n // Hide aspect ratios when: shape is not free (circle/square are always 1:1),\n // OR aspect ratio is locked by preset\n if (this._aspectSection) {\n this._aspectSection.style.display = (shape === 'free' && !lockAspect) ? '' : 'none'\n }\n }\n\n /**\n * Show controls\n */\n show() {\n if (this.element) {\n this.element.style.display = ''\n }\n }\n\n /**\n * Hide controls\n */\n hide() {\n if (this.element) {\n this.element.style.display = 'none'\n }\n }\n\n /**\n * Clean up\n */\n destroy() {\n this._unsubscribers.forEach(unsub => unsub())\n this._unsubscribers = []\n this._shapeChips.clear()\n this._aspectChips.clear()\n this._rotationPresetChips.clear()\n this.element?.remove()\n this.element = null\n }\n}\n","/**\n * Active Filters Panel component for the vanilla image editor\n * Displays currently active filters with quick controls\n */\nimport { el, createButton, createIconButton } from './UIBuilder.js'\nimport * as icons from '../icons/icons.js'\n\nexport class ActiveFiltersPanel {\n constructor(state, filterManager) {\n this.state = state\n this.filterManager = filterManager\n this.element = null\n this._filterItems = new Map()\n this._unsubscribers = []\n this._onRemove = null\n this._onReset = null\n this._onClearAll = null\n this._onUpdateValue = null\n this._onSelect = null\n }\n\n /**\n * Create and render the active filters panel\n * @param {Object} options - { onRemove, onReset, onClearAll, onUpdateValue, onSelect }\n * @returns {HTMLElement}\n */\n render({ onRemove, onReset, onClearAll, onUpdateValue, onSelect }) {\n this._onRemove = onRemove\n this._onReset = onReset\n this._onClearAll = onClearAll\n this._onUpdateValue = onUpdateValue\n this._onSelect = onSelect\n\n this.element = el('div', {\n className: 'active-filters-panel',\n 'data-testid': 'active-filters-panel'\n })\n\n // Header\n const header = el('div', { className: 'panel-header' })\n header.appendChild(el('h3', { className: 'panel-title' }, 'Active Filters'))\n const clearAllBtn = createButton({\n label: 'Clear All',\n className: 'btn-text btn-danger',\n onClick: () => this._handleClearAll()\n })\n clearAllBtn.dataset.testid = 'clear-all-filters'\n header.appendChild(clearAllBtn)\n this.element.appendChild(header)\n\n // Filter list container\n this._listContainer = el('div', { className: 'active-filters-list' })\n this.element.appendChild(this._listContainer)\n\n // Subscribe to state changes\n this._subscribeToState()\n\n // Initial render\n this._renderFilterList()\n\n return this.element\n }\n\n /**\n * Subscribe to state changes\n */\n _subscribeToState() {\n // Re-render when active filters change\n const unsubActive = this.state.on('change:activeFilters', () => {\n this._renderFilterList()\n })\n this._unsubscribers.push(unsubActive)\n\n // Update values display\n const unsubValues = this.state.on('change:filterValues', () => {\n this._updateValuesDisplay()\n })\n this._unsubscribers.push(unsubValues)\n\n // Update selected state\n const unsubSelected = this.state.on('change:selectedFilter', () => {\n this._updateSelectedState()\n })\n this._unsubscribers.push(unsubSelected)\n }\n\n /**\n * Update selected state for all items\n */\n _updateSelectedState() {\n const selectedFilter = this.state.get('selectedFilter')\n this._filterItems.forEach((item, filterId) => {\n item.classList.toggle('selected', filterId === selectedFilter)\n })\n }\n\n /**\n * Render the filter list\n */\n _renderFilterList() {\n this._listContainer.innerHTML = ''\n this._filterItems.clear()\n\n const activeFilters = this.state.get('activeFilters')\n\n if (activeFilters.size === 0) {\n this._listContainer.appendChild(\n el('div', { className: 'no-filters-message' }, 'No filters active')\n )\n return\n }\n\n activeFilters.forEach(filterId => {\n const def = this.filterManager.getFilterDef(filterId)\n if (!def) return\n\n const item = this._createFilterItem(filterId, def)\n this._filterItems.set(filterId, item)\n this._listContainer.appendChild(item)\n })\n }\n\n /**\n * Create a filter item\n * @param {string} filterId\n * @param {Object} def - Filter definition\n * @returns {HTMLElement}\n */\n _createFilterItem(filterId, def) {\n const selectedFilter = this.state.get('selectedFilter')\n const isSelected = selectedFilter === filterId\n\n const item = el('div', {\n className: `active-filter-item ${isSelected ? 'selected' : ''}`,\n 'data-active-filter': filterId,\n 'data-testid': `active-filter-${filterId}`,\n onClick: (e) => {\n // Don't select if clicking on action buttons\n if (e.target.closest('.filter-item-actions')) return\n this._handleSelect(filterId)\n }\n })\n\n // Header row with name and actions\n const headerRow = el('div', { className: 'filter-item-header' })\n\n const name = el('span', { className: 'filter-item-name' }, def.name)\n\n const actions = el('div', { className: 'filter-item-actions' })\n\n const resetBtn = createIconButton({\n icon: icons.refresh,\n title: 'Reset filter',\n className: 'btn-icon-sm',\n onClick: () => this._handleReset(filterId)\n })\n\n const removeBtn = createIconButton({\n icon: icons.trash,\n title: 'Remove filter',\n className: 'btn-icon-sm btn-danger',\n onClick: () => this._handleRemove(filterId)\n })\n\n actions.appendChild(resetBtn)\n actions.appendChild(removeBtn)\n\n headerRow.appendChild(name)\n headerRow.appendChild(actions)\n item.appendChild(headerRow)\n\n // Values summary\n const values = this.state.getFilterValues(filterId)\n const summaryEl = el('div', { className: 'filter-item-summary' })\n summaryEl.textContent = this._getValuesSummary(def, values)\n item.appendChild(summaryEl)\n item._summaryEl = summaryEl\n\n return item\n }\n\n /**\n * Normalize control type (handle aliases)\n * @param {string} type\n * @returns {string}\n */\n _normalizeControlType(type) {\n const typeMap = {\n 'slider': 'slider',\n 'range': 'slider',\n 'toggle': 'toggle',\n 'checkbox': 'toggle',\n 'color': 'color',\n 'select': 'select',\n 'dropdown': 'select',\n 'button': 'button'\n }\n return typeMap[type] || type\n }\n\n /**\n * Get a summary of filter values\n * @param {Object} def - Filter definition\n * @param {Object} values - Current values\n * @returns {string}\n */\n _getValuesSummary(def, values) {\n if (!def.controls || !Array.isArray(def.controls)) {\n return 'Default values'\n }\n\n const parts = []\n def.controls.forEach(ctl => {\n const controlType = this._normalizeControlType(ctl.type)\n if (controlType === 'button') return\n\n const label = ctl.label || ctl.id\n const value = values[ctl.id] ?? ctl.default\n if (value === ctl.default) return // Skip default values\n\n if (controlType === 'slider') {\n parts.push(`${label}: ${this._formatValue(value)}`)\n } else if (controlType === 'toggle') {\n if (value) parts.push(label)\n } else if (controlType === 'color') {\n parts.push(`${label}: ${value}`)\n } else if (controlType === 'select') {\n parts.push(`${label}: ${value}`)\n }\n })\n\n return parts.length > 0 ? parts.join(', ') : 'Default values'\n }\n\n /**\n * Format a numeric value for display\n * @param {number} value\n * @returns {string}\n */\n _formatValue(value) {\n if (typeof value !== 'number') return String(value)\n if (Number.isInteger(value)) return String(value)\n return value.toFixed(2)\n }\n\n /**\n * Update values display for all items\n */\n _updateValuesDisplay() {\n this._filterItems.forEach((item, filterId) => {\n const def = this.filterManager.getFilterDef(filterId)\n if (!def || !item._summaryEl) return\n\n const values = this.state.getFilterValues(filterId)\n item._summaryEl.textContent = this._getValuesSummary(def, values)\n })\n }\n\n /**\n * Handle filter removal\n * @param {string} filterId\n */\n _handleRemove(filterId) {\n this._onRemove?.(filterId)\n }\n\n /**\n * Handle filter reset\n * @param {string} filterId\n */\n _handleReset(filterId) {\n this._onReset?.(filterId)\n }\n\n /**\n * Handle clear all\n */\n _handleClearAll() {\n this._onClearAll?.()\n }\n\n /**\n * Handle filter selection\n * @param {string} filterId\n */\n _handleSelect(filterId) {\n this.state.set('selectedFilter', filterId)\n this._onSelect?.(filterId)\n }\n\n /**\n * Show panel\n */\n show() {\n if (this.element) {\n this.element.style.display = ''\n }\n }\n\n /**\n * Hide panel\n */\n hide() {\n if (this.element) {\n this.element.style.display = 'none'\n }\n }\n\n /**\n * Clean up\n */\n destroy() {\n this._unsubscribers.forEach(unsub => unsub())\n this._unsubscribers = []\n this._filterItems.clear()\n this.element?.remove()\n this.element = null\n }\n}\n","/**\n * Editor Preset System\n *\n * Presets configure the image editor for specific use cases (avatars, banners, etc.).\n * Each preset defines initial tool state, crop shape, aspect ratio, and control visibility.\n *\n * @example\n * import { getPreset, PRESETS } from './presets/index.js'\n *\n * // Get a built-in preset by name\n * const avatarPreset = getPreset('avatar')\n *\n * // Use directly\n * const editor = new VanillaImageEditor(container, { preset: avatarPreset })\n *\n * // Or pass by name (resolved later)\n * const editor = new VanillaImageEditor(container, { preset: 'avatar' })\n */\n\n/**\n * @typedef {Object} EditorPreset\n * @property {string} name - Preset identifier (e.g., 'avatar', 'banner', 'product')\n * @property {'crop'|'filters'} initialMode - Which tool is active when editor opens\n * @property {'free'|'square'|'circle'} cropShape - Default crop shape\n * @property {string} aspectRatio - Aspect ratio: 'free', '1:1', '4:3', '16:9', '3:2', '2:3', or custom like '3:1'\n * @property {boolean} autoZoomOnCropOverflow - Smart zoom-out when crop exceeds image bounds\n * @property {boolean} lockCropShape - Prevent user from changing crop shape\n * @property {boolean} lockAspectRatio - Prevent user from changing aspect ratio\n * @property {boolean} showFilters - Whether filter panel is available\n * @property {boolean} showCropControls - Whether crop controls are available\n * @property {number} [maxExportWidth] - Optional max output width in pixels\n * @property {number} [maxExportHeight] - Optional max output height in pixels\n */\n\n/**\n * Default values for an EditorPreset. Used as the base when merging.\n * @type {EditorPreset}\n */\nconst DEFAULT_PRESET = {\n name: 'free',\n initialMode: 'filters',\n cropShape: 'free',\n aspectRatio: 'free',\n autoZoomOnCropOverflow: false,\n lockCropShape: false,\n lockAspectRatio: false,\n showFilters: true,\n showCropControls: true,\n maxExportWidth: undefined,\n maxExportHeight: undefined\n}\n\n/**\n * Built-in presets.\n * @type {Record<string, EditorPreset>}\n */\nexport const PRESETS = {\n free: {\n ...DEFAULT_PRESET\n },\n\n avatar: {\n ...DEFAULT_PRESET,\n name: 'avatar',\n initialMode: 'crop',\n cropShape: 'circle',\n aspectRatio: '1:1',\n autoZoomOnCropOverflow: true,\n lockCropShape: true,\n lockAspectRatio: true\n },\n\n banner: {\n ...DEFAULT_PRESET,\n name: 'banner',\n initialMode: 'crop',\n cropShape: 'square',\n aspectRatio: '16:9',\n autoZoomOnCropOverflow: false,\n lockCropShape: false,\n lockAspectRatio: true\n },\n\n cover: {\n ...DEFAULT_PRESET,\n name: 'cover',\n initialMode: 'crop',\n cropShape: 'free',\n aspectRatio: '24:5',\n autoZoomOnCropOverflow: true,\n lockCropShape: true,\n lockAspectRatio: true\n },\n\n product: {\n ...DEFAULT_PRESET,\n name: 'product',\n initialMode: 'filters',\n cropShape: 'square',\n aspectRatio: '1:1',\n autoZoomOnCropOverflow: false,\n lockCropShape: false,\n lockAspectRatio: false\n }\n}\n\n/**\n * Resolve a preset by name or return a custom preset object as-is.\n * Falls back to the 'free' preset if the name is not recognized.\n *\n * @param {string|EditorPreset} preset - Preset name or custom preset object\n * @returns {EditorPreset} Resolved preset\n */\nexport function getPreset(preset) {\n if (!preset) {\n return { ...PRESETS.free }\n }\n\n // If it's already an object, merge with defaults to fill missing fields\n if (typeof preset === 'object' && preset !== null) {\n return { ...DEFAULT_PRESET, ...preset }\n }\n\n // Look up by name, fallback to 'free'\n const found = PRESETS[preset]\n if (found) {\n return { ...found }\n }\n\n return { ...PRESETS.free }\n}\n\n/**\n * Merge a preset with explicit option overrides.\n * Options take precedence over preset values.\n *\n * @param {EditorPreset} preset - Base preset\n * @param {Partial<EditorPreset>} overrides - Explicit overrides\n * @returns {EditorPreset} Merged result\n */\nexport function mergePresetWithOverrides(preset, overrides) {\n if (!overrides || Object.keys(overrides).length === 0) {\n return { ...preset }\n }\n\n const merged = { ...preset }\n\n // Only apply overrides that are explicitly defined (not undefined)\n for (const key of Object.keys(overrides)) {\n if (overrides[key] !== undefined) {\n merged[key] = overrides[key]\n }\n }\n\n return merged\n}\n\n/**\n * Parse an aspect ratio string into a numeric value.\n * Handles standard ratios ('1:1', '16:9', etc.) and custom strings ('3:1', '21:9').\n * Returns null for 'free' or invalid values.\n *\n * @param {string} ratio - Aspect ratio string\n * @returns {number|null} Numeric ratio (width / height), or null for 'free'\n */\nexport function parseAspectRatio(ratio) {\n if (!ratio || ratio === 'free') {\n return null\n }\n\n const parts = ratio.split(':')\n if (parts.length === 2) {\n const w = parseFloat(parts[0])\n const h = parseFloat(parts[1])\n if (Number.isFinite(w) && Number.isFinite(h) && h > 0) {\n return w / h\n }\n }\n\n return null\n}\n\nexport { DEFAULT_PRESET }\n","/**\n * VanillaImageEditor - Main orchestrator class\n * Framework-agnostic image editor using vanilla JavaScript\n */\nimport { EventEmitter } from './core/EventEmitter.js'\nimport { createState } from './core/State.js'\nimport { PixiRenderer } from './renderer/PixiRenderer.js'\nimport { FilterManager } from './renderer/FilterManager.js'\nimport { CropManager } from './renderer/CropManager.js'\nimport { RemoveBgManager } from './renderer/RemoveBgManager.js'\nimport { Toolbar } from './ui/Toolbar.js'\nimport { CategoryCarousel, REGISTRY_CATEGORY_MAP } from './ui/CategoryCarousel.js'\nimport { FilterCarousel } from './ui/FilterCarousel.js'\nimport { FilterAdjustments } from './ui/FilterAdjustments.js'\nimport { MobileFilterDrawer } from './ui/MobileFilterDrawer.js'\nimport { MobileActiveFilters } from './ui/MobileActiveFilters.js'\nimport { CropControls } from './ui/CropControls.js'\nimport { ActiveFiltersPanel } from './ui/ActiveFiltersPanel.js'\nimport { el } from './ui/UIBuilder.js'\nimport { getPreset, mergePresetWithOverrides } from './presets/index.js'\n\nfunction normalizeRotationAngle(angle) {\n if (!Number.isFinite(Number(angle))) return 0\n const normalized = Number(angle) % 360\n return normalized < 0 ? normalized + 360 : normalized\n}\n\nfunction shortestRotationDelta(from, to) {\n const current = normalizeRotationAngle(from)\n const target = normalizeRotationAngle(to)\n return ((target - current + 540) % 360) - 180\n}\n\nexport class VanillaImageEditor extends EventEmitter {\n /**\n * Create a new VanillaImageEditor\n * @param {HTMLElement} container - Container element to mount the editor\n * @param {Object} options - Configuration options\n */\n constructor(container, options = {}) {\n super()\n\n if (!container) {\n throw new Error('VanillaImageEditor: container element is required')\n }\n\n this._container = container\n\n // Lifecycle guards\n this._destroyed = false\n this._loadVersion = 0\n this._objectUrls = new Set() // Track created object URLs for cleanup\n this._activeObjectUrl = null\n\n // Resolve preset: if a preset name or object is provided, resolve it\n // then merge with any explicit option overrides (options win over preset)\n const resolvedPreset = options.preset\n ? getPreset(options.preset)\n : null\n\n // Build effective options: defaults ← preset ← explicit options\n const presetOverrides = resolvedPreset ? {\n initialMode: resolvedPreset.initialMode === 'crop' ? 'crop' : 'adjust',\n cropShape: resolvedPreset.cropShape,\n initialAspectRatio: resolvedPreset.aspectRatio\n } : {}\n\n this._options = {\n theme: 'auto',\n initialImage: null,\n initialMode: 'adjust',\n cropShape: 'free',\n initialAspectRatio: 'free',\n backgroundRemoval: {\n enabled: true,\n endpoint: '/api/v1/media/background-removal/preview',\n optionsEndpoint: '/api/v1/media/background-removal/options',\n fallbackEndpoint: null\n },\n ...presetOverrides,\n ...options\n }\n\n // Store the resolved preset for state flags and later use\n this._preset = resolvedPreset\n\n // Initialize state\n this._state = createState()\n\n // Set initial state from options\n this._state.set('theme', this._options.theme)\n this._state.set('crop.shape', this._options.cropShape)\n this._state.set('crop.aspect', this._options.initialAspectRatio)\n\n // Apply preset state flags\n if (this._preset) {\n this._state.set('lockCropShape', !!this._preset.lockCropShape)\n this._state.set('lockAspectRatio', !!this._preset.lockAspectRatio)\n this._state.set('showFilters', this._preset.showFilters !== false)\n this._state.set('showCropControls', this._preset.showCropControls !== false)\n this._state.set('autoZoomOnCropOverflow', !!this._preset.autoZoomOnCropOverflow)\n }\n\n // Initialize managers\n this._renderer = new PixiRenderer()\n this._filterManager = new FilterManager(this._state, this._renderer)\n this._cropManager = new CropManager(this._state, this._renderer)\n\n // Background removal manager (optional)\n this._removeBgManager = null\n this._backgroundRemovalAvailable = false\n if (this._options.backgroundRemoval?.enabled !== false) {\n this._removeBgManager = new RemoveBgManager({\n endpoint: this._options.backgroundRemoval?.endpoint,\n optionsEndpoint: this._options.backgroundRemoval?.optionsEndpoint,\n fallbackEndpoint: this._options.backgroundRemoval?.fallbackEndpoint\n })\n }\n\n // Loading overlay element\n this._loadingOverlay = null\n\n // Mobile detection\n this._isMobile = typeof window !== 'undefined' ? window.innerWidth <= 768 : false\n\n // UI components\n this._toolbar = null\n this._categoryCarousel = null\n this._filterCarousel = null\n this._filterAdjustments = null\n this._mobileFilterDrawer = null\n this._mobileActiveFilters = null\n this._cropControls = null\n this._activeFiltersPanel = null\n\n // DOM elements\n this._editorEl = null\n this._canvasContainer = null\n this._cropOverlay = null\n this._controlsSection = null\n this._filterControlsEl = null\n this._fileInput = null\n\n // Resize observer\n this._resizeObserver = null\n\n // Initialize\n this._initPromise = Promise.resolve(this._init()).catch((error) => {\n if (!this._destroyed) {\n this.emit('error', error instanceof Error ? error : new Error(String(error)))\n }\n })\n }\n\n /**\n * Initialize the editor\n */\n async _init() {\n // Detect theme\n this._state.detectTheme()\n\n // Build DOM structure\n this._buildDOM()\n\n // Mount PIXI renderer\n const mounted = await this._renderer.mount(\n this._canvasContainer,\n this._state.get('isDarkMode') ? 0x0a0a0a : 0xffffff\n )\n if (!mounted || this._destroyed) return\n\n // Set crop overlay canvas\n this._cropManager.setOverlayCanvas(this._cropOverlay)\n\n // Initialize UI components\n this._initUI()\n\n // Set up resize handling\n this._setupResizeObserver()\n this._setupWindowResize()\n\n // Subscribe to state changes\n this._subscribeToState()\n void this._refreshBackgroundRemovalAvailability()\n\n // Load initial image if provided\n if (this._options.initialImage) {\n await this.loadImage(this._options.initialImage)\n }\n\n this.emit('ready')\n }\n\n /**\n * Build DOM structure\n */\n _buildDOM() {\n // Clear container\n this._container.innerHTML = ''\n\n // Main editor element\n this._editorEl = el('div', {\n className: `vanilla-image-editor ${this._state.get('isDarkMode') ? 'dark' : 'light'}`\n })\n\n // Toolbar\n const toolbarContainer = el('div', { className: 'editor-toolbar-container' })\n this._editorEl.appendChild(toolbarContainer)\n this._toolbarContainer = toolbarContainer\n\n // Content area (canvas + controls)\n const content = el('div', { className: 'editor-content' })\n\n // Canvas section\n const canvasSection = el('div', { className: 'canvas-section' })\n this._canvasContainer = el('div', { className: 'canvas-container' })\n\n // Crop overlay canvas (decorative — hidden from screen readers)\n this._cropOverlay = el('canvas', { className: 'crop-overlay', 'aria-hidden': 'true' })\n\n canvasSection.appendChild(this._canvasContainer)\n canvasSection.appendChild(this._cropOverlay)\n content.appendChild(canvasSection)\n\n // Controls section\n this._controlsSection = el('div', { className: 'controls-section' })\n content.appendChild(this._controlsSection)\n\n this._editorEl.appendChild(content)\n\n // Hidden file input\n this._fileInput = el('input', {\n type: 'file',\n accept: 'image/*',\n className: 'hidden-file-input',\n 'aria-label': 'Choose image file to edit',\n onChange: (e) => this._handleFileSelect(e)\n })\n this._editorEl.appendChild(this._fileInput)\n\n this._container.appendChild(this._editorEl)\n }\n\n /**\n * Initialize UI components\n */\n _initUI() {\n // Toolbar\n this._toolbar = new Toolbar(this._state, this)\n this._toolbarContainer.appendChild(this._toolbar.render())\n\n // Build filter controls container\n this._filterControlsEl = el('div', { className: 'filter-controls-container' })\n\n // Category carousel\n this._categoryCarousel = new CategoryCarousel(this._state, this)\n this._filterControlsEl.appendChild(this._categoryCarousel.render((categoryId) => {\n // Category selected\n }))\n\n // Two-column layout for filters\n const filterLayout = el('div', { className: 'filter-layout' })\n\n // Left column\n const leftColumn = el('div', { className: 'filter-left-column' })\n\n // Filter carousel\n this._filterCarousel = new FilterCarousel(this._state, this._filterManager)\n leftColumn.appendChild(this._filterCarousel.render({\n onToggle: (filterId, enabled) => this._handleFilterToggle(filterId, enabled),\n onSelect: (filterId) => {\n this._handleFilterSelect(filterId)\n // On mobile, open the drawer instead of inline adjustments\n if (this._isMobile && this._mobileFilterDrawer) {\n this._mobileFilterDrawer.open(filterId)\n }\n }\n }))\n\n // Filter adjustments\n this._filterAdjustments = new FilterAdjustments(this._state, this._filterManager)\n leftColumn.appendChild(this._filterAdjustments.render({\n onChange: (filterId, controlId, value) => this._handleFilterChange(filterId, controlId, value),\n onReset: (filterId) => this._handleFilterReset(filterId),\n onAction: (filterId, action) => this._handleFilterAction(filterId, action)\n }))\n\n filterLayout.appendChild(leftColumn)\n\n // Right column - Active filters panel\n const rightColumn = el('div', { className: 'filter-right-column' })\n this._activeFiltersPanel = new ActiveFiltersPanel(this._state, this._filterManager)\n rightColumn.appendChild(this._activeFiltersPanel.render({\n onRemove: (filterId) => this._handleFilterToggle(filterId, false),\n onReset: (filterId) => this._handleFilterReset(filterId),\n onClearAll: () => this.resetAll(),\n onSelect: (filterId) => this._handleFilterSelect(filterId)\n }))\n filterLayout.appendChild(rightColumn)\n\n this._filterControlsEl.appendChild(filterLayout)\n\n // Mobile active filters carousel (shown below filter carousel on mobile)\n this._mobileActiveFilters = new MobileActiveFilters(this._state, this._filterManager)\n this._filterControlsEl.appendChild(this._mobileActiveFilters.render({\n onToggle: (filterId, enabled) => this._handleFilterToggle(filterId, enabled),\n onSelect: (filterId) => {\n this._handleFilterSelect(filterId)\n if (this._isMobile && this._mobileFilterDrawer) {\n this._mobileFilterDrawer.open(filterId)\n }\n }\n }))\n\n // Crop controls\n this._cropControls = new CropControls(this._state, this._cropManager, this)\n this._cropControlsEl = this._cropControls.render()\n this._cropControlsEl.style.display = 'none'\n\n // Add to controls section\n this._controlsSection.appendChild(this._filterControlsEl)\n this._controlsSection.appendChild(this._cropControlsEl)\n\n // Mobile filter drawer (appended to controls section — sized to match it)\n this._mobileFilterDrawer = new MobileFilterDrawer(this._state, this._filterManager)\n this._mobileFilterDrawer.build(this._controlsSection, {\n onChange: (filterId, controlId, value) => this._handleFilterChange(filterId, controlId, value),\n onReset: (filterId) => this._handleFilterReset(filterId),\n onRemove: (filterId) => {\n this._handleFilterToggle(filterId, false)\n this._state.set('selectedFilter', null)\n },\n onAction: (filterId, action) => this._handleFilterAction(filterId, action)\n })\n }\n\n /**\n * Set up resize observer with debounce to prevent infinite loops.\n * Without debounce, PIXI renderer.resize() changes the canvas buffer\n * which triggers another ResizeObserver notification, causing a loop\n * that floods the console with \"ResizeObserver loop completed\" errors\n * and prevents the image from rendering (gray canvas).\n */\n _setupResizeObserver() {\n let resizeTimer = null\n this._resizeObserver = new ResizeObserver(() => {\n clearTimeout(resizeTimer)\n resizeTimer = setTimeout(() => {\n if (this._canvasContainer) {\n this._renderer.resizeTo(this._canvasContainer)\n if (this._state.get('mode') === 'crop') {\n this._cropManager.drawOverlay()\n }\n }\n }, 50)\n })\n this._resizeObserver.observe(this._canvasContainer)\n }\n\n /**\n * Set up window resize listener for mobile detection\n */\n _setupWindowResize() {\n this._handleWindowResize = () => {\n this._isMobile = window.innerWidth <= 768\n // Close drawer if switching to desktop\n if (!this._isMobile && this._mobileFilterDrawer?.isOpen) {\n this._mobileFilterDrawer.close()\n }\n }\n window.addEventListener('resize', this._handleWindowResize)\n }\n\n /**\n * Subscribe to state changes\n */\n _subscribeToState() {\n // Mode changes\n this._state.on('change:mode', ({ value }) => {\n this._updateModeUI(value)\n // Re-apply filters when returning from crop mode — crop may replace the sprite\n if (value === 'filters') {\n this._filterManager.applyFilters()\n }\n })\n\n // Theme changes\n this._state.on('change:isDarkMode', ({ value }) => {\n this._editorEl.classList.toggle('dark', value)\n this._editorEl.classList.toggle('light', !value)\n this._renderer.setBackgroundColor(value ? 0x0a0a0a : 0xffffff)\n })\n\n // Has image changes\n this._state.on('change:hasImage', ({ value }) => {\n this._editorEl.classList.toggle('has-image', value)\n })\n\n // Zoom changes from renderer\n this._renderer.on('zoomChange', (zoom) => {\n this._state.set('zoom', zoom)\n })\n }\n\n /**\n * Update UI based on mode\n * @param {string} mode\n */\n _updateModeUI(mode) {\n if (mode === 'crop') {\n this._filterControlsEl.style.display = 'none'\n this._cropControlsEl.style.display = ''\n // Close mobile drawer when entering crop mode\n if (this._mobileFilterDrawer?.isOpen) {\n this._mobileFilterDrawer.close()\n }\n } else {\n this._filterControlsEl.style.display = ''\n this._cropControlsEl.style.display = 'none'\n }\n }\n\n /**\n * Handle file selection\n * @param {Event} event\n */\n async _handleFileSelect(event) {\n const file = event.target.files?.[0]\n if (file) {\n await this.loadImage(file)\n }\n // Reset input\n this._fileInput.value = ''\n }\n\n /**\n * Handle filter toggle\n * @param {string} filterId\n * @param {boolean} enabled\n */\n _handleFilterToggle(filterId, enabled) {\n this._filterManager.toggle(filterId, enabled)\n }\n\n /**\n * Handle filter select\n * @param {string} filterId\n */\n _handleFilterSelect(filterId) {\n this._state.set('selectedFilter', filterId)\n }\n\n /**\n * Handle filter value change\n * @param {string} filterId\n * @param {string} controlId\n * @param {*} value\n */\n _handleFilterChange(filterId, controlId, value) {\n const inPlace = this._filterManager.updateValue(filterId, controlId, value)\n if (!inPlace) {\n this._filterManager.applyFilters()\n }\n this._renderer.render()\n }\n\n /**\n * Handle filter reset\n * @param {string} filterId\n */\n _handleFilterReset(filterId) {\n this._filterManager.resetValues(filterId)\n this._filterManager.applyFilters()\n }\n\n /**\n * Handle filter action (button click).\n * Delegates to FilterManager.performFilterAction which syncs state after\n * the action so the result survives filter recreation.\n * @param {string} filterId\n * @param {string} action\n */\n _handleFilterAction(filterId, action) {\n this._filterManager.performFilterAction(filterId, action)\n }\n\n _revokeObjectUrl(url) {\n if (!url || !this._objectUrls.has(url)) return\n\n URL.revokeObjectURL(url)\n this._objectUrls.delete(url)\n\n if (this._activeObjectUrl === url) {\n this._activeObjectUrl = null\n }\n }\n\n _replaceActiveObjectUrl(url) {\n const previousUrl = this._activeObjectUrl\n this._activeObjectUrl = url\n this._objectUrls.add(url)\n\n if (previousUrl && previousUrl !== url) {\n this._revokeObjectUrl(previousUrl)\n }\n }\n\n _clearActiveObjectUrl() {\n if (this._activeObjectUrl) {\n this._revokeObjectUrl(this._activeObjectUrl)\n }\n }\n\n // ==================== Public API ====================\n\n /**\n * Set the filter registry (from mediables/filters)\n * @param {Object} registry - { getFilter, getAllFilters, getFiltersByCategory }\n */\n setFilterRegistry(registry) {\n this._filterManager.setRegistry(registry)\n }\n\n /**\n * Load an image into the editor\n * @param {string|Blob|File} imageSource - URL, data URL, Blob, or File object\n * @param {Object} [options] - Load options\n * @param {Object|null} [options.state] - ImageEditorSessionState to restore\n * @param {string|number} [options.sessionKey] - Session boundary identifier\n */\n async loadImage(imageSource, options = {}) {\n if (this._destroyed) return\n this._removeBgManager?.cancelActiveRequests?.('image-load')\n\n const loadVersion = ++this._loadVersion\n let url = imageSource\n let ownedObjectUrl = null\n\n // Reset prior session state BEFORE loading — prevents stale bleed\n this._resetEditorState()\n this._state.set('hasImage', false)\n this._state.set('imageUrl', null)\n\n // For Blob/File, create an object URL instead of base64 (lower memory)\n if (imageSource instanceof Blob) {\n url = URL.createObjectURL(imageSource)\n ownedObjectUrl = url\n this._objectUrls.add(url)\n }\n\n const loaded = await this._renderer.loadTexture(url, {\n isCurrent: () => !this._destroyed && this._loadVersion === loadVersion,\n })\n\n // Guard: bail if destroyed or a newer load started during the await\n if (this._destroyed || this._loadVersion !== loadVersion) {\n this._revokeObjectUrl(ownedObjectUrl)\n return\n }\n\n // Guard: bail if texture load failed (bad URL, CORS, 403, etc.)\n if (!loaded) {\n this._revokeObjectUrl(ownedObjectUrl)\n this.emit('error', new Error('Failed to load image'))\n return\n }\n\n if (ownedObjectUrl) {\n this._replaceActiveObjectUrl(ownedObjectUrl)\n } else {\n this._clearActiveObjectUrl()\n }\n\n this._state.set('hasImage', true)\n this._state.set('imageUrl', url)\n\n // Restore editor state if provided\n if (options.state) {\n this._hydrateState(options.state)\n }\n\n // Apply initial mode from preset/options after image loads\n // Crop mode requires an image to be loaded first\n if (this._options.initialMode === 'crop') {\n this.setMode('crop')\n }\n\n this.emit('imageLoaded', { url })\n }\n\n /**\n * Reset all editor state (filters + crop) to a clean baseline.\n * Called at the start of loadImage() to prevent stale state bleed between sessions.\n */\n _resetEditorState() {\n // Reset filters — clears active set, values, sprite.filters\n this._filterManager.resetAll()\n\n // Reset crop state (both interactive and applied/baked)\n this._state.set('crop.rect', null)\n this._state.set('crop.appliedRect', null)\n this._state.set('crop.appliedShape', null)\n this._state.set('crop.appliedAspect', null)\n this._state.set('crop.dirty', false)\n this._state.set('crop.shape', this._options.cropShape || 'free')\n this._state.set('crop.aspect', this._options.initialAspectRatio || 'free')\n this._state.set('transform.rotation', 0)\n\n // Ensure we're in filters mode (not stuck in crop)\n if (this._state.get('mode') === 'crop') {\n this._cropManager.disable()\n this._state.set('mode', 'filters')\n }\n }\n\n /**\n * Hydrate editor state from a canonical ImageEditorSessionState object.\n * Restores filter stack + values and crop metadata (shape, aspect, rect).\n * @param {Object} state - ImageEditorSessionState\n */\n _hydrateState(state) {\n if (!state || state.version !== 1) return\n\n const rotation = normalizeRotationAngle(state.transform?.rotation ?? 0)\n if (rotation !== 0 && this._renderer.rotateBy(rotation)) {\n this._state.set('transform.rotation', rotation)\n }\n\n // Restore crop: if a pixel-rect was saved, re-apply the crop to the texture.\n // This is the critical path for reopen fidelity — without it, the user sees\n // the uncropped root source image.\n if (state.crop) {\n const shape = state.crop.shape || 'free'\n const aspect = state.crop.aspectRatio || 'free'\n\n if (state.crop.rect) {\n // Apply the crop to the loaded texture using pixel coordinates\n this._cropManager.applyFromPixelRect(state.crop.rect, shape)\n\n // Store the applied crop metadata for getSerializableState()\n this._state.set('crop.appliedRect', { ...state.crop.rect })\n this._state.set('crop.appliedShape', shape)\n this._state.set('crop.appliedAspect', aspect)\n }\n\n this._state.set('crop.shape', shape)\n this._state.set('crop.aspect', aspect)\n }\n\n // Restore filters\n if (Array.isArray(state.filters)) {\n let restoredFilterId = null\n\n for (const filter of state.filters) {\n if (!filter.enabled) continue\n if (restoredFilterId === null) {\n restoredFilterId = filter.id\n }\n this._state.toggleFilter(filter.id, true)\n this._filterManager.initializeValues(filter.id)\n if (filter.values) {\n for (const [controlId, value] of Object.entries(filter.values)) {\n this._state.setFilterValue(filter.id, controlId, value)\n }\n }\n }\n\n if (restoredFilterId !== null) {\n this._state.set('selectedFilter', restoredFilterId)\n\n const filterDef = this._filterManager.getFilterDef(restoredFilterId)\n if (typeof filterDef?.category === 'string') {\n const category = REGISTRY_CATEGORY_MAP[filterDef.category] ?? filterDef.category\n this._state.set('selectedCategory', category)\n }\n }\n\n this._filterManager.applyFilters()\n }\n }\n\n /**\n * Open the file picker dialog\n */\n openFilePicker() {\n this._fileInput?.click()\n }\n\n /**\n * Export the current image.\n *\n * @param {string} format - 'png' or 'jpeg'\n * @param {number} quality - Quality for jpeg (0-1)\n * @param {Object} [options]\n * @param {number} [options.maxEdge] - Cap the longest edge to this pixel count\n * @param {number} [options.maxPixels] - Cap total pixel count (width*height)\n * @returns {string|null} Data URL\n */\n exportImage(format = 'png', quality = 0.92, options = {}) {\n if (!this._state.get('hasImage')) {\n return null\n }\n\n if (!this._applyPendingCropBeforeExport('VanillaImageEditor.exportImage')) {\n return null\n }\n\n return this._renderer.exportImage(\n format,\n quality,\n options.maxEdge ?? 0,\n options.dontUpscale !== false,\n options.maxPixels ?? 0\n )\n }\n\n /**\n * Export the current image as a Blob after finalizing pending crop edits.\n *\n * @param {string} format - 'png' or 'jpeg'\n * @param {number} quality - Quality for jpeg (0-1)\n * @param {Object} [options]\n * @returns {Promise<{blob: Blob, width: number, height: number}|null>}\n */\n async exportBlob(format = 'png', quality = 0.92, options = {}) {\n if (!this._state.get('hasImage')) {\n return null\n }\n\n if (!this._applyPendingCropBeforeExport('VanillaImageEditor.exportBlob')) {\n return null\n }\n\n return this._renderer.exportBlob(format, quality, options)\n }\n\n _applyPendingCropBeforeExport(context) {\n const pendingCropRect = this._state.get('crop.rect')\n if (pendingCropRect) {\n const appliedCrop = this._cropManager.apply()\n if (!appliedCrop) {\n console.error(`[${context}] Failed to apply pending crop before export`)\n return false\n }\n }\n\n return true\n }\n\n /**\n * Save the image (triggers download and emits 'save' event)\n */\n async save() {\n if (!this._state.get('hasImage')) return\n\n this._state.set('isSaving', true)\n let objectUrl = null\n\n try {\n const exported = await this.exportBlob('png', 0.92)\n if (!exported) throw new Error('Failed to export image')\n\n // Trigger download\n const link = document.createElement('a')\n objectUrl = URL.createObjectURL(exported.blob)\n link.href = objectUrl\n link.download = `edited-image-${Date.now()}.png`\n document.body.appendChild(link)\n link.click()\n document.body.removeChild(link)\n\n this.emit('save', {\n blob: exported.blob,\n mimeType: exported.blob.type || 'image/png',\n dimensions: { width: exported.width, height: exported.height },\n state: this.getSerializableState()\n })\n } catch (error) {\n this.emit('error', { error })\n } finally {\n if (objectUrl) URL.revokeObjectURL(objectUrl)\n this._state.set('isSaving', false)\n }\n }\n\n /**\n * Get a canonical, serializable snapshot of the current editor state.\n * Safe for persistence — returns a deep copy with no mutable references.\n * @returns {Object} ImageEditorSessionState\n */\n getSerializableState() {\n const activeFilters = this._state.get('activeFilters')\n const filters = []\n\n if (activeFilters) {\n for (const filterId of activeFilters) {\n const values = this._state.getFilterValues(filterId)\n filters.push({\n id: filterId,\n enabled: true,\n values: { ...values }\n })\n }\n }\n\n // Prefer appliedRect (baked into texture via CropManager.apply()) over\n // crop.rect (the interactive selection, cleared by disable()).\n const appliedRect = this._state.get('crop.appliedRect')\n const interactiveRect = this._state.get('crop.rect')\n const cropRect = appliedRect || interactiveRect\n\n return {\n version: 1,\n crop: {\n rect: cropRect ? { ...cropRect } : null,\n aspectRatio: this._state.get('crop.appliedAspect') || this._state.get('crop.aspect') || 'free',\n shape: this._state.get('crop.appliedShape') || this._state.get('crop.shape') || 'free'\n },\n transform: {\n rotation: normalizeRotationAngle(this._state.get('transform.rotation') ?? 0)\n },\n filters\n }\n }\n\n /**\n * Close the editor (emits 'cancel' event)\n */\n close() {\n this.emit('cancel')\n }\n\n /**\n * Set zoom level\n * @param {number} zoom\n */\n setZoom(zoom) {\n this._renderer.setZoom(zoom)\n }\n\n /**\n * Fit image to screen\n */\n fitToScreen() {\n this._renderer.fitToScreen()\n }\n\n /**\n * Reset all filters\n */\n resetAll() {\n this._filterManager.resetAll()\n }\n\n rotateBy(degrees) {\n const current = this._state.get('transform.rotation') ?? 0\n return this.setRotationAngle(Number(current) + Number(degrees))\n }\n\n setRotationAngle(angle) {\n if (!this._state.get('hasImage')) return false\n\n const current = normalizeRotationAngle(this._state.get('transform.rotation') ?? 0)\n const target = normalizeRotationAngle(angle)\n const delta = shortestRotationDelta(current, target)\n const wasCropping = this._state.get('mode') === 'crop'\n\n if (Math.abs(delta) > 0.001) {\n if (wasCropping) {\n this._cropManager.disable()\n }\n\n const rotated = this._renderer.rotateBy(delta)\n if (!rotated) {\n if (wasCropping) {\n this._cropManager.enable()\n }\n return false\n }\n\n this._filterManager.applyFilters()\n this._state.set('crop.rect', null)\n this._state.set('crop.dirty', false)\n\n if (wasCropping) {\n this._cropManager.enable()\n }\n }\n\n this._state.set('transform.rotation', target)\n this.emit('transformChanged', { rotation: target })\n return true\n }\n\n resetRotation() {\n return this.setRotationAngle(0)\n }\n\n /**\n * Toggle theme\n */\n toggleTheme() {\n const isDark = this._state.get('isDarkMode')\n this._state.set('isDarkMode', !isDark)\n }\n\n /**\n * Set theme\n * @param {'light'|'dark'|'auto'} theme\n */\n setTheme(theme) {\n this._state.set('theme', theme)\n this._state.detectTheme()\n }\n\n /**\n * Set editor mode\n * @param {'filters'|'crop'} mode\n */\n setMode(mode) {\n if (mode === 'crop') {\n this._cropManager.enable()\n } else {\n this._cropManager.disable()\n }\n this._state.set('mode', mode)\n }\n\n /**\n * Get current state (for debugging)\n * @returns {Object}\n */\n getState() {\n return this._state.getAll()\n }\n\n // ==================== Background Removal ====================\n\n /**\n * Remove the background from the current image\n * @param {Object} options\n * @param {string} [options.tier='balanced'] - Quality tier: 'fast', 'balanced', 'best'\n * @param {string} [options.model] - Explicit model name (overrides tier)\n * @param {boolean} [options.alpha_matting=false] - Enable alpha matting for edge refinement\n * @returns {Promise<{model: string, processMs: string}>}\n */\n async removeBackground(options = {}) {\n if (!this._removeBgManager) {\n throw new Error('Background removal is not enabled')\n }\n\n if (!this._state.get('hasImage')) {\n throw new Error('No image loaded')\n }\n if (!this.canRemoveBackground()) {\n throw new Error('Background removal is not available for the current editor state')\n }\n\n this._state.set('isProcessing', true)\n this._showLoadingOverlay('Removing background...')\n\n try {\n // Export current image state\n const imageData = this.exportImage('png')\n if (!imageData) {\n throw new Error('Failed to export image for background removal')\n }\n\n // Send to background removal service\n const previewRequest = await this._removeBgManager.preparePreviewRequest(imageData, options)\n const previewContext = this._buildPreviewBackgroundRemovalContext(previewRequest)\n const result = await this._removeBgManager.removeBackground(previewRequest.blob, previewRequest)\n if (!await this._isCurrentPreviewBackgroundRemovalContext(previewContext, result)) {\n this.emit('background-removal-stale', {\n operationId: previewContext.operationId,\n sourceHash: previewContext.sourceHash\n })\n\n return { stale: true }\n }\n\n // Load the result back into the editor\n await this.loadImage(result.dataUrl)\n\n this.emit('background-removed', {\n model: result.model,\n processMs: result.processMs\n })\n\n return {\n model: result.model,\n processMs: result.processMs\n }\n } catch (error) {\n this.emit('error', { error, context: 'background-removal' })\n throw error\n } finally {\n this._state?.set?.('isProcessing', false)\n this._hideLoadingOverlay()\n }\n }\n\n /**\n * Check if background removal is available\n * @returns {Promise<boolean>}\n */\n async isBackgroundRemovalAvailable() {\n if (!this._removeBgManager) return false\n return this._refreshBackgroundRemovalAvailability()\n }\n\n canRemoveBackground() {\n return !!this._removeBgManager\n && this._state.get('hasImage') === true\n && this._backgroundRemovalAvailable === true\n }\n\n async _refreshBackgroundRemovalAvailability() {\n if (!this._removeBgManager) {\n this._backgroundRemovalAvailable = false\n this._state.set('backgroundRemovalAvailable', false)\n return false\n }\n\n const available = await this._removeBgManager.isAvailable()\n if (this._destroyed) return false\n\n this._backgroundRemovalAvailable = available\n this._state.set('backgroundRemovalAvailable', available)\n\n return available\n }\n\n _buildPreviewBackgroundRemovalContext(request) {\n return {\n loadVersion: this._loadVersion,\n imageUrl: this._state.get('imageUrl') ?? null,\n operationId: request.operationId,\n sessionKey: request.sessionKey,\n sourceHash: request.sourceHash,\n targetRef: request.targetRef ?? null\n }\n }\n\n async _isCurrentPreviewBackgroundRemovalContext(context, result = null) {\n if (this._destroyed || this._loadVersion !== context.loadVersion || this._state.get('hasImage') !== true) return false\n if ((this._state.get('imageUrl') ?? null) !== context.imageUrl) return false\n if (result) {\n if (result.operationId && result.operationId !== context.operationId) return false\n if (result.sessionKey && result.sessionKey !== context.sessionKey) return false\n if (result.sourceHash && result.sourceHash !== context.sourceHash) return false\n if (result.targetRef && context.targetRef && result.targetRef !== context.targetRef) return false\n }\n\n const currentImageData = this.exportImage('png')\n if (!currentImageData) return false\n\n return await this._removeBgManager.computeImageDataHash(currentImageData) === context.sourceHash\n }\n\n // ==================== Loading Overlay ====================\n\n /**\n * Show the loading overlay\n * @param {string} message - Message to display\n */\n _showLoadingOverlay(message) {\n if (!this._loadingOverlay) {\n this._loadingOverlay = el('div', { className: 'editor-loading-overlay' }, [\n el('div', { className: 'editor-loading-spinner' }),\n el('div', { className: 'editor-loading-text' }, message)\n ])\n } else {\n const textEl = this._loadingOverlay.querySelector('.editor-loading-text')\n if (textEl) textEl.textContent = message\n }\n\n // Append to canvas section (above canvas)\n const canvasSection = this._canvasContainer?.parentElement\n if (canvasSection && !this._loadingOverlay.parentElement) {\n canvasSection.appendChild(this._loadingOverlay)\n }\n }\n\n /**\n * Hide the loading overlay\n */\n _hideLoadingOverlay() {\n this._loadingOverlay?.remove()\n }\n\n /**\n * Destroy the editor and clean up\n */\n destroy() {\n // Mark destroyed FIRST — guards async operations that may resolve after this\n this._destroyed = true\n this._removeBgManager?.cancelActiveRequests?.('editor-destroy')\n\n // Revoke all object URLs created during this session\n for (const url of this._objectUrls) {\n try { URL.revokeObjectURL(url) } catch (_e) { /* ignore */ }\n }\n this._objectUrls.clear()\n this._activeObjectUrl = null\n\n // Clean up resize observer\n this._resizeObserver?.disconnect()\n\n // Clean up managers\n this._renderer.destroy()\n this._cropManager.disable()\n\n // Clean up window resize listener\n if (this._handleWindowResize) {\n window.removeEventListener('resize', this._handleWindowResize)\n }\n\n // Clean up UI components\n this._toolbar?.destroy()\n this._categoryCarousel?.destroy()\n this._filterCarousel?.destroy()\n this._filterAdjustments?.destroy()\n this._mobileFilterDrawer?.destroy()\n this._mobileActiveFilters?.destroy()\n this._cropControls?.destroy()\n this._activeFiltersPanel?.destroy()\n\n // Remove DOM\n this._editorEl?.remove()\n\n // Clear references\n this._container = null\n this._state = null\n\n this.emit('destroyed')\n this.removeAllListeners()\n }\n}\n"],"names":["TEXT_BLEND_MODES","DEFAULT_TEXT_EFFECTS","PixiRenderer","EventEmitter","_a","canvas","_b","w","_d","_c","h","_f","_e","timeoutMs","resolve","settled","timeoutId","finish","tex","scale","opts","s","desired","z","container","backgroundColor","options","mountToken","doMount","PIXI","width","height","backgroundAlpha","app","imageUrl","isCurrent","img","settledScale","document","renderToken","isCurrentRender","subjectAdded","layer","displayObject","factory","layerEntry","opacity","context","filters","imageFilters","filter","combinedFilters","textDisplayObject","imageDisplayObject","fill","texture","box","value","min","max","fallback","numeric","allowed","candidate","mode","content","wordWrapWidth","style","effects","fontSize","lineHeight","letterSpacing","fontWeight","fontStyle","align","textStyle","source","stroke","shadow","glow","backdrop","amount","textObject","mask","effectMask","sourceUrl","key","url","image","fit","tiled","stops","gradient","radius","radians","centerX","centerY","length","dx","dy","stop","offset","effect","blur","graphics","color","alpha","primitive","kind","padding","x","y","backdropWidth","backdropHeight","rect","sourceWidth","sourceHeight","_g","_h","ownsTexture","degrees","normalized","rotation","rawCos","rawSin","cos","sin","srcW","srcH","outW","outH","src","rt","prevTexture","previousFilters","newSprite","format","quality","maxEdge","dontUpscale","maxPixels","previewW","previewH","previewSourceScale","previewToNativeScale","layerDocumentForExport","hasLayerDocumentForExport","exportFilters","f","cloned","totalPadding","p","initialMargin","renderWithMargin","margin","renderW","renderH","exportRoot","exportSprites","addSubjectSprite","exportSprite","addImageLayer","liveDisplayObject","addBackgroundLayer","background","addTextLayer","layerDocument","hasLayerDocument","childCount","scanAlphaBounds","ctx","pixels","threshold","minX","minY","maxX","maxY","stride","rowStride","rowStart","touchesEdge","result","rendered","bounds","retryMargin","retried","retriedBounds","edge","outCanvas","outCtx","dataUrl","blob","dims","prevCenter","chevronLeft","chevronRight","zoomIn","zoomOut","expand","settings","water","colorPalette","sparkles","move","brush","flash","crop","save","close","refresh","rotateCcw","rotateCw","trash","openFolder","checkmark","layers","eye","eyeOff","lockClosed","lockOpen","pencil","duplicate","text","square","circle","freeform","sunny","moon","film","personOutline","EDITOR_CATEGORIES","icons.settings","icons.water","icons.colorPalette","icons.sparkles","icons.move","icons.flash","icons.film","icons.text","icons.brush","icons.layers","icons.crop","UI_TO_REGISTRY","c","REGISTRY_TO_UI","rc","getRegistryCategoriesForUi","uiCategoryId","FilterManager","state","pixiRenderer","registry","filterId","category","registryCategories","seenIds","registryCat","catFilters","target","path","tokens","obj","i","last","expectedLayerId","def","values","controlIds","ctl","enabled","controlId","isDynamicPath","inst","synced","val","existing","action","params","expected","_i","filterState","dimensions","activeLayerId","trackInstances","renderFilter","applied","sprite","failedFilters","id","error","_CropManager","aspect","aspectMap","parts","shape","targetAspect","currentAspect","side","cropRect","spriteWidth","spriteHeight","texWidth","texHeight","fitScale","currentZoom","overflowW","overflowH","pad","targetZoom","zoomForW","zoomForH","now","overlay","cv","cssW","cssH","dpr","thirdW","thirdH","hs","corners","edges","all","hover","size","px","py","near","ax","ay","bx","by","event","pos","maxW","maxH","stage","prevZoom","scaleX","scaleY","rawTx","rawTy","offsetX","offsetY","cx","cy","g","previousAppliedRect","appliedPixelRect","appliedShape","appliedAspect","pixelRect","__publicField","CropManager","RemoveBgManager","controller","reason","controllers","imageData","request","operationId","sessionKey","sourceHash","formData","response","withSignal","init","e","errorMessage","errorText","resultBlob","endpoint","payload","data","header","mimeMatch","mime","binary","array","reject","reader","char","random","buffer","digest","byte","el","tag","attrs","children","element","child","createSlider","label","step","onChange","wrapper","formatValue","input","display","createToggle","checked","labelEl","toggle","slider","createColorPicker","createTextInput","placeholder","onCommit","lastCommitted","commit","next","createSelect","select","opt","option","createButton","className","onClick","icon","disabled","button","iconEl","svg","createIconButton","title","testId","ariaLabel","createChip","active","chip","isActive","Toolbar","editor","leftSection","openBtn","icons.openFolder","rightSection","icons.sunny","icons.moon","removeBgGroup","optionEl","icons.personOutline","resetBtn","icons.refresh","saveBtn","saveIconEl","icons.save","saveSvg","closeBtn","icons.close","unsubTheme","unsubMode","unsubHasImage","unsubProcessing","unsubAvailability","unsubDocument","unsubLayers","unsubActiveLayer","busy","message","unsub","CATEGORIES","CategoryCarousel","onSelect","icons.chevronLeft","cat","icons.chevronRight","pageCount","dot","categoryId","currentMode","selectedChip","pageIndex","itemWidth","dots","activePage","FilterCarousel","filterManager","onToggle","unsubCat","unsubActive","unsubSelected","activeFilters","selectedFilter","card","isSelected","preview","previewText","name","willBeActive","icons.checkmark","maxIndex","FilterAdjustments","onReset","onAction","unsubValues","grid","controlEl","instance","dynamicControls","stateKey","controlType","updateKey","type","MobileFilterDrawer","onRemove","headerActions","removeBtn","MobileActiveFilters","check","SHAPES","icons.freeform","icons.square","icons.circle","ASPECT_RATIOS","ROTATION_PRESETS","normalizeAngle","angle","CropControls","cropManager","shapeSection","shapeRow","currentShape","aspectRow","actionRow","cancelBtn","applyBtn","section","quickRow","rotateLeft","icons.rotateCcw","rotateRight","icons.rotateCw","rotateHalf","rotateThreeQuarter","presetRow","current","preset","arbitrary","inputRow","reset","unsubShape","unsubAspect","unsubRotation","delta","rawValue","rounded","shapeId","aspectId","lockAspect","ActiveFiltersPanel","onClearAll","onUpdateValue","clearAllBtn","item","headerRow","actions","icons.trash","summaryEl","DEFAULT_PRESET","PRESETS","getPreset","found","normalizeRotationAngle","shortestRotationDelta","from","to","VanillaImageEditor","resolvedPreset","presetOverrides","createState","toolbarContainer","canvasSection","filterLayout","leftColumn","rightColumn","resizeTimer","zoom","file","previousUrl","imageSource","loadVersion","ownedObjectUrl","loaded","restoredFilterId","filterDef","REGISTRY_CATEGORY_MAP","objectUrl","exported","link","appliedRect","interactiveRect","wasCropping","isDark","theme","previewRequest","previewContext","available","currentImageData","textEl","_j","_k","_l"],"mappings":"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"}