UNPKG

kinetic-slider

Version:

A WebGL-powered kinetic slider component using PIXI.js

1 lines 68.7 kB
{"version":3,"file":"ResourceManager.cjs","sources":["../../../src/managers/ResourceManager.ts"],"sourcesContent":["import { Texture, Filter, Container, Application } from 'pixi.js';\nimport { gsap } from 'gsap';\nimport { ShaderResourceManager } from './ShaderResourceManager';\n\n/**\n * Types for resource tracking and management\n */\ntype EventCallback = EventListenerOrEventListenerObject;\ntype Timer = ReturnType<typeof setTimeout>;\ntype Animation = gsap.core.Tween | gsap.core.Timeline;\n\n/**\n * Resource entry with reference counting for proper cleanup\n */\ninterface ResourceEntry<T> {\n resource: T;\n refCount: number;\n lastUsed?: number; // Timestamp for potential LRU optimization\n}\n\n/**\n * Resource batch tracking statistics\n */\ninterface BatchStats {\n totalBatches: number;\n totalItems: number;\n averageBatchSize: number;\n largestBatch: number;\n}\n\n/**\n * Configuration options for the ResourceManager\n */\ninterface ResourceManagerOptions {\n logLevel?: 'error' | 'warn' | 'info' | 'debug';\n enableMetrics?: boolean;\n autoCleanupInterval?: number | null; // ms, null to disable\n enableShaderPooling?: boolean;\n}\n\n/**\n * Performance metrics for resource operations\n */\ninterface PerformanceMetrics {\n operations: {\n [key: string]: {\n count: number;\n totalTime: number;\n averageTime: number;\n }\n };\n batchStats: {\n textures: BatchStats;\n filters: BatchStats;\n displayObjects: BatchStats;\n animations: BatchStats;\n };\n}\n\n/**\n * Centralized Resource Manager for WebGL and Browser Resources\n *\n * Provides efficient batch tracking and lifecycle management for:\n * - PIXI.js textures, filters, and display objects\n * - GSAP animations\n * - DOM event listeners\n * - Timers and intervals\n */\nclass ResourceManager {\n // Resource collections\n private textures = new Map<string, ResourceEntry<Texture>>();\n private filters = new Set<Filter>();\n private displayObjects = new Set<Container>();\n private pixiApps = new Set<Application>();\n private animations = new Set<Animation>();\n\n // Event listener tracking with improved nesting\n private listeners = new Map<EventTarget, Map<string, Set<EventCallback>>>();\n\n // Timer tracking\n private timeouts = new Set<Timer>();\n private intervals = new Set<Timer>();\n\n // Manager state\n private disposed = false;\n private unmounting = false;\n private readonly componentId: string;\n private readonly options: ResourceManagerOptions;\n\n // Performance metrics (optional)\n private metrics: PerformanceMetrics | null = null;\n private autoCleanupTimer: Timer | null = null;\n\n // Shader resource manager\n private shaderManager: ShaderResourceManager | null = null;\n\n /**\n * Creates a new ResourceManager instance\n *\n * @param componentId - Unique identifier for this component instance\n * @param options - Configuration options\n */\n constructor(componentId: string, options: ResourceManagerOptions = {}) {\n this.componentId = componentId;\n this.options = {\n logLevel: options.logLevel || 'warn',\n enableMetrics: options.enableMetrics || false,\n autoCleanupInterval: options.autoCleanupInterval || null,\n enableShaderPooling: options.enableShaderPooling !== false // Enable by default\n };\n\n // Initialize metrics if enabled\n if (this.options.enableMetrics) {\n this.metrics = {\n operations: {},\n batchStats: {\n textures: this.createEmptyBatchStats(),\n filters: this.createEmptyBatchStats(),\n displayObjects: this.createEmptyBatchStats(),\n animations: this.createEmptyBatchStats()\n }\n };\n }\n\n // Initialize shader manager if shader pooling is enabled\n if (this.options.enableShaderPooling) {\n this.shaderManager = ShaderResourceManager.getInstance({\n debug: this.options.logLevel === 'debug',\n enableMetrics: this.options.enableMetrics\n });\n this.log('info', 'Shader pooling enabled');\n }\n\n this.log('info', `ResourceManager initialized`);\n\n // Set up auto cleanup if enabled\n this.setupAutoCleanup();\n }\n\n /**\n * Creates an empty batch statistics object\n */\n private createEmptyBatchStats(): BatchStats {\n return {\n totalBatches: 0,\n totalItems: 0,\n averageBatchSize: 0,\n largestBatch: 0\n };\n }\n\n /**\n * Set up automatic cleanup interval\n */\n private setupAutoCleanup(): void {\n if (this.options.autoCleanupInterval) {\n this.autoCleanupTimer = setInterval(() => {\n this.performAutoCleanup();\n }, this.options.autoCleanupInterval);\n }\n }\n\n /**\n * Perform automatic cleanup of unused resources\n */\n private performAutoCleanup(): void {\n if (this.disposed || this.unmounting) return;\n\n this.log('debug', 'Performing automatic resource cleanup...');\n\n // Clean up 0-reference count textures\n let texturesReleased = 0;\n this.textures.forEach((entry, url) => {\n if (entry.refCount <= 0) {\n this.releaseTexture(url);\n texturesReleased++;\n }\n });\n\n // Clean up unused animations\n let animationsReleased = 0;\n this.animations.forEach(animation => {\n if (animation.isActive() === false) {\n animation.kill();\n this.animations.delete(animation);\n animationsReleased++;\n }\n });\n\n this.log('debug', `Auto cleanup complete: ${texturesReleased} textures and ${animationsReleased} animations released`);\n }\n\n /**\n * Log a message with the appropriate level\n *\n * @param level - Log level\n * @param message - Log message\n * @param data - Optional data to log\n */\n private log(level: 'error' | 'warn' | 'info' | 'debug', message: string, data?: any): void {\n const logLevels = {\n 'error': 0,\n 'warn': 1,\n 'info': 2,\n 'debug': 3\n };\n\n if (logLevels[level] <= logLevels[this.options.logLevel || 'warn']) {\n const logMessage = `[ResourceManager:${this.componentId}] ${message}`;\n\n switch (level) {\n case 'error':\n console.error(logMessage, data);\n break;\n case 'warn':\n console.warn(logMessage, data);\n break;\n case 'info':\n console.info(logMessage, data);\n break;\n case 'debug':\n console.debug(logMessage, data);\n break;\n }\n }\n }\n\n /**\n * Records performance metric for an operation\n *\n * @param name - Operation name\n * @param startTime - Start time of the operation\n */\n private recordMetric(name: string, startTime: number): void {\n if (!this.metrics) return;\n\n const endTime = performance.now();\n const duration = endTime - startTime;\n\n if (!this.metrics.operations[name]) {\n this.metrics.operations[name] = {\n count: 0,\n totalTime: 0,\n averageTime: 0\n };\n }\n\n const op = this.metrics.operations[name];\n op.count++;\n op.totalTime += duration;\n op.averageTime = op.totalTime / op.count;\n }\n\n /**\n * Update batch statistics\n *\n * @param type - Resource type\n * @param batchSize - Size of the batch\n */\n private updateBatchStats(type: 'textures' | 'filters' | 'displayObjects' | 'animations', batchSize: number): void {\n if (!this.metrics) return;\n\n const stats = this.metrics.batchStats[type];\n stats.totalBatches++;\n stats.totalItems += batchSize;\n stats.averageBatchSize = stats.totalItems / stats.totalBatches;\n stats.largestBatch = Math.max(stats.largestBatch, batchSize);\n }\n\n /**\n * Mark component as unmounting to prevent new resource allocations\n */\n markUnmounting(): void {\n this.unmounting = true;\n this.log('info', 'Component marked as unmounting');\n }\n\n /**\n * Check if the resource manager is active and can allocate resources\n */\n isActive(): boolean {\n return !this.unmounting && !this.disposed;\n }\n\n // ===== FILTER CONTROL METHODS =====\n\n /**\n * Safely disable a filter to ensure it has no visible effect\n *\n * @param filter - Filter to disable\n */\n disableFilter(filter: Filter): void {\n try {\n // Try multiple approaches to disable the filter\n if ('enabled' in filter && typeof filter.enabled === 'boolean') {\n filter.enabled = false;\n }\n\n // Some filters use alpha property\n if ('alpha' in filter && typeof filter.alpha === 'number') {\n filter.alpha = 0;\n }\n\n // Some filters use strength\n if ('strength' in filter && typeof filter.strength === 'number') {\n filter.strength = 0;\n }\n\n // Some filters have a scale property (e.g. DisplacementFilter)\n if ('scale' in filter) {\n const scale = filter.scale as any;\n if (scale && typeof scale.x === 'number' && typeof scale.y === 'number') {\n scale.x = 0;\n scale.y = 0;\n } else if (typeof scale === 'number') {\n (filter as any).scale = 0;\n }\n }\n\n // Some filters use the blur property\n if ('blur' in filter && typeof filter.blur === 'number') {\n filter.blur = 0;\n }\n } catch (error) {\n this.log('debug', 'Error disabling filter', error);\n }\n }\n\n /**\n * Disable all filters on a display object\n *\n * @param displayObject - The display object whose filters should be disabled\n */\n disableFiltersOnObject(displayObject: Container): void {\n if (!displayObject.filters) return;\n\n try {\n if (Array.isArray(displayObject.filters)) {\n displayObject.filters.forEach(filter => {\n if (filter) this.disableFilter(filter);\n });\n } else if (displayObject.filters) {\n // Handle single filter case\n this.disableFilter(displayObject.filters as Filter);\n }\n\n this.log('debug', 'Disabled filters on display object');\n } catch (error) {\n this.log('warn', 'Error disabling filters on object', error);\n }\n }\n\n /**\n * Initialize a filter in a disabled state\n * This ensures the filter has no effect when first created\n *\n * @param filter - Filter to initialize\n * @returns The initialized filter\n */\n initializeFilterDisabled<T extends Filter>(filter: T): T {\n this.disableFilter(filter);\n return filter;\n }\n\n /**\n * Batch initialize and disable filters\n *\n * @param filters - Array of filters to initialize disabled\n * @returns The array of initialized filters\n */\n initializeFilterBatchDisabled<T extends Filter[]>(filters: T): T {\n filters.forEach(filter => this.disableFilter(filter));\n return filters;\n }\n\n /**\n * Track a filter\n *\n * @param filter - Filter to track\n * @returns The filter for chaining\n */\n trackFilter(filter: Filter): Filter {\n if (!this.isActive()) return filter;\n\n const startTime = this.metrics ? performance.now() : 0;\n this.filters.add(filter);\n\n // Register with shader manager if available\n if (this.shaderManager) {\n // The shader manager will keep track of shader programs used by this filter\n // This is a simplified integration - in a real implementation we'd hook into\n // the filter's shader creation process\n this.log('debug', `Filter registered with shader manager`);\n }\n\n if (this.metrics) {\n this.recordMetric('trackFilter', startTime);\n this.updateBatchStats('filters', 1);\n }\n\n return filter;\n }\n\n /**\n * Dispose of a single filter\n */\n private disposeFilter(filter: Filter): void {\n const startTime = this.metrics ? performance.now() : 0;\n\n try {\n // First disable the filter before destroying it\n this.disableFilter(filter);\n\n // Release any shaders associated with this filter\n if (this.shaderManager) {\n this.shaderManager.releaseShader(filter as any);\n }\n\n // Then destroy it\n filter.destroy();\n } catch (error) {\n // Fallback destruction for resilience\n this.log('debug', `Using fallback disposal for filter`, error);\n this.disableFilter(filter);\n }\n\n this.filters.delete(filter);\n\n if (this.metrics) {\n this.recordMetric('disposeFilter', startTime);\n }\n }\n\n /**\n * Get shader manager instance\n *\n * @returns The shader manager instance or null if not enabled\n */\n getShaderManager(): ShaderResourceManager | null {\n return this.shaderManager;\n }\n\n // ===== BATCH TRACKING METHODS =====\n\n /**\n * Track multiple textures at once\n *\n * @param textures - Map of URL to texture\n * @returns The same map for chaining\n */\n trackTextureBatch(textures: Map<string, Texture>): Map<string, Texture> {\n if (!this.isActive()) return textures;\n\n const startTime = this.metrics ? performance.now() : 0;\n\n textures.forEach((texture, url) => {\n const entry = this.textures.get(url);\n if (entry) {\n entry.refCount++;\n entry.lastUsed = Date.now();\n } else {\n this.textures.set(url, {\n resource: texture,\n refCount: 1,\n lastUsed: Date.now()\n });\n }\n });\n\n if (this.metrics) {\n this.recordMetric('trackTextureBatch', startTime);\n this.updateBatchStats('textures', textures.size);\n }\n\n this.log('debug', `Tracked ${textures.size} textures in batch`);\n return textures;\n }\n\n /**\n * Track multiple filters at once\n *\n * @param filters - Array of filters to track\n * @returns The same array for chaining\n */\n trackFilterBatch(filters: Filter[]): Filter[] {\n if (!this.isActive()) return filters;\n\n const startTime = this.metrics ? performance.now() : 0;\n\n filters.forEach(filter => this.filters.add(filter));\n\n if (this.metrics) {\n this.recordMetric('trackFilterBatch', startTime);\n this.updateBatchStats('filters', filters.length);\n }\n\n this.log('debug', `Tracked ${filters.length} filters in batch`);\n return filters;\n }\n\n /**\n * Track multiple display objects at once\n *\n * @param objects - Array of display objects to track\n * @returns The same array for chaining\n */\n trackDisplayObjectBatch(objects: Container[]): Container[] {\n if (!this.isActive()) return objects;\n\n const startTime = this.metrics ? performance.now() : 0;\n\n objects.forEach(object => this.displayObjects.add(object));\n\n if (this.metrics) {\n this.recordMetric('trackDisplayObjectBatch', startTime);\n this.updateBatchStats('displayObjects', objects.length);\n }\n\n this.log('debug', `Tracked ${objects.length} display objects in batch`);\n return objects;\n }\n\n /**\n * Track multiple animations at once\n *\n * @param animations - Array of animations to track\n * @returns The same array for chaining\n */\n trackAnimationBatch(animations: Animation[]): Animation[] {\n if (!this.isActive()) return animations;\n\n const startTime = this.metrics ? performance.now() : 0;\n\n animations.forEach(animation => this.animations.add(animation));\n\n if (this.metrics) {\n this.recordMetric('trackAnimationBatch', startTime);\n this.updateBatchStats('animations', animations.length);\n }\n\n this.log('debug', `Tracked ${animations.length} animations in batch`);\n return animations;\n }\n\n /**\n * Track event listeners in batch\n *\n * @param element - DOM element\n * @param listeners - Map of event types to callbacks\n */\n addEventListenerBatch(element: EventTarget, listeners: Map<string, EventCallback[]>): void {\n if (!this.isActive()) return;\n\n const startTime = this.metrics ? performance.now() : 0;\n let count = 0;\n\n // Ensure we have a map for this element\n if (!this.listeners.has(element)) {\n this.listeners.set(element, new Map());\n }\n\n const elementListeners = this.listeners.get(element)!;\n\n // Add all the listeners\n listeners.forEach((callbacks, eventType) => {\n if (!elementListeners.has(eventType)) {\n elementListeners.set(eventType, new Set());\n }\n\n const callbackSet = elementListeners.get(eventType)!;\n\n callbacks.forEach(callback => {\n callbackSet.add(callback);\n element.addEventListener(eventType, callback);\n count++;\n });\n });\n\n if (this.metrics) {\n this.recordMetric('addEventListenerBatch', startTime);\n }\n\n this.log('debug', `Added ${count} event listeners in batch`);\n }\n\n // ===== INDIVIDUAL TRACKING METHODS =====\n\n /**\n * Track a GSAP animation\n *\n * @param animation - Animation to track\n * @returns The animation for chaining\n */\n trackAnimation<T extends Animation>(animation: T): T {\n if (!this.isActive()) {\n animation.kill();\n return animation;\n }\n\n const startTime = this.metrics ? performance.now() : 0;\n this.animations.add(animation);\n\n if (this.metrics) {\n this.recordMetric('trackAnimation', startTime);\n this.updateBatchStats('animations', 1);\n }\n\n return animation;\n }\n\n /**\n * Track a texture\n *\n * @param url - Texture URL\n * @param texture - Texture to track\n * @returns The texture for chaining\n */\n trackTexture(url: string, texture: Texture): Texture {\n if (!this.isActive()) return texture;\n\n const startTime = this.metrics ? performance.now() : 0;\n\n const entry = this.textures.get(url);\n if (entry) {\n entry.refCount++;\n entry.lastUsed = Date.now();\n } else {\n this.textures.set(url, {\n resource: texture,\n refCount: 1,\n lastUsed: Date.now()\n });\n }\n\n if (this.metrics) {\n this.recordMetric('trackTexture', startTime);\n this.updateBatchStats('textures', 1);\n }\n\n return texture;\n }\n\n /**\n * Release a texture, destroying it when no longer referenced\n */\n releaseTexture(url: string): void {\n const entry = this.textures.get(url);\n if (!entry) return;\n\n const startTime = this.metrics ? performance.now() : 0;\n\n entry.refCount--;\n\n if (entry.refCount <= 0) {\n try {\n entry.resource.destroy(true);\n this.textures.delete(url);\n this.log('debug', `Destroyed texture: ${url}`);\n } catch (error) {\n this.log('warn', `Failed to destroy texture: ${url}`, error);\n }\n }\n\n if (this.metrics) {\n this.recordMetric('releaseTexture', startTime);\n }\n }\n\n /**\n * Track a PIXI Application\n *\n * @param app - Application to track\n * @returns The application for chaining\n */\n trackPixiApp(app: Application): Application {\n if (!this.isActive()) {\n this.disposePixiApp(app);\n return app;\n }\n\n const startTime = this.metrics ? performance.now() : 0;\n this.pixiApps.add(app);\n\n if (this.metrics) {\n this.recordMetric('trackPixiApp', startTime);\n }\n\n this.log('info', 'Tracking PIXI application');\n return app;\n }\n\n /**\n * Dispose of a PIXI Application\n */\n private disposePixiApp(app: Application): void {\n const startTime = this.metrics ? performance.now() : 0;\n\n try {\n app.stop();\n\n // Remove canvas from DOM\n if (app.canvas instanceof HTMLCanvasElement) {\n app.canvas.remove();\n }\n\n app.destroy(true, { children: true });\n this.log('info', 'PIXI application destroyed');\n } catch (error) {\n this.log('warn', 'Error disposing PIXI application', error);\n }\n\n this.pixiApps.delete(app);\n\n if (this.metrics) {\n this.recordMetric('disposePixiApp', startTime);\n }\n }\n\n /**\n * Track a display object\n *\n * @param displayObject - Display object to track\n * @returns The display object for chaining\n */\n trackDisplayObject<T extends Container>(displayObject: T): T {\n if (!this.isActive()) return displayObject;\n\n const startTime = this.metrics ? performance.now() : 0;\n this.displayObjects.add(displayObject);\n\n if (this.metrics) {\n this.recordMetric('trackDisplayObject', startTime);\n this.updateBatchStats('displayObjects', 1);\n }\n\n return displayObject;\n }\n\n /**\n * Dispose of a display object\n */\n private disposeDisplayObject(displayObject: Container): void {\n const startTime = this.metrics ? performance.now() : 0;\n\n try {\n // Remove from parent if possible\n if (displayObject.parent) {\n displayObject.parent.removeChild(displayObject);\n }\n\n // Disable filters first, then dispose them\n this.disableFiltersOnObject(displayObject);\n\n // Dispose filters if any\n if (displayObject.filters) {\n if (Array.isArray(displayObject.filters)) {\n displayObject.filters.forEach((filter: Filter) => {\n this.disposeFilter(filter);\n });\n } else {\n // Handle single filter case\n this.disposeFilter(displayObject.filters as Filter);\n }\n // Set to empty array instead of null\n displayObject.filters = [];\n }\n\n // Destroy the object with appropriate options\n displayObject.destroy({\n children: true,\n texture: false\n });\n } catch (error) {\n this.log('warn', 'Error disposing display object', error);\n }\n\n this.displayObjects.delete(displayObject);\n\n if (this.metrics) {\n this.recordMetric('disposeDisplayObject', startTime);\n }\n }\n\n /**\n * Add an event listener with tracking\n */\n addEventListener(\n element: EventTarget,\n eventType: string,\n callback: EventCallback\n ): void {\n if (!this.isActive()) return;\n\n const startTime = this.metrics ? performance.now() : 0;\n\n if (!this.listeners.has(element)) {\n this.listeners.set(element, new Map());\n }\n\n const elementListeners = this.listeners.get(element)!;\n if (!elementListeners.has(eventType)) {\n elementListeners.set(eventType, new Set());\n }\n\n const callbacks = elementListeners.get(eventType)!;\n callbacks.add(callback);\n element.addEventListener(eventType, callback);\n\n if (this.metrics) {\n this.recordMetric('addEventListener', startTime);\n }\n }\n\n /**\n * Remove all event listeners\n */\n private removeAllEventListeners(): void {\n const startTime = this.metrics ? performance.now() : 0;\n let count = 0;\n\n this.listeners.forEach((eventMap, element) => {\n eventMap.forEach((callbacks, eventType) => {\n callbacks.forEach(callback => {\n element.removeEventListener(eventType, callback);\n count++;\n });\n });\n });\n\n this.listeners.clear();\n\n if (this.metrics) {\n this.recordMetric('removeAllEventListeners', startTime);\n }\n\n this.log('debug', `Removed ${count} event listeners`);\n }\n\n /**\n * Create a setTimeout with tracking\n */\n setTimeout(callback: () => void, delay: number): Timer {\n if (!this.isActive()) return setTimeout(() => {}, 0);\n\n const startTime = this.metrics ? performance.now() : 0;\n\n const timeout = setTimeout(() => {\n this.timeouts.delete(timeout);\n callback();\n }, delay);\n\n this.timeouts.add(timeout);\n\n if (this.metrics) {\n this.recordMetric('setTimeout', startTime);\n }\n\n return timeout;\n }\n\n /**\n * Create a setInterval with tracking\n */\n setInterval(callback: () => void, delay: number): Timer {\n if (!this.isActive()) return setInterval(() => {}, 0);\n\n const startTime = this.metrics ? performance.now() : 0;\n\n const interval = setInterval(callback, delay);\n this.intervals.add(interval);\n\n if (this.metrics) {\n this.recordMetric('setInterval', startTime);\n }\n\n return interval;\n }\n\n /**\n * Clear a tracked timeout\n */\n clearTimeout(id: Timer): void {\n const startTime = this.metrics ? performance.now() : 0;\n\n globalThis.clearTimeout(id);\n this.timeouts.delete(id);\n\n if (this.metrics) {\n this.recordMetric('clearTimeout', startTime);\n }\n }\n\n /**\n * Clear a tracked interval\n */\n clearInterval(id: Timer): void {\n const startTime = this.metrics ? performance.now() : 0;\n\n globalThis.clearInterval(id);\n this.intervals.delete(id);\n\n if (this.metrics) {\n this.recordMetric('clearInterval', startTime);\n }\n }\n\n /**\n * Clear all tracked timeouts\n */\n private clearAllTimeouts(): void {\n const startTime = this.metrics ? performance.now() : 0;\n let count = 0;\n\n this.timeouts.forEach(id => {\n globalThis.clearTimeout(id);\n count++;\n });\n\n this.timeouts.clear();\n\n if (this.metrics) {\n this.recordMetric('clearAllTimeouts', startTime);\n }\n\n this.log('debug', `Cleared ${count} timeouts`);\n }\n\n /**\n * Clear all tracked intervals\n */\n private clearAllIntervals(): void {\n const startTime = this.metrics ? performance.now() : 0;\n let count = 0;\n\n this.intervals.forEach(id => {\n globalThis.clearInterval(id);\n count++;\n });\n\n this.intervals.clear();\n\n if (this.metrics) {\n this.recordMetric('clearAllIntervals', startTime);\n }\n\n this.log('debug', `Cleared ${count} intervals`);\n }\n\n /**\n * Get current resource statistics\n *\n * @returns An object containing counts of various tracked resources\n */\n getStats(): Record<string, number | BatchStats | PerformanceMetrics | any> {\n const result: Record<string, number | BatchStats | PerformanceMetrics | any> = {\n textures: this.textures.size,\n filters: this.filters.size,\n displayObjects: this.displayObjects.size,\n animations: this.animations.size,\n eventTargets: this.listeners.size,\n timeouts: this.timeouts.size,\n intervals: this.intervals.size,\n pixiApps: this.pixiApps.size,\n shaderPoolingEnabled: !!this.shaderManager\n };\n\n // Add performance metrics if enabled\n if (this.metrics) {\n result.metrics = this.metrics;\n }\n\n // Add shader manager stats if available\n if (this.shaderManager) {\n result.shaderManager = this.shaderManager.getStats();\n }\n\n return result;\n }\n\n /**\n * Clear all tracked resources\n */\n dispose(): void {\n if (this.disposed) return;\n\n const startTime = this.metrics ? performance.now() : 0;\n\n this.log('info', 'Disposing all resources...');\n\n this.disposed = true;\n this.markUnmounting();\n\n // Stop auto cleanup if running\n if (this.autoCleanupTimer) {\n clearInterval(this.autoCleanupTimer);\n this.autoCleanupTimer = null;\n }\n\n // Clear animations first to stop visual changes\n this.animations.forEach(animation => animation.kill());\n this.animations.clear();\n\n // Remove all event listeners\n this.removeAllEventListeners();\n\n // Dispose PIXI applications (which will handle most resources)\n this.pixiApps.forEach(app => this.disposePixiApp(app));\n this.pixiApps.clear();\n\n // Disable all filters on display objects first\n this.displayObjects.forEach(obj => this.disableFiltersOnObject(obj));\n\n // Dispose remaining display objects\n this.displayObjects.forEach(obj => this.disposeDisplayObject(obj));\n this.displayObjects.clear();\n\n // Dispose filters\n this.filters.forEach(filter => this.disposeFilter(filter));\n this.filters.clear();\n\n // Release textures\n this.textures.forEach((entry, url) => {\n try {\n entry.resource.destroy(true);\n } catch (error) {\n this.log('warn', `Error disposing texture: ${url}`, error);\n }\n });\n this.textures.clear();\n\n // Clear timers\n this.clearAllTimeouts();\n this.clearAllIntervals();\n\n // Clear shader manager if present\n if (this.shaderManager) {\n // Release all filters from the shader manager by iterating through our filters\n this.filters.forEach(filter => {\n this.shaderManager?.releaseShader(filter as any);\n });\n // Set to null without calling the nonexistent clear method\n this.shaderManager = null;\n }\n\n if (this.metrics) {\n this.recordMetric('dispose', startTime);\n\n // Log performance summary\n this.log('info', 'Performance metrics summary:', this.metrics);\n }\n\n this.log('info', 'All resources disposed');\n }\n\n /**\n * Clears any pending updates in the queue\n */\n clearPendingUpdates(): void {\n // Clear any pending timeouts or intervals\n this.timeouts.forEach(clearTimeout);\n this.timeouts.clear();\n\n // Clear any pending animation frames\n this.intervals.forEach(clearInterval);\n this.intervals.clear();\n }\n\n /**\n * Monitor filter performance and adjust quality if necessary\n * Part of the shader optimization implementation\n *\n * @param filter - Filter to monitor\n * @param performanceThreshold - Performance threshold in ms\n * @param qualityReduceFactor - How much to reduce quality (0-1)\n * @returns Whether the filter was optimized\n */\n monitorFilterPerformance(\n filter: Filter,\n performanceThreshold: number = 16,\n qualityReduceFactor: number = 0.5\n ): boolean {\n if (!filter || !this.isActive()) return false;\n\n try {\n // Measure filter rendering time\n const startTime = performance.now();\n\n // Simulate a render cycle - in practice this would be done\n // during actual rendering with proper timing\n if (filter.enabled) {\n // Force filter to update its internal state\n if ('apply' in filter && typeof filter.apply === 'function') {\n // This is a simplification - in a real implementation,\n // we would measure during actual rendering\n this.log('debug', 'Monitoring filter performance');\n }\n }\n\n const endTime = performance.now();\n const renderTime = endTime - startTime;\n\n // If rendering takes too long, reduce quality\n if (renderTime > performanceThreshold) {\n this.optimizeFilterQuality(filter, qualityReduceFactor);\n this.log('info', `Filter performance optimized: ${renderTime.toFixed(2)}ms -> threshold: ${performanceThreshold}ms`);\n return true;\n }\n\n return false;\n } catch (error) {\n this.log('warn', 'Error monitoring filter performance', error);\n return false;\n }\n }\n\n /**\n * Optimize a filter's quality settings to improve performance\n *\n * @param filter - Filter to optimize\n * @param factor - Factor to reduce quality by (0-1)\n * @returns Whether optimization was applied\n */\n optimizeFilterQuality(filter: Filter, factor: number = 0.5): boolean {\n if (!filter || !this.isActive()) return false;\n\n try {\n let optimized = false;\n\n // Adjust quality parameter if available\n if ('quality' in filter && typeof filter.quality === 'number') {\n const newQuality = Math.max(1, Math.floor(filter.quality * factor));\n if (newQuality < filter.quality) {\n filter.quality = newQuality;\n optimized = true;\n this.log('debug', `Reduced filter quality to ${newQuality}`);\n }\n }\n\n // Adjust resolution if available\n if ('resolution' in filter && typeof filter.resolution === 'number') {\n const newResolution = Math.max(0.1, filter.resolution * factor);\n if (newResolution < filter.resolution) {\n filter.resolution = newResolution;\n optimized = true;\n this.log('debug', `Reduced filter resolution to ${newResolution.toFixed(2)}`);\n }\n }\n\n // Many filters have a specific quality parameter\n const specificParams = [\n 'kernelSize', 'blur', 'steps', 'passes', 'iterations',\n 'sampleSize', 'pixelSize', 'blurX', 'blurY'\n ];\n\n // Try to adjust known quality parameters\n for (const param of specificParams) {\n if (param in filter && typeof (filter as any)[param] === 'number') {\n const currentValue = (filter as any)[param];\n // For parameters where higher = better quality, reduce\n const newValue = Math.max(1, Math.floor(currentValue * factor));\n\n if (newValue < currentValue) {\n (filter as any)[param] = newValue;\n optimized = true;\n this.log('debug', `Reduced filter ${param} to ${newValue}`);\n }\n }\n }\n\n return optimized;\n } catch (error) {\n this.log('warn', 'Error optimizing filter quality', error);\n return false;\n }\n }\n\n /**\n * Run diagnostics on all active filters\n * Provides insights into filter performance\n *\n * @returns Diagnostic information about filters\n */\n runFilterDiagnostics(): Record<string, any> {\n const results: Record<string, any> = {\n totalFilters: this.filters.size,\n filtersByType: {},\n potentialOptimizations: 0\n };\n\n try {\n // Group filters by type\n this.filters.forEach(filter => {\n const filterType = filter.constructor.name;\n if (!results.filtersByType[filterType]) {\n results.filtersByType[filterType] = 0;\n }\n results.filtersByType[filterType]++;\n\n // Check for potential optimizations\n let canOptimize = false;\n\n // Check common quality parameters\n if ('quality' in filter && typeof filter.quality === 'number' && filter.quality > 1) {\n canOptimize = true;\n }\n\n if ('resolution' in filter && typeof filter.resolution === 'number' && filter.resolution > 0.5) {\n canOptimize = true;\n }\n\n // Count potential optimizations\n if (canOptimize) {\n results.potentialOptimizations++;\n }\n });\n\n // Add shader manager diagnostics if available\n if (this.shaderManager) {\n results.shaderPoolStats = this.shaderManager.getStats();\n }\n\n return results;\n } catch (error) {\n this.log('warn', 'Error running filter diagnostics', error);\n return { error: 'Failed to run diagnostics', totalFilters: this.filters.size };\n }\n }\n\n /**\n * Automatically optimize all filters based on FPS\n * This method should be called periodically during animation\n *\n * @param currentFPS - Current FPS of the application\n * @param targetFPS - Target FPS to maintain\n * @param optimizationStep - How aggressive to optimize (0-1)\n * @returns Number of filters optimized\n */\n autoOptimizeFilters(\n currentFPS: number,\n targetFPS: number = 55,\n optimizationStep: number = 0.8\n ): number {\n if (!this.isActive() || this.filters.size === 0) return 0;\n\n // Don't optimize if FPS is already good\n if (currentFPS >= targetFPS) return 0;\n\n try {\n let optimizedCount = 0;\n const fpsDifference = targetFPS - currentFPS;\n\n // More aggressive optimization for lower FPS\n const optimizationFactor = Math.min(0.9, Math.max(0.5,\n optimizationStep * (1 - (currentFPS / targetFPS))\n ));\n\n this.log('info', `Auto-optimizing filters: FPS ${currentFPS.toFixed(1)}/${targetFPS}, factor: ${optimizationFactor.toFixed(2)}`);\n\n // Sort filters by complexity/cost (simplified approach)\n // In a real implementation, you'd track actual rendering cost\n const filterEntries = Array.from(this.filters.entries())\n .map(([id, filter]) => {\n // Estimate filter cost based on its properties\n let estimatedCost = 1;\n\n // Quality-based cost estimation\n if ('quality' in filter && typeof filter.quality === 'number') {\n estimatedCost *= filter.quality;\n }\n\n // Resolution-based cost estimation\n if ('resolution' in filter && typeof filter.resolution === 'number') {\n estimatedCost *= filter.resolution;\n }\n\n return { id, filter, cost: estimatedCost };\n })\n .sort((a, b) => b.cost - a.cost); // Sort by highest cost first\n\n // Optimize the most expensive filters first\n for (const { filter } of filterEntries) {\n if (optimizedCount >= 3) break; // Limit optimizations per frame\n\n const wasOptimized = this.optimizeFilterQuality(filter, optimizationFactor);\n if (wasOptimized) {\n optimizedCount++;\n }\n }\n\n if (optimizedCount > 0) {\n this.log('info', `Optimized ${optimizedCount} filters to improve performance`);\n }\n\n return optimizedCount;\n } catch (error) {\n this.log('warn', 'Error auto-optimizing filters', error);\n return 0;\n }\n }\n}\n\nexport default ResourceManager;"],"names":["ShaderResourceManager"],"mappings":";;;;;;;;;AAoEA,MAAM,eAAgB,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkClB,WAAY,CAAA,WAAA,EAAqB,OAAkC,GAAA,EAAI,EAAA;AAhCvE;AAAA,IAAQ,aAAA,CAAA,IAAA,EAAA,UAAA,sBAAe,GAAoC,EAAA,CAAA;AAC3D,IAAQ,aAAA,CAAA,IAAA,EAAA,SAAA,sBAAc,GAAY,EAAA,CAAA;AAClC,IAAQ,aAAA,CAAA,IAAA,EAAA,gBAAA,sBAAqB,GAAe,EAAA,CAAA;AAC5C,IAAQ,aAAA,CAAA,IAAA,EAAA,UAAA,sBAAe,GAAiB,EAAA,CAAA;AACxC,IAAQ,aAAA,CAAA,IAAA,EAAA,YAAA,sBAAiB,GAAe,EAAA,CAAA;AAGxC;AAAA,IAAQ,aAAA,CAAA,IAAA,EAAA,WAAA,sBAAgB,GAAkD,EAAA,CAAA;AAG1E;AAAA,IAAQ,aAAA,CAAA,IAAA,EAAA,UAAA,sBAAe,GAAW,EAAA,CAAA;AAClC,IAAQ,aAAA,CAAA,IAAA,EAAA,WAAA,sBAAgB,GAAW,EAAA,CAAA;AAGnC;AAAA,IAAA,aAAA,CAAA,IAAA,EAAQ,UAAW,EAAA,KAAA,CAAA;AACnB,IAAA,aAAA,CAAA,IAAA,EAAQ,YAAa,EAAA,KAAA,CAAA;AACrB,IAAiB,aAAA,CAAA,IAAA,EAAA,aAAA,CAAA;AACjB,IAAiB,aAAA,CAAA,IAAA,EAAA,SAAA,CAAA;AAGjB;AAAA,IAAA,aAAA,CAAA,IAAA,EAAQ,SAAqC,EAAA,IAAA,CAAA;AAC7C,IAAA,aAAA,CAAA,IAAA,EAAQ,kBAAiC,EAAA,IAAA,CAAA;AAGzC;AAAA,IAAA,aAAA,CAAA,IAAA,EAAQ,eAA8C,EAAA,IAAA,CAAA;AASlD,IAAA,IAAA,CAAK,WAAc,GAAA,WAAA;AACnB,IAAA,IAAA,CAAK,OAAU,GAAA;AAAA,MACX,QAAA,EAAU,QAAQ,QAAY,IAAA,MAAA;AAAA,MAC9B,aAAA,EAAe,QAAQ,aAAiB,IAAA,KAAA;AAAA,MACxC,mBAAA,EAAqB,QAAQ,mBAAuB,IAAA,IAAA;AAAA,MACpD,mBAAA,EAAqB,QAAQ,mBAAwB,KAAA;AAAA;AAAA,KACzD;AAGA,IAAI,IAAA,IAAA,CAAK,QAAQ,aAAe,EAAA;AAC5B,MAAA,IAAA,CAAK,OAAU,GAAA;AAAA,QACX,YAAY,EAAC;AAAA,QACb,UAAY,EAAA;AAAA,UACR,QAAA,EAAU,KAAK,qBAAsB,EAAA;AAAA,UACrC,OAAA,EAAS,KAAK,qBAAsB,EAAA;AAAA,UACpC,cAAA,EAAgB,KAAK,qBAAsB,EAAA;AAAA,UAC3C,UAAA,EAAY,KAAK,qBAAsB;AAAA;AAC3C,OACJ;AAAA;AAIJ,IAAI,IAAA,IAAA,CAAK,QAAQ,mBAAqB,EAAA;AAClC,MAAK,IAAA,CAAA,aAAA,GAAgBA,4CAAsB,WAAY,CAAA;AAAA,QACnD,KAAA,EAAO,IAAK,CAAA,OAAA,CAAQ,QAAa,KAAA,OAAA;AAAA,QACjC,aAAA,EAAe,KAAK,OAAQ,CAAA;AAAA,OAC/B,CAAA;AACD,MAAK,IAAA,CAAA,GAAA,CAAI,QAAQ,wBAAwB,CAAA;AAAA;AAG7C,IAAK,IAAA,CAAA,GAAA,CAAI,QAAQ,CAA6B,2BAAA,CAAA,CAAA;AAG9C,IAAA,IAAA,CAAK,gBAAiB,EAAA;AAAA;AAC1B;AAAA;AAAA;AAAA,EAKQ,qBAAoC,GAAA;AACxC,IAAO,OAAA;AAAA,MACH,YAAc,EAAA,CAAA;AAAA,MACd,UAAY,EAAA,CAAA;AAAA,MACZ,gBAAkB,EAAA,CAAA;AAAA,MAClB,YAAc,EAAA;AAAA,KAClB;AAAA;AACJ;AAAA;AAAA;AAAA,EAKQ,gBAAyB,GAAA;AAC7B,IAAI,IAAA,IAAA,CAAK,QAAQ,mBAAqB,EAAA;AAClC,MAAK,IAAA,CAAA,gBAAA,GAAmB,YAAY,MAAM;AACtC,QAAA,IAAA,CAAK,kBAAmB,EAAA;AAAA,OAC5B,EAAG,IAAK,CAAA,OAAA,CAAQ,mBAAmB,CAAA;AAAA;AACvC;AACJ;AAAA;AAAA;AAAA,EAKQ,kBAA2B,GAAA;AAC/B,IAAI,IAAA,IAAA,CAAK,QAAY,IAAA,IAAA,CAAK,UAAY,EAAA;AAEtC,IAAK,IAAA,CAAA,GAAA,CAAI,SAAS,0CAA0C,CAAA;AAG5D,IAAA,IAAI,gBAAmB,GAAA,CAAA;AACvB,IAAA,IAAA,CAAK,QAAS,CAAA,OAAA,CAAQ,CAAC,KAAA,EAAO,GAAQ,KAAA;AAClC,MAAI,IAAA,KAAA,CAAM,YAAY,CAAG,EAAA;AACrB,QAAA,IAAA,CAAK,eAAe,GAAG,CAAA;AACvB,QAAA,gBAAA,EAAA;AAAA;AACJ,KACH,CAAA;AAGD,IAAA,IAAI,kBAAqB,GAAA,CAAA;AACzB,IAAK,IAAA,CAAA,UAAA,CAAW,QAAQ,CAAa,SAAA,KAAA;AACjC,MAAI,IAAA,SAAA,CAAU,QAAS,EAAA,KAAM,KAAO,EAAA;AAChC,QAAA,SAAA,CAAU,IAAK,EAAA;AACf,QAAK,IAAA,CAAA,UAAA,CAAW,OAAO,SAAS,CAAA;AAChC,QAAA,kBAAA,EAAA;AAAA;AACJ,KACH,CAAA;AAED,IAAA,IAAA,CAAK,IAAI,OAAS,EAAA,CAAA,uBAAA,EAA0B,gBAAgB,CAAA,cAAA,EAAiB,kBAAkB,CAAsB,oBAAA,CAAA,CAAA;AAAA;AACzH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,GAAA,CAAI,KAA4C,EAAA,OAAA,EAAiB,IAAkB,EAAA;AACvF,IAAA,MAAM,SAAY,GAAA;AAAA,MACd,OAAS,EAAA,CAAA;AAAA,MACT,MAAQ,EAAA,CAAA;AAAA,MACR,MAAQ,EAAA,CAAA;AAAA,MACR,OAAS,EAAA;AAAA,KACb;AAEA,IAAI,IAAA,SAAA,CAAU,KAAK,CAAK,IAAA,SAAA,CAAU,KAAK,OAAQ,CAAA,QAAA,IAAY,MAAM,CAAG,EAAA;AAChE,MAAA,MAAM,UAAa,GAAA,CAAA,iBAAA,EAAoB,IAAK,CAAA,WAAW,KAAK,OAAO,CAAA,CAAA;AAEnE,MAAA,QAAQ,KAAO;AAAA,QACX,KAAK,OAAA;AACD,UAAQ,OAAA,CAAA,KAAA,CAAM,YAAY,IAAI,CAAA;AAC9B,UAAA;AAAA,QACJ,KAAK,MAAA;AACD,UAAQ,OAAA,CAAA,IAAA,CAAK,YAAY,IAAI,CAAA;AAC7B,UAAA;AAAA,QACJ,KAAK,MAAA;AACD,UAAQ,OAAA,CAAA,IAAA,CAAK,YAAY,IAAI,CAAA;AAC7B,UAAA;AAAA,QACJ,KAAK,OAAA;AACD,UAAQ,OAAA,CAAA,KAAA,CAAM,YAAY,IAAI,CAAA;AAC9B,UAAA;AAAA;AACR;AACJ;AACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,YAAA,CAAa,MAAc,SAAyB,EAAA;AACxD,IAAI,IAAA,CAAC,KAAK,OAAS,EAAA;AAEnB,IAAM,MAAA,OAAA,GAAU,YAAY,GAAI,EAAA;AAChC,IAAA,MAAM,WAAW,OAAU,GAAA,SAAA;AAE3B,IAAA,IAAI,CAAC,IAAA,CAAK,OAAQ,CAAA,UAAA,CAAW,IAAI,CAAG,EAAA;AAChC,MAAK,IAAA,CAAA,OAAA,CAAQ,UAAW,CAAA,IAAI,CAAI,GAAA;AAAA,QAC5B,KAAO,EAAA,CAAA;AAAA,QACP,SAAW,EAAA,CAAA;AAAA,QACX,WAAa,EAAA;AAAA,OACjB;AAAA;AAGJ,IAAA,MAAM,EAAK,GAAA,IAAA,CAAK,OAAQ,CAAA,UAAA,CAAW,IAAI,CAAA;AACvC,IAAG,EAAA,CAAA,KAAA,EAAA;AACH,IAAA,EAAA,CAAG,SAAa,IAAA,QAAA;AAChB,IAAG,EAAA,CAAA,WAAA,GAAc,EAAG,CAAA,SAAA,GAAY,EAAG,CAAA,KAAA;AAAA;AACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,gBAAA,CAAiB,MAAgE,SAAyB,EAAA;AAC9G,IAAI,IAAA,CAAC,KAAK,OAAS,EAAA;AAEnB,IAAA,MAAM,KAAQ,GAAA,IAAA,CAAK,OAAQ,CAAA,UAAA,CAAW,IAAI,CAAA;AAC1C,IAAM,KAAA,CAAA,YAAA,EAAA;AACN,IAAA,KAAA,CAAM,UAAc,IAAA,SAAA;AACpB,IAAM,KAAA,CAAA,gBAAA,GAAmB,KAAM,CAAA,UAAA,GAAa,KAAM,CAAA,YAAA;AAClD,IAAA,KAAA,CAAM,YAAe,GAAA,IAAA,CAAK,GAAI,CAAA,KAAA,CAAM,cAAc,SAAS,CAAA;AAAA;AAC/D;AAAA;AAAA;AAAA,EAKA,cAAuB,GAAA;AACnB,IAAA,IAAA,CAAK,UAAa,GAAA,IAAA;AAClB,IAAK,IAAA,CAAA,GAAA,CAAI,QAAQ,gCAAgC,CAAA;AAAA;AACrD;AAAA;AAAA;AAAA,EAKA,QAAoB,GAAA;AAChB,IAAA,OAAO,CAAC,IAAA,CAAK,UAAc,IAAA,CAAC,IAAK,CAAA,QAAA;AAAA;AACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,cAAc,MAAsB,EAAA;AAChC,IAAI,IAAA;AAEA,MAAA,IAAI,SAAa,IAAA,MAAA,IAAU,OAAO,MAAA,CAAO,YAAY,SAAW,EAAA;AAC5D,QAAA,MAAA,CAAO,OAAU,GAAA,KAAA;AAAA;AAIrB,MAAA,IAAI,OAAW,IAAA,MAAA,IAAU,OAAO,MAAA,CAAO,UAAU,QAAU,EAAA;AACvD,QAAA,MAAA,CAAO,KAAQ,GAAA,CAAA;AAAA;AAInB,MAAA,IAAI,UAAc,IAAA,MAAA,IAAU,OAAO,MAAA,CAAO,aAAa,QAAU,EAAA;AAC7D,QAAA,MAAA,CAAO,QAAW,GAAA,CAAA;AAAA;AAItB,MAAA,IAAI,WAAW,MAAQ,EAAA;AACnB,QAAA,MAAM,QAAQ,MAAO,CAAA,KAAA;AACrB,QAAI,IAAA,KAAA,IAAS,OAAO,KAAM,CAAA,CAAA,KAAM,YAAY,OAAO,KAAA,CAAM,MAAM,QAAU,EAAA;AACrE,UAAA,KAAA,CAAM,CAAI,GAAA,CAAA;AACV,UAAA,KAAA,CAAM,CAAI,GAAA,CAAA;AAAA,SACd,MAAA,IAAW,OAAO,KAAA,KAAU,QAAU,EAAA;AAClC,UAAC,OAAe,KAAQ,GAAA,CAAA;AAAA;AAC5B;AAIJ,MAAA,IAAI,MAAU,IAAA,MAAA,IAAU,OAAO,MAAA,CAAO,SAAS,QAAU,EAAA;AACrD,QAAA,MAAA,CAAO,IAAO,GAAA,CAAA;AAAA;AAClB,aACK,KAAO,EAAA;AACZ,MAAK,IAAA,CAAA,GAAA,CAAI,OAAS,EAAA,wBAAA,EAA0B,KAAK,CAAA;AAAA;AACrD;AACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,uBAAuB,aAAgC,EAAA;AACnD,IAAI,IAAA,CAAC,cAAc,OAAS,EAAA;AAE5B,IAAI,IAAA;AACA,MAAA,IAAI,KAAM,CAAA,OAAA,CAAQ,aAAc,CAAA,OAAO,CAAG,EAAA;AACtC,QAAc,aAAA,CAAA,OAAA,CAAQ,QAAQ,CAAU,MAAA,KAAA;AACpC,UAAI,IAAA,MAAA,EAAa,IAAA,CAAA,aAAA,CAAc,MAAM,CAAA;AAAA,SACxC,CAAA;AAAA,OACL,MAAA,IAAW,cAAc,OAAS,EAAA;AAE9B,QAAK,IAAA,CAAA,aAAA,CAAc,cAAc,OAAiB,CAAA;AAAA;AAGtD,MAAK,IAAA,CAAA,GAAA,CAAI,SAAS,oCAAoC,CAAA;AAAA,aACjD,KAAO,EAAA;AACZ,MAAK,IAAA,CAAA,GAAA,CAAI,MAAQ,EAAA,mCAAA,EAAqC,KAAK,CAAA;AAAA;AAC/D;AACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,yBAA2C,MAAc,EAAA;AACrD,IAAA,IAAA,CAAK,cAAc,MAAM,CAAA;AACzB,IAAO,OAAA,MAAA;AAAA;AACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,8BAAkD,OAAe,EAAA;AAC7D,IAAA,OAAA,CAAQ,OAAQ,CAAA,CAAA,MAAA,KAAU,IAAK,CAAA,aAAA,CAAc,MAAM,CAAC,CAAA;AACpD,IAAO,OAAA,OAAA;AAAA;AACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,YAAY,MAAwB,EAAA;AAChC,IAAA,IAAI,CAAC,IAAA,CAAK,QAAS,EAAA,EAAU,OAAA,MAAA;AAE7B,IAAA,MAAM,SAAY,GAAA,IAAA,CAAK,OAAU,GAAA,WAAA,CAAY,KAAQ,GAAA,CAAA;AACrD,IAAK,IAAA,CAAA,OAAA,CAAQ,IAAI,MAAM,CAAA;AAGvB,IAAA,IAAI,KAAK,aAAe,EAAA;AAIpB,MAAK,IAAA,CAAA,GAAA,CAAI,SAAS,CAAuC,qCAAA,CAAA,CAAA;AAAA;AAG7D,IAAA,IAAI,KAAK,OAAS,EAAA;AACd,MAAK,IAAA,CAAA,YAAA,CAAa,eAAe,SAAS,CAAA;AAC1C,MAAK,IAAA,CAAA,gBAAA,CAAiB,WAAW,CAAC,CAAA;AAAA;AAGtC,IAAO,OAAA,MAAA;AAAA;AACX;AAAA;AAAA;AAAA,EAKQ,cAAc,MAAsB,EAAA;AACxC,IAAA,MAAM,SAAY,GAAA,IAAA,CAAK,OAAU,GAAA,WAAA,CAAY,KAAQ,GAAA,CAAA;AAErD,IAAI,IAAA;AAEA,MAAA,IAAA,CAAK,cAAc,MAAM,CAAA;AAGzB,MAAA,IAAI,KAAK,aAAe,EAAA;AACpB,QAAK,IAAA,CAAA,aAAA,CAAc,cAAc,MAAa,CAAA;AAAA;AAIlD,MAAA,MAAA,CAAO,OAAQ,EAAA;AAAA,aACV,KAAO,EAAA;AAEZ,MAAK,IAAA,CAAA,GAAA,CAAI,OAAS,EAAA,CAAA,kCAAA,CAAA,EAAsC,KAAK,CAAA;AAC7D,MAAA,IAAA,CAAK,cAAc,MAAM,CAAA;AAAA;AAG7B,IAAK,IAAA,CAAA,OAAA,CAAQ,OAAO,MAAM,CAAA;AAE1B,IAAA,IAAI,KAAK,OAAS,EAAA;AACd,MAAK,IAAA,CAAA,YAAA,CAAa,iBAAiB,SAAS,CAAA;AAAA;AAChD;AACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAiD,GAAA;AAC7C,IAAA,OAAO,IAAK,CAAA,aAAA;AAAA;AAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,kBAAkB,QAAsD,EAAA;AACpE,IAAA,IAAI,CAAC,IAAA,CAAK,QAAS,EAAA,EAAU,OAAA,QAAA;AAE7B,IAAA,MAAM,SAAY,GAAA,IAAA,CAAK,OAAU,GAAA,WAAA,CAAY,KAAQ,GAAA,CAAA;AAErD,IAAS,QAAA,CAAA,OAAA,CAAQ,CAAC,OAAA,EAAS,GAAQ,KAAA;AAC/B,MAAA,MAAM,KAAQ,GAAA,IAAA,CAAK,QAAS,CAAA,GAAA,CAAI,GAAG,CAAA;AACnC,MAAA,IAAI,KAAO,EAAA;AACP,QAAM,KAAA,CAAA,QAAA,E