@frybynite/image-cloud 0.9.6 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -15
- package/dist/image-cloud-auto-init.js +47 -46
- package/dist/image-cloud-auto-init.js.map +1 -1
- package/dist/image-cloud.js +79 -74
- package/dist/image-cloud.js.map +1 -1
- package/dist/image-cloud.umd.js +3 -3
- package/dist/image-cloud.umd.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/react.d.ts +2 -0
- package/dist/react.js +101 -101
- package/dist/react.js.map +1 -1
- package/dist/vue.d.ts +2 -0
- package/dist/vue.js +53 -53
- package/dist/vue.js.map +1 -1
- package/dist/web-component.d.ts +2 -0
- package/dist/web-component.js +3 -3
- package/dist/web-component.js.map +1 -1
- package/package.json +1 -1
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.js","sources":["../src/config/defaults.ts","../src/engines/AnimationEngine.ts","../src/engines/PathAnimator.ts","../src/engines/EntryAnimationEngine.ts","../src/engines/IdleAnimationEngine.ts","../src/layouts/RandomPlacementLayout.ts","../src/layouts/RadialPlacementLayout.ts","../src/layouts/GridPlacementLayout.ts","../src/layouts/SpiralPlacementLayout.ts","../src/layouts/ClusterPlacementLayout.ts","../src/layouts/WavePlacementLayout.ts","../src/utils/hexagonGeometry.ts","../src/layouts/HoneycombPlacementLayout.ts","../src/engines/LayoutEngine.ts","../src/config/types.ts","../src/utils/clipPathGenerator.ts","../src/utils/styleUtils.ts","../src/engines/ZoomEngine.ts","../src/engines/SwipeEngine.ts","../src/loaders/GoogleDriveLoader.ts","../src/loaders/StaticImageLoader.ts","../src/loaders/CompositeLoader.ts","../src/loaders/ImageFilter.ts","../src/styles/functionalStyles.ts","../src/ImageCloud.ts","../src/react/index.tsx"],"sourcesContent":["/**\n * Default configuration for Image Gallery\n * Centralized settings for animation, layout, and API configuration\n */\n\nimport type { ImageCloudConfig, ImageCloudOptions, DeepPartial, ImageStylingConfig, ImageStyleState, ShadowPreset, WaveAlgorithmConfig, HoneycombAlgorithmConfig, RadialAlgorithmConfig, BouncePathConfig, ElasticPathConfig, WavePathConfig, BouncePreset, ElasticPreset, WavePathPreset, EntryPathConfig, EntryRotationConfig, EntryScaleConfig, ImageConfig, ImageSizingConfig, ImageRotationConfig, ImageVarianceConfig, ResponsiveBreakpoints, SharedLoaderConfig, ConfigSection, LoaderEntry, DebugConfig, IdleWiggleConfig, IdlePulseConfig, IdleBlinkConfig, IdleSpinConfig, IdleAnimationConfig } from './types';\n\n/**\n * Shadow presets for image styling\n */\nexport const SHADOW_PRESETS: Record<ShadowPreset, string> = Object.freeze({\n 'none': 'none',\n 'sm': '0 2px 4px rgba(0,0,0,0.1)',\n 'md': '0 4px 16px rgba(0,0,0,0.4)',\n 'lg': '0 8px 32px rgba(0,0,0,0.5)',\n 'glow': '0 0 30px rgba(255,255,255,0.6)'\n});\n\n/**\n * Bounce path presets - overshoot and settle animations\n */\nexport const BOUNCE_PRESETS: Record<BouncePreset, BouncePathConfig> = Object.freeze({\n energetic: Object.freeze({ overshoot: 0.25, bounces: 2, decayRatio: 0.5 }),\n playful: Object.freeze({ overshoot: 0.15, bounces: 1, decayRatio: 0.5 }),\n subtle: Object.freeze({ overshoot: 0.08, bounces: 1, decayRatio: 0.5 })\n});\n\n/**\n * Elastic path presets - spring-like oscillation animations\n */\nexport const ELASTIC_PRESETS: Record<ElasticPreset, ElasticPathConfig> = Object.freeze({\n gentle: Object.freeze({ stiffness: 150, damping: 30, mass: 1, oscillations: 2 }),\n bouncy: Object.freeze({ stiffness: 300, damping: 15, mass: 1, oscillations: 4 }),\n wobbly: Object.freeze({ stiffness: 180, damping: 12, mass: 1.5, oscillations: 5 }),\n snappy: Object.freeze({ stiffness: 400, damping: 25, mass: 0.8, oscillations: 2 })\n});\n\n/**\n * Wave path presets - sinusoidal path animations\n */\nexport const WAVE_PATH_PRESETS: Record<WavePathPreset, WavePathConfig> = Object.freeze({\n gentle: Object.freeze({ amplitude: 30, frequency: 1.5, decay: true, decayRate: 0.9, phase: 0 }),\n playful: Object.freeze({ amplitude: 50, frequency: 2.5, decay: true, decayRate: 0.7, phase: 0 }),\n serpentine: Object.freeze({ amplitude: 60, frequency: 3, decay: false, decayRate: 1, phase: 0 }),\n flutter: Object.freeze({ amplitude: 20, frequency: 4, decay: true, decayRate: 0.5, phase: 0 })\n});\n\n/**\n * Default path configuration (linear - no special path effects)\n */\nexport const DEFAULT_PATH_CONFIG: EntryPathConfig = Object.freeze({\n type: 'linear' as const\n});\n\n/**\n * Default entry rotation configuration (no rotation animation)\n */\nexport const DEFAULT_ENTRY_ROTATION: EntryRotationConfig = Object.freeze({\n mode: 'none' as const\n});\n\n/**\n * Default entry scale configuration (no scale animation)\n */\nexport const DEFAULT_ENTRY_SCALE: EntryScaleConfig = Object.freeze({\n mode: 'none' as const\n});\n\n\n/**\n * Default image styling configuration\n */\nexport const DEFAULT_STYLING: ImageStylingConfig = Object.freeze({\n default: Object.freeze({\n border: Object.freeze({\n width: 0,\n color: '#000000',\n radius: 0,\n style: 'solid' as const\n }),\n shadow: 'none' as ShadowPreset,\n clipPath: undefined,\n filter: Object.freeze({}),\n opacity: 1,\n cursor: 'pointer',\n outline: Object.freeze({\n width: 0,\n color: '#000000',\n style: 'solid' as const,\n offset: 0\n })\n }),\n hover: Object.freeze({\n shadow: 'none' as ShadowPreset\n }),\n focused: Object.freeze({\n shadow: 'none' as ShadowPreset\n })\n});\n\n/**\n * Default radial layout configuration\n */\nexport const DEFAULT_RADIAL_CONFIG: RadialAlgorithmConfig = Object.freeze({\n tightness: 1.0,\n});\n\n/**\n * Default wave layout configuration\n */\nexport const DEFAULT_WAVE_CONFIG: WaveAlgorithmConfig = Object.freeze({\n rows: 1,\n amplitude: 100,\n frequency: 2,\n phaseShift: 0,\n synchronization: 'offset' as const\n // Note: Image rotation along wave is now controlled via image.rotation.mode = 'tangent'\n});\n\nexport const DEFAULT_HONEYCOMB_CONFIG: HoneycombAlgorithmConfig = Object.freeze({\n spacing: 0\n});\n\n/**\n * Default responsive breakpoints for layout\n */\nexport const DEFAULT_RESPONSIVE_BREAKPOINTS: ResponsiveBreakpoints = Object.freeze({\n mobile: Object.freeze({ maxWidth: 767 }),\n tablet: Object.freeze({ maxWidth: 1199 })\n});\n\n/**\n * Default image sizing configuration\n */\nexport const DEFAULT_IMAGE_SIZING: ImageSizingConfig = Object.freeze({\n mode: 'adaptive' as const, // Default to adaptive sizing\n minSize: 50, // Adaptive mode minimum\n maxSize: 400, // Adaptive mode maximum\n variance: Object.freeze({\n min: 1.0, // No variance by default\n max: 1.0\n })\n});\n\n/**\n * Default image rotation configuration\n */\nexport const DEFAULT_IMAGE_ROTATION: ImageRotationConfig = Object.freeze({\n mode: 'none' as const,\n range: Object.freeze({\n min: -15,\n max: 15\n })\n});\n\n/**\n * Default image configuration\n */\nexport const DEFAULT_IMAGE_CONFIG: ImageConfig = Object.freeze({\n sizing: DEFAULT_IMAGE_SIZING,\n rotation: DEFAULT_IMAGE_ROTATION\n});\n\n/**\n * Default configuration object\n * Frozen to prevent accidental modifications\n */\n/**\n * Default shared loader configuration\n */\nexport const DEFAULT_SHARED_LOADER_CONFIG: SharedLoaderConfig = Object.freeze({\n validateUrls: true,\n validationTimeout: 5000,\n validationMethod: 'head' as const,\n allowedExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp']\n});\n\n/**\n * Default debug configuration\n */\nexport const DEFAULT_DEBUG_CONFIG: DebugConfig = Object.freeze({\n enabled: false,\n centers: false,\n loaders: false\n});\n\nexport const DEFAULT_IDLE_WIGGLE: IdleWiggleConfig = Object.freeze({ maxAngle: 5, speed: 2000, sync: 'random' as const });\nexport const DEFAULT_IDLE_PULSE: IdlePulseConfig = Object.freeze({ minScale: 0.95, maxScale: 1.05, speed: 2400, sync: 'random' as const });\nexport const DEFAULT_IDLE_BLINK: IdleBlinkConfig = Object.freeze({ onRatio: 0.7, speed: 3000, style: 'snap' as const });\nexport const DEFAULT_IDLE_SPIN: IdleSpinConfig = Object.freeze({ speed: 4000, direction: 'clockwise' as const });\nexport const DEFAULT_IDLE_CONFIG: IdleAnimationConfig = Object.freeze({ type: 'none' as const });\n\nexport const DEFAULT_CONFIG: ImageCloudConfig = Object.freeze({\n // Loader configuration (always an array, composite behavior is implicit)\n loaders: [] as LoaderEntry[],\n\n // Shared loader settings and debug config\n config: Object.freeze({\n loaders: DEFAULT_SHARED_LOADER_CONFIG,\n debug: DEFAULT_DEBUG_CONFIG\n }),\n\n // Image sizing and rotation configuration\n image: DEFAULT_IMAGE_CONFIG,\n\n // Pattern-based layout configuration\n layout: Object.freeze({\n algorithm: 'radial' as const,\n scaleDecay: 0, // No decay by default (0-1 for radial/spiral)\n responsive: DEFAULT_RESPONSIVE_BREAKPOINTS,\n targetCoverage: 0.6, // Target 60% of container area\n densityFactor: 1.0, // Default density\n spacing: Object.freeze({\n padding: 50 // padding from viewport edges\n })\n }),\n\n // Pattern-based animation configuration\n animation: Object.freeze({\n duration: 600, // milliseconds\n easing: Object.freeze({\n default: 'cubic-bezier(0.4, 0.0, 0.2, 1)', // smooth easing\n bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)', // bounce effect\n focus: 'cubic-bezier(0.4, 0.0, 0.2, 1)' // focus/zoom easing\n }),\n queue: Object.freeze({\n enabled: true, // When false, all images display simultaneously\n interval: 150, // ms between processing queue items (when enabled)\n }),\n entry: Object.freeze({\n start: Object.freeze({\n position: 'nearest-edge' as const, // Default to nearest edge (current behavior)\n offset: 100, // pixels beyond edge\n circular: Object.freeze({\n radius: '120%', // 120% of container diagonal\n distribution: 'even' as const\n })\n }),\n timing: Object.freeze({\n duration: 600 // ms\n }),\n easing: 'cubic-bezier(0.25, 1, 0.5, 1)', // smooth deceleration\n path: DEFAULT_PATH_CONFIG,\n rotation: DEFAULT_ENTRY_ROTATION,\n scale: DEFAULT_ENTRY_SCALE\n }),\n idle: DEFAULT_IDLE_CONFIG\n }),\n\n // Pattern-based interaction configuration\n interaction: Object.freeze({\n focus: Object.freeze({\n scalePercent: 0.8, // 80% of container height\n zIndex: 1000,\n animationDuration: undefined // Use default animation duration\n }),\n navigation: Object.freeze({\n keyboard: true,\n swipe: true,\n mouseWheel: undefined // STUB: Not implemented yet\n }),\n dragging: true\n }),\n\n // UI configuration\n ui: Object.freeze({\n showLoadingSpinner: false,\n showImageCounter: false,\n showNavButtons: false,\n showFocusOutline: false\n }),\n\n // Image styling\n styling: DEFAULT_STYLING\n});\n\n/**\n * Deep merge a single style state (border, filter, outline, etc.)\n */\nfunction deepMergeStyleState(\n base: ImageStyleState | undefined,\n override: Partial<ImageStyleState> | undefined\n): ImageStyleState {\n if (!base) return override as ImageStyleState || {};\n if (!override) return { ...base };\n\n const merged: ImageStyleState = { ...base };\n\n // Merge border\n if (override.border !== undefined) {\n merged.border = { ...base.border, ...override.border };\n }\n\n // Merge per-side borders\n if (override.borderTop !== undefined) {\n merged.borderTop = { ...base.borderTop, ...override.borderTop };\n }\n if (override.borderRight !== undefined) {\n merged.borderRight = { ...base.borderRight, ...override.borderRight };\n }\n if (override.borderBottom !== undefined) {\n merged.borderBottom = { ...base.borderBottom, ...override.borderBottom };\n }\n if (override.borderLeft !== undefined) {\n merged.borderLeft = { ...base.borderLeft, ...override.borderLeft };\n }\n\n // Merge filter\n if (override.filter !== undefined) {\n merged.filter = { ...base.filter, ...override.filter };\n }\n\n // Merge outline\n if (override.outline !== undefined) {\n merged.outline = { ...base.outline, ...override.outline };\n }\n\n // Override simple properties\n if (override.shadow !== undefined) merged.shadow = override.shadow;\n if (override.clipPath !== undefined) merged.clipPath = override.clipPath;\n if (override.opacity !== undefined) merged.opacity = override.opacity;\n if (override.cursor !== undefined) merged.cursor = override.cursor;\n if (override.className !== undefined) merged.className = override.className;\n if (override.objectFit !== undefined) merged.objectFit = override.objectFit;\n if (override.aspectRatio !== undefined) merged.aspectRatio = override.aspectRatio;\n\n // Override per-corner border radius\n if (override.borderRadiusTopLeft !== undefined) merged.borderRadiusTopLeft = override.borderRadiusTopLeft;\n if (override.borderRadiusTopRight !== undefined) merged.borderRadiusTopRight = override.borderRadiusTopRight;\n if (override.borderRadiusBottomRight !== undefined) merged.borderRadiusBottomRight = override.borderRadiusBottomRight;\n if (override.borderRadiusBottomLeft !== undefined) merged.borderRadiusBottomLeft = override.borderRadiusBottomLeft;\n\n return merged;\n}\n\n/**\n * Deep merge styling config with proper state inheritance\n * - hover inherits from default, then applies overrides\n * - focused inherits from default, then applies overrides\n */\nfunction deepMergeStyling(\n defaults: ImageStylingConfig,\n userStyling: Partial<ImageStylingConfig> | undefined\n): ImageStylingConfig {\n if (!userStyling) return { ...defaults };\n\n // First, merge the default state\n const mergedDefault = deepMergeStyleState(defaults.default, userStyling.default);\n\n // Hover inherits from merged default, then user hover overrides\n const mergedHover = deepMergeStyleState(\n deepMergeStyleState(mergedDefault, defaults.hover),\n userStyling.hover\n );\n\n // Focused inherits from merged default, then user focused overrides\n const mergedFocused = deepMergeStyleState(\n deepMergeStyleState(mergedDefault, defaults.focused),\n userStyling.focused\n );\n\n return {\n default: mergedDefault,\n hover: mergedHover,\n focused: mergedFocused\n };\n}\n\n/**\n * Deep merge utility for config objects\n * Merges user config with default config\n */\n/**\n * Deep merge image config with validation\n */\nfunction deepMergeImageConfig(\n defaults: ImageConfig,\n userImage: Partial<ImageConfig> | undefined\n): ImageConfig {\n if (!userImage) return { ...defaults };\n\n const merged: ImageConfig = { ...defaults };\n\n // Deep merge sizing config\n if (userImage.sizing !== undefined) {\n merged.sizing = {\n ...defaults.sizing,\n ...userImage.sizing\n };\n\n // Deep merge variance with validation (min: 0.25-1, max: 1-1.75)\n if (userImage.sizing.variance) {\n const userVariance = userImage.sizing.variance as ImageVarianceConfig;\n const validMin = userVariance.min !== undefined && userVariance.min >= 0.25 && userVariance.min <= 1\n ? userVariance.min\n : defaults.sizing?.variance?.min ?? 1.0;\n const validMax = userVariance.max !== undefined && userVariance.max >= 1 && userVariance.max <= 1.75\n ? userVariance.max\n : defaults.sizing?.variance?.max ?? 1.0;\n merged.sizing!.variance = { min: validMin, max: validMax };\n }\n }\n\n // Deep merge rotation config\n if (userImage.rotation !== undefined) {\n merged.rotation = {\n ...defaults.rotation,\n ...userImage.rotation\n };\n\n // Deep merge rotation range with validation\n if (userImage.rotation.range) {\n const userRange = userImage.rotation.range;\n const validMin = userRange.min !== undefined && userRange.min >= -180 && userRange.min <= 0\n ? userRange.min\n : defaults.rotation?.range?.min ?? -15;\n const validMax = userRange.max !== undefined && userRange.max >= 0 && userRange.max <= 180\n ? userRange.max\n : defaults.rotation?.range?.max ?? 15;\n merged.rotation!.range = { min: validMin, max: validMax };\n }\n }\n\n return merged;\n}\n\n/**\n * Convert legacy layout.rotation config to new image.rotation format\n * This provides backward compatibility with the old API\n */\nfunction convertLegacyRotationConfig(userConfig: DeepPartial<ImageCloudConfig>): Partial<ImageConfig> | undefined {\n const legacyRotation = (userConfig.layout as any)?.rotation;\n if (!legacyRotation) return undefined;\n\n // Legacy format: { enabled: boolean, range: { min, max } }\n if ('enabled' in legacyRotation) {\n return {\n rotation: {\n mode: legacyRotation.enabled ? 'random' : 'none',\n range: legacyRotation.range\n }\n };\n }\n\n return undefined;\n}\n\n/**\n * Convert legacy layout.sizing.variance config to new image.sizing.variance format\n */\nfunction convertLegacyVarianceConfig(userConfig: DeepPartial<ImageCloudConfig>): Partial<ImageConfig> | undefined {\n const legacyVariance = (userConfig.layout as any)?.sizing?.variance;\n if (!legacyVariance) return undefined;\n\n return {\n sizing: {\n mode: 'adaptive', // Legacy variance config implies adaptive mode\n variance: legacyVariance\n }\n };\n}\n\nexport function mergeConfig(\n userConfig: ImageCloudOptions = {}\n): ImageCloudConfig {\n // Convert legacy configs to new format\n const legacyRotation = convertLegacyRotationConfig(userConfig as any);\n const legacyVariance = convertLegacyVarianceConfig(userConfig as any);\n\n // Combine user image config with converted legacy configs\n // User's explicit image config takes precedence over legacy conversions\n let combinedImageConfig: Partial<ImageConfig> | undefined = userConfig.image as Partial<ImageConfig> | undefined;\n if (legacyRotation || legacyVariance) {\n combinedImageConfig = {\n ...(legacyVariance || {}),\n ...(legacyRotation || {}),\n ...combinedImageConfig\n };\n // Deep merge the rotation config if both exist\n if (combinedImageConfig.rotation && legacyRotation?.rotation && userConfig.image?.rotation) {\n combinedImageConfig.rotation = {\n ...legacyRotation.rotation,\n ...(userConfig.image as any).rotation\n };\n }\n }\n\n // Build loaders array: images shorthand prepended, then explicit loaders\n const loaders = [...(userConfig.loaders ?? [])];\n if (userConfig.images && userConfig.images.length > 0) {\n loaders.unshift({\n static: {\n sources: [{ urls: userConfig.images }]\n }\n });\n }\n\n // Merge shared loader config\n const sharedLoaderConfig: SharedLoaderConfig = {\n ...DEFAULT_SHARED_LOADER_CONFIG,\n ...(userConfig.config?.loaders ?? {})\n };\n\n const mergedConfig: ConfigSection = {\n loaders: sharedLoaderConfig\n };\n\n const merged: ImageCloudConfig = {\n loaders,\n config: mergedConfig,\n image: deepMergeImageConfig(DEFAULT_IMAGE_CONFIG, combinedImageConfig),\n layout: { ...DEFAULT_CONFIG.layout },\n animation: { ...DEFAULT_CONFIG.animation },\n interaction: { ...DEFAULT_CONFIG.interaction },\n ui: { ...DEFAULT_CONFIG.ui },\n styling: deepMergeStyling(DEFAULT_STYLING, userConfig.styling as Partial<ImageStylingConfig> | undefined)\n };\n\n // Deep merge layout config\n if (userConfig.layout) {\n merged.layout = {\n ...DEFAULT_CONFIG.layout,\n ...userConfig.layout\n } as any;\n\n // Deep merge responsive breakpoints config\n if (userConfig.layout.responsive) {\n merged.layout.responsive = {\n ...DEFAULT_CONFIG.layout.responsive!,\n mobile: userConfig.layout.responsive.mobile\n ? { ...DEFAULT_CONFIG.layout.responsive!.mobile, ...userConfig.layout.responsive.mobile }\n : DEFAULT_CONFIG.layout.responsive!.mobile,\n tablet: userConfig.layout.responsive.tablet\n ? { ...DEFAULT_CONFIG.layout.responsive!.tablet, ...userConfig.layout.responsive.tablet }\n : DEFAULT_CONFIG.layout.responsive!.tablet\n };\n }\n\n // Deep merge spacing config\n if (userConfig.layout.spacing) {\n merged.layout.spacing = {\n ...DEFAULT_CONFIG.layout.spacing,\n ...userConfig.layout.spacing\n };\n }\n }\n\n // Deep merge animation config\n if (userConfig.animation) {\n merged.animation = {\n ...DEFAULT_CONFIG.animation,\n ...userConfig.animation\n } as any;\n\n // Deep merge easing config\n if (userConfig.animation.easing) {\n merged.animation.easing = {\n ...DEFAULT_CONFIG.animation.easing,\n ...userConfig.animation.easing\n };\n }\n\n // Deep merge queue config\n if (userConfig.animation.queue) {\n merged.animation.queue = {\n ...DEFAULT_CONFIG.animation.queue,\n ...userConfig.animation.queue\n };\n }\n\n // Deep merge entry animation config\n if (userConfig.animation.entry) {\n merged.animation.entry = {\n ...DEFAULT_CONFIG.animation.entry!,\n ...userConfig.animation.entry,\n start: userConfig.animation.entry.start\n ? {\n ...DEFAULT_CONFIG.animation.entry!.start,\n ...userConfig.animation.entry.start,\n circular: userConfig.animation.entry.start.circular\n ? { ...DEFAULT_CONFIG.animation.entry!.start.circular, ...userConfig.animation.entry.start.circular }\n : DEFAULT_CONFIG.animation.entry!.start.circular\n }\n : DEFAULT_CONFIG.animation.entry!.start,\n timing: userConfig.animation.entry.timing\n ? { ...DEFAULT_CONFIG.animation.entry!.timing, ...userConfig.animation.entry.timing }\n : DEFAULT_CONFIG.animation.entry!.timing,\n path: userConfig.animation.entry.path\n ? { ...DEFAULT_PATH_CONFIG, ...userConfig.animation.entry.path }\n : DEFAULT_CONFIG.animation.entry!.path,\n rotation: userConfig.animation.entry.rotation\n ? { ...DEFAULT_ENTRY_ROTATION, ...userConfig.animation.entry.rotation }\n : DEFAULT_CONFIG.animation.entry!.rotation,\n scale: userConfig.animation.entry.scale\n ? { ...DEFAULT_ENTRY_SCALE, ...userConfig.animation.entry.scale }\n : DEFAULT_CONFIG.animation.entry!.scale\n };\n }\n\n // Deep merge idle animation config\n if (userConfig.animation.idle) {\n merged.animation.idle = {\n ...DEFAULT_IDLE_CONFIG,\n ...userConfig.animation.idle\n };\n }\n }\n\n // Deep merge interaction config\n if (userConfig.interaction) {\n merged.interaction = {\n ...DEFAULT_CONFIG.interaction,\n ...userConfig.interaction\n } as any;\n\n // Deep merge focus config\n if (userConfig.interaction.focus) {\n merged.interaction.focus = {\n ...DEFAULT_CONFIG.interaction.focus,\n ...userConfig.interaction.focus\n };\n }\n\n // Deep merge navigation config\n if (userConfig.interaction.navigation) {\n merged.interaction.navigation = {\n ...DEFAULT_CONFIG.interaction.navigation,\n ...userConfig.interaction.navigation\n };\n }\n\n }\n\n // Merge ui config (with backwards compat for old rendering.ui)\n const legacyUi = (userConfig as any).rendering?.ui;\n if (legacyUi) {\n console.warn('[ImageCloud] rendering.ui is deprecated. Use top-level ui instead.');\n }\n merged.ui = {\n ...DEFAULT_CONFIG.ui,\n ...legacyUi,\n ...userConfig.ui\n };\n\n // Merge debug config\n merged.config.debug = {\n ...DEFAULT_DEBUG_CONFIG,\n ...(userConfig.config?.debug ?? {})\n };\n\n // Honeycomb forces default/hover clip path to hexagon height-relative for edge-to-edge tiling\n if (merged.layout.algorithm === 'honeycomb' && merged.styling) {\n const honeycombClip = { shape: 'hexagon' as const, mode: 'height-relative' as const };\n merged.styling = {\n ...merged.styling,\n default: { ...merged.styling.default, clipPath: honeycombClip },\n hover: { ...merged.styling.hover, clipPath: honeycombClip },\n // focused: untouched — user config respected\n };\n }\n\n return merged;\n}\n\n/**\n * Resolve bounce path config from preset and overrides\n */\nexport function resolveBounceConfig(\n preset?: BouncePreset,\n overrides?: Partial<BouncePathConfig>\n): BouncePathConfig {\n const base = preset ? BOUNCE_PRESETS[preset] : BOUNCE_PRESETS.playful;\n return { ...base, ...overrides };\n}\n\n/**\n * Resolve elastic path config from preset and overrides\n */\nexport function resolveElasticConfig(\n preset?: ElasticPreset,\n overrides?: Partial<ElasticPathConfig>\n): ElasticPathConfig {\n const base = preset ? ELASTIC_PRESETS[preset] : ELASTIC_PRESETS.gentle;\n return { ...base, ...overrides };\n}\n\n/**\n * Resolve wave path config from preset and overrides\n */\nexport function resolveWavePathConfig(\n preset?: WavePathPreset,\n overrides?: Partial<WavePathConfig>\n): WavePathConfig {\n const base = preset ? WAVE_PATH_PRESETS[preset] : WAVE_PATH_PRESETS.gentle;\n return { ...base, ...overrides };\n}\n\n/**\n * Debug logger\n */\nexport function debugLog(config: ImageCloudConfig, ...args: unknown[]): void {\n if (config.config.debug?.enabled && typeof console !== 'undefined') {\n console.log(...args);\n }\n}","/**\n * AnimationEngine.ts\n * Handles smooth animations with easing for the image cloud\n *\n * Public API:\n * - animateTransform(element, properties, duration, easing)\n * - animateTransformCancellable(element, from, to, duration, easing) - Web Animations API\n * - cancelAnimation(handle, commitStyle) - Cancel and optionally keep current position\n * - getCurrentTransform(element) - Get current transform state mid-animation\n * - hasActiveAnimation(element) - Check if element has active animation\n * - resetTransform(element, originalState)\n * - clearTransition(element)\n * - wait(ms)\n */\n\nimport type { AnimationConfig, TransformParams, ImageLayout, AnimationHandle, AnimationSnapshot } from '../config/types';\n\nexport class AnimationEngine {\n private config: AnimationConfig;\n private activeAnimations: Map<HTMLElement, AnimationHandle> = new Map();\n private animationIdCounter = 0;\n\n constructor(config: AnimationConfig) {\n this.config = config;\n }\n\n /**\n * Build transform string from transform params\n * Always starts with centering transform to match image positioning system\n */\n private buildTransformString(params: TransformParams): string {\n const transforms: string[] = ['translate(-50%, -50%)'];\n\n if (params.x !== undefined || params.y !== undefined) {\n const x = params.x ?? 0;\n const y = params.y ?? 0;\n transforms.push(`translate(${x}px, ${y}px)`);\n }\n\n if (params.rotation !== undefined) {\n transforms.push(`rotate(${params.rotation}deg)`);\n }\n\n if (params.scale !== undefined) {\n transforms.push(`scale(${params.scale})`);\n }\n\n return transforms.join(' ');\n }\n\n /**\n * Start a cancellable transform animation using Web Animations API\n * @param element - The element to animate\n * @param from - Starting transform state\n * @param to - Ending transform state\n * @param duration - Animation duration in ms (optional)\n * @param easing - CSS easing function (optional)\n * @returns AnimationHandle that can be used to cancel or query the animation\n */\n animateTransformCancellable(\n element: HTMLElement,\n from: TransformParams,\n to: TransformParams,\n duration: number | null = null,\n easing: string | null = null\n ): AnimationHandle {\n // Cancel any existing animation on this element\n this.cancelAllAnimations(element);\n\n const animDuration = duration ?? this.config.duration;\n const animEasing = easing ?? this.config.easing.default;\n\n const fromTransform = this.buildTransformString(from);\n const toTransform = this.buildTransformString(to);\n\n // Clear any CSS transitions to avoid conflicts\n element.style.transition = 'none';\n\n // Create Web Animation\n const animation = element.animate(\n [\n { transform: fromTransform },\n { transform: toTransform }\n ],\n {\n duration: animDuration,\n easing: animEasing,\n fill: 'forwards' // Keep final state after animation\n }\n );\n\n const handle: AnimationHandle = {\n id: `anim-${++this.animationIdCounter}`,\n element,\n animation,\n fromState: from,\n toState: to,\n startTime: performance.now(),\n duration: animDuration\n };\n\n this.activeAnimations.set(element, handle);\n\n // Clean up when animation finishes (normally or cancelled)\n animation.finished\n .then(() => {\n // Apply final transform as inline style for consistency\n element.style.transform = toTransform;\n this.activeAnimations.delete(element);\n })\n .catch(() => {\n // Animation was cancelled - cleanup handled by cancelAnimation\n this.activeAnimations.delete(element);\n });\n\n return handle;\n }\n\n /**\n * Cancel an active animation\n * @param handle - The animation handle to cancel\n * @param commitStyle - If true, keeps current position; if false, no style change\n * @returns Snapshot of where the animation was when cancelled\n */\n cancelAnimation(handle: AnimationHandle, commitStyle: boolean = true): AnimationSnapshot {\n const snapshot = this.getCurrentTransform(handle.element);\n\n // Cancel the Web Animation\n handle.animation.cancel();\n\n if (commitStyle) {\n // Apply current position as inline style\n const currentTransform = this.buildTransformString({\n x: snapshot.x,\n y: snapshot.y,\n rotation: snapshot.rotation,\n scale: snapshot.scale\n });\n handle.element.style.transform = currentTransform;\n }\n\n this.activeAnimations.delete(handle.element);\n\n return snapshot;\n }\n\n /**\n * Cancel all animations on an element\n * Uses Web Animations API to find and cancel ALL animations, not just tracked ones\n * @param element - The element to cancel animations for\n */\n cancelAllAnimations(element: HTMLElement): void {\n // Cancel tracked animation\n const handle = this.activeAnimations.get(element);\n if (handle) {\n this.cancelAnimation(handle, false);\n }\n\n // Also cancel any other animations on the element (e.g., completed animations with fill: 'forwards')\n const allAnimations = element.getAnimations();\n for (const anim of allAnimations) {\n anim.cancel();\n }\n }\n\n /**\n * Get current transform state of an element (works mid-animation)\n * Uses DOMMatrix to parse the computed transform\n * @param element - The element to query\n * @returns Current transform snapshot\n */\n getCurrentTransform(element: HTMLElement): AnimationSnapshot {\n const computed = getComputedStyle(element);\n const transformStr = computed.transform;\n\n if (transformStr === 'none' || !transformStr) {\n return { x: 0, y: 0, rotation: 0, scale: 1 };\n }\n\n const matrix = new DOMMatrix(transformStr);\n\n // Extract scale from matrix (for uniform scale)\n const scale = Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b);\n\n // Extract rotation from matrix (in degrees)\n const rotation = Math.atan2(matrix.b, matrix.a) * (180 / Math.PI);\n\n // Extract translation\n // Note: matrix.e and matrix.f include ALL translations including the centering offset\n // The centering is translate(-50%, -50%) which depends on element dimensions\n // Since our transform chain is: translate(-50%, -50%) translate(x, y) rotate scale\n // The additional x,y offset we applied is already baked into e,f along with centering\n // For cross-animation, we need the relative offset, not absolute position\n // We'll return e,f directly and let ZoomEngine handle the interpretation\n const x = matrix.e;\n const y = matrix.f;\n\n return { x, y, rotation, scale };\n }\n\n /**\n * Check if an element has an active animation\n * @param element - The element to check\n * @returns True if animation is in progress\n */\n hasActiveAnimation(element: HTMLElement): boolean {\n return this.activeAnimations.has(element);\n }\n\n /**\n * Get animation handle for an element if it exists\n * @param element - The element to query\n * @returns AnimationHandle or undefined\n */\n getAnimationHandle(element: HTMLElement): AnimationHandle | undefined {\n return this.activeAnimations.get(element);\n }\n\n /**\n * Animate element transform with smooth easing (CSS transitions - legacy method)\n * @param element - The element to animate\n * @param properties - Transform properties {x, y, rotation, scale}\n * @param duration - Animation duration in ms (optional)\n * @param easing - CSS easing function (optional)\n * @returns Promise that resolves when animation completes\n */\n animateTransform(\n element: HTMLElement,\n properties: TransformParams,\n duration: number | null = null,\n easing: string | null = null\n ): Promise<void> {\n return new Promise((resolve) => {\n const animDuration = duration ?? this.config.duration;\n const animEasing = easing ?? this.config.easing.default;\n\n // Apply transition\n element.style.transition = `transform ${animDuration}ms ${animEasing}, box-shadow ${animDuration}ms ${animEasing}`;\n\n // Apply transform using shared helper\n element.style.transform = this.buildTransformString(properties);\n\n // Resolve promise when animation completes\n setTimeout(() => {\n resolve();\n }, animDuration);\n });\n }\n\n /**\n * Reset element to its original transform\n * @param element - The element to reset\n * @param originalState - Original transform state {x, y, rotation, scale}\n * @returns Promise that resolves when animation completes\n */\n resetTransform(element: HTMLElement, originalState: TransformParams | ImageLayout): Promise<void> {\n return this.animateTransform(element, originalState);\n }\n\n /**\n * Remove transition styles from element\n * @param element - The element to clear\n */\n clearTransition(element: HTMLElement): void {\n element.style.transition = '';\n }\n\n /**\n * Utility: Wait for a specified duration\n * @param ms - Milliseconds to wait\n * @returns Promise that resolves after the specified duration\n */\n wait(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}","/**\n * PathAnimator.ts\n * Provides path calculation functions for bounce, elastic, and wave entry animations\n *\n * These animations require JavaScript-driven frame updates because they involve\n * complex mathematical curves that can't be expressed with CSS transitions alone.\n *\n * Public API:\n * - animatePath(element, startPos, endPos, pathConfig, duration, onComplete)\n * - calculateBouncePosition(t, start, end, config)\n * - calculateElasticPosition(t, start, end, config)\n * - calculateWavePosition(t, start, end, config)\n */\n\nimport type {\n BouncePathConfig,\n ElasticPathConfig,\n WavePathConfig,\n EntryPathConfig,\n EntryPathType,\n EntryRotationConfig,\n EntryScaleConfig\n} from '../config/types';\nimport {\n resolveBounceConfig,\n resolveElasticConfig,\n resolveWavePathConfig\n} from '../config/defaults';\n\nexport interface Point {\n x: number;\n y: number;\n}\n\nexport interface PathAnimationOptions {\n element: HTMLElement;\n startPosition: Point;\n endPosition: Point;\n pathConfig: EntryPathConfig;\n duration: number;\n imageWidth: number;\n imageHeight: number;\n rotation: number; // Final rotation\n scale: number; // Final scale\n onComplete?: () => void;\n // Rotation animation options\n rotationConfig?: EntryRotationConfig;\n startRotation?: number; // Starting rotation (if different from final)\n // Scale animation options\n scaleConfig?: EntryScaleConfig;\n startScale?: number; // Starting scale (if different from final)\n}\n\n/**\n * Linear interpolation helper\n */\nfunction lerp(start: number, end: number, t: number): number {\n return start + (end - start) * t;\n}\n\n/**\n * Calculate position along a bounce path\n * Overshoot and settle animation\n */\nexport function calculateBouncePosition(\n t: number,\n start: Point,\n end: Point,\n config: BouncePathConfig\n): Point {\n const { overshoot, bounces, decayRatio } = config;\n\n // Direction vector from start to end\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n\n // Calculate keyframe timings based on number of bounces\n const keyframes = generateBounceKeyframes(bounces, decayRatio);\n\n // Find current segment\n let progress = 0;\n let segmentStart = 0;\n let segmentEnd = 1;\n let segmentOvershoot = overshoot;\n let isOvershootPhase = false;\n\n for (let i = 0; i < keyframes.length; i++) {\n if (t <= keyframes[i].time) {\n segmentStart = i === 0 ? 0 : keyframes[i - 1].time;\n segmentEnd = keyframes[i].time;\n segmentOvershoot = keyframes[i].overshoot;\n isOvershootPhase = keyframes[i].isOvershoot;\n break;\n }\n }\n\n // Calculate progress within current segment\n const segmentT = (t - segmentStart) / (segmentEnd - segmentStart);\n\n if (isOvershootPhase) {\n // Ease out into overshoot\n progress = 1 + segmentOvershoot * easeOutQuad(segmentT);\n } else if (segmentStart === 0) {\n // Initial travel to target - ease out\n progress = easeOutQuad(segmentT);\n } else {\n // Settling back from overshoot\n const prevOvershoot = keyframes.find((k, i) =>\n k.time > segmentStart && i > 0 && keyframes[i - 1].isOvershoot\n );\n const fromProgress = 1 + (prevOvershoot?.overshoot || segmentOvershoot);\n progress = lerp(fromProgress, 1, easeOutQuad(segmentT));\n }\n\n return {\n x: start.x + dx * progress,\n y: start.y + dy * progress\n };\n}\n\n/**\n * Generate keyframe timings for bounce animation\n */\nfunction generateBounceKeyframes(\n bounces: number,\n decayRatio: number\n): Array<{ time: number; overshoot: number; isOvershoot: boolean }> {\n const keyframes: Array<{ time: number; overshoot: number; isOvershoot: boolean }> = [];\n\n // Initial travel takes 60% of time\n let currentTime = 0.6;\n keyframes.push({ time: currentTime, overshoot: 0, isOvershoot: false });\n\n let currentOvershoot = 0.15; // Initial overshoot amount\n const remainingTime = 0.4;\n const bounceTime = remainingTime / (bounces * 2);\n\n for (let i = 0; i < bounces; i++) {\n // Overshoot phase\n currentTime += bounceTime;\n keyframes.push({ time: currentTime, overshoot: currentOvershoot, isOvershoot: true });\n\n // Settle phase\n currentTime += bounceTime;\n keyframes.push({ time: currentTime, overshoot: currentOvershoot * decayRatio, isOvershoot: false });\n\n currentOvershoot *= decayRatio;\n }\n\n // Final settle\n keyframes.push({ time: 1, overshoot: 0, isOvershoot: false });\n\n return keyframes;\n}\n\n/**\n * Calculate position along an elastic path\n * Spring-like oscillation animation\n */\nexport function calculateElasticPosition(\n t: number,\n start: Point,\n end: Point,\n config: ElasticPathConfig\n): Point {\n const { stiffness, damping, mass, oscillations } = config;\n\n // Direction vector from start to end\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n\n // Normalized spring physics\n // Natural frequency based on stiffness and mass\n const omega = Math.sqrt(stiffness / mass);\n\n // Damping ratio (normalized)\n const zeta = damping / (2 * Math.sqrt(stiffness * mass));\n\n // Progress using damped harmonic oscillator\n let progress: number;\n\n if (zeta < 1) {\n // Underdamped - oscillates\n const dampedFreq = omega * Math.sqrt(1 - zeta * zeta);\n const envelope = Math.exp(-zeta * omega * t * 3);\n const oscillation = Math.cos(dampedFreq * t * oscillations * Math.PI);\n progress = 1 - envelope * oscillation;\n } else {\n // Critically damped or overdamped - no oscillation\n progress = 1 - Math.exp(-omega * t * 3);\n }\n\n // Clamp progress\n progress = Math.max(0, Math.min(progress, 1.3)); // Allow slight overshoot\n\n return {\n x: start.x + dx * progress,\n y: start.y + dy * progress\n };\n}\n\n/**\n * Calculate position along a wave path\n * Sinusoidal path from start to end\n */\nexport function calculateWavePosition(\n t: number,\n start: Point,\n end: Point,\n config: WavePathConfig\n): Point {\n const { amplitude, frequency, decay, decayRate, phase } = config;\n\n // Direction vector from start to end\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n const length = Math.sqrt(dx * dx + dy * dy);\n\n // Perpendicular vector (normalized)\n const perpX = length > 0 ? -dy / length : 0;\n const perpY = length > 0 ? dx / length : 1;\n\n // Wave calculation\n const wavePhase = frequency * Math.PI * 2 * t + phase;\n const decayFactor = decay ? Math.pow(1 - t, decayRate) : 1;\n const waveOffset = amplitude * Math.sin(wavePhase) * decayFactor;\n\n // Ease out for smooth arrival\n const progressT = easeOutCubic(t);\n\n // Linear interpolation + wave offset\n return {\n x: lerp(start.x, end.x, progressT) + waveOffset * perpX,\n y: lerp(start.y, end.y, progressT) + waveOffset * perpY\n };\n}\n\n/**\n * Easing functions\n */\nfunction easeOutQuad(t: number): number {\n return 1 - (1 - t) * (1 - t);\n}\n\nfunction easeOutCubic(t: number): number {\n return 1 - Math.pow(1 - t, 3);\n}\n\n/**\n * Calculate wobble rotation for a given animation progress\n */\nfunction calculateWobbleRotation(\n progress: number,\n finalRotation: number,\n wobbleConfig: { amplitude: number; frequency: number; decay: boolean }\n): number {\n const { amplitude, frequency, decay } = wobbleConfig;\n\n // Oscillation using sine wave\n const oscillation = Math.sin(progress * frequency * Math.PI * 2);\n\n // Apply decay if enabled (stronger decay toward end)\n const decayFactor = decay ? Math.pow(1 - progress, 2) : 1;\n\n // Calculate wobble offset\n const wobbleOffset = amplitude * oscillation * decayFactor;\n\n return finalRotation + wobbleOffset;\n}\n\n/**\n * Calculate pop scale for a given animation progress\n */\nfunction calculatePopScale(\n progress: number,\n finalScale: number,\n popConfig: { overshoot: number; bounces: number }\n): number {\n const { overshoot, bounces } = popConfig;\n\n // Generate keyframes for bounce effect\n const keyframes: Array<{ time: number; scale: number }> = [];\n\n // Reach overshoot at 50% of animation\n keyframes.push({ time: 0.5, scale: overshoot });\n\n // Add bounces\n let currentOvershoot = overshoot;\n const bounceDecay = 0.5;\n const remainingTime = 0.5;\n const bounceTime = remainingTime / (bounces * 2);\n\n let currentTime = 0.5;\n for (let i = 0; i < bounces; i++) {\n const undershoot = 1 - (currentOvershoot - 1) * bounceDecay;\n currentTime += bounceTime;\n keyframes.push({ time: currentTime, scale: undershoot });\n\n currentOvershoot = 1 + (currentOvershoot - 1) * bounceDecay * bounceDecay;\n currentTime += bounceTime;\n if (i < bounces - 1) {\n keyframes.push({ time: currentTime, scale: currentOvershoot });\n }\n }\n\n keyframes.push({ time: 1, scale: 1 });\n\n // Find current segment\n let currentScale = 1;\n for (let i = 0; i < keyframes.length; i++) {\n if (progress <= keyframes[i].time) {\n const prevTime = i === 0 ? 0 : keyframes[i - 1].time;\n const prevScale = i === 0 ? 1 : keyframes[i - 1].scale;\n const segmentProgress = (progress - prevTime) / (keyframes[i].time - prevTime);\n const easedProgress = easeOutQuad(segmentProgress);\n currentScale = prevScale + (keyframes[i].scale - prevScale) * easedProgress;\n break;\n }\n }\n\n return currentScale * finalScale;\n}\n\n/**\n * Animate an element along a path using requestAnimationFrame\n */\nexport function animatePath(options: PathAnimationOptions): void {\n const {\n element,\n startPosition,\n endPosition,\n pathConfig,\n duration,\n imageWidth,\n imageHeight,\n rotation: finalRotation,\n scale: finalScale,\n onComplete,\n rotationConfig,\n startRotation,\n scaleConfig,\n startScale\n } = options;\n\n const pathType = pathConfig.type;\n\n // Determine if we need to animate rotation\n const animateRotation = startRotation !== undefined && startRotation !== finalRotation;\n const isWobbleMode = rotationConfig?.mode === 'wobble';\n const wobbleConfig = rotationConfig?.wobble || { amplitude: 15, frequency: 3, decay: true };\n const needsRotationAnimation = animateRotation || isWobbleMode;\n\n // Determine if we need to animate scale\n const animateScale = startScale !== undefined && startScale !== finalScale;\n const isPopMode = scaleConfig?.mode === 'pop';\n const popConfig = scaleConfig?.pop || { overshoot: 1.2, bounces: 1 };\n const needsScaleAnimation = animateScale || isPopMode;\n\n // For linear/arc paths WITHOUT rotation or scale animation, use CSS transitions (handled elsewhere)\n if ((pathType === 'linear' || pathType === 'arc') && !needsRotationAnimation && !needsScaleAnimation) {\n if (onComplete) onComplete();\n return;\n }\n\n const startTime = performance.now();\n\n // Build center offset for transform\n const centerOffsetX = -imageWidth / 2;\n const centerOffsetY = -imageHeight / 2;\n\n function tick(currentTime: number): void {\n const elapsed = currentTime - startTime;\n const t = Math.min(elapsed / duration, 1);\n\n // Calculate position based on path type\n let position: Point;\n\n switch (pathType) {\n case 'bounce': {\n const config = resolveBounceConfig(\n pathConfig.bouncePreset,\n pathConfig.bounce\n );\n position = calculateBouncePosition(t, startPosition, endPosition, config);\n break;\n }\n case 'elastic': {\n const config = resolveElasticConfig(\n pathConfig.elasticPreset,\n pathConfig.elastic\n );\n position = calculateElasticPosition(t, startPosition, endPosition, config);\n break;\n }\n case 'wave': {\n const config = resolveWavePathConfig(\n pathConfig.wavePreset,\n pathConfig.wave\n );\n position = calculateWavePosition(t, startPosition, endPosition, config);\n break;\n }\n default:\n position = {\n x: lerp(startPosition.x, endPosition.x, t),\n y: lerp(startPosition.y, endPosition.y, t)\n };\n }\n\n // Calculate translate offset from final position\n const translateX = position.x - endPosition.x;\n const translateY = position.y - endPosition.y;\n\n // Calculate current rotation\n let currentRotation: number;\n if (isWobbleMode) {\n currentRotation = calculateWobbleRotation(t, finalRotation, wobbleConfig);\n } else if (animateRotation) {\n currentRotation = lerp(startRotation!, finalRotation, t);\n } else {\n currentRotation = finalRotation;\n }\n\n // Calculate current scale\n let currentScale: number;\n if (isPopMode) {\n currentScale = calculatePopScale(t, finalScale, popConfig);\n } else if (animateScale) {\n currentScale = lerp(startScale!, finalScale, t);\n } else {\n currentScale = finalScale;\n }\n\n // Apply transform\n element.style.transform =\n `translate(${centerOffsetX}px, ${centerOffsetY}px) ` +\n `translate(${translateX}px, ${translateY}px) ` +\n `rotate(${currentRotation}deg) scale(${currentScale})`;\n\n if (t < 1) {\n requestAnimationFrame(tick);\n } else {\n // Ensure we end exactly at the final position, rotation, and scale\n element.style.transform =\n `translate(${centerOffsetX}px, ${centerOffsetY}px) ` +\n `rotate(${finalRotation}deg) scale(${finalScale})`;\n if (onComplete) onComplete();\n }\n }\n\n requestAnimationFrame(tick);\n}\n\n/**\n * Check if a path type requires JavaScript animation (vs CSS transitions)\n */\nexport function requiresJSAnimation(pathType: EntryPathType): boolean {\n return pathType === 'bounce' || pathType === 'elastic' || pathType === 'wave';\n}\n\n","/**\n * EntryAnimationEngine.ts\n * Calculates starting positions for entry animations based on configuration\n *\n * Public API:\n * - calculateStartPosition(finalPosition, imageSize, containerBounds, imageIndex, totalImages)\n * - getAnimationParams(imageIndex)\n */\n\nimport type {\n EntryAnimationConfig,\n EntryStartPosition,\n ContainerBounds,\n LayoutAlgorithm,\n EntryPathConfig,\n EntryPathType,\n EntryRotationConfig,\n EntryRotationMode,\n EntryScaleConfig,\n EntryScaleMode\n} from '../config/types';\nimport { DEFAULT_PATH_CONFIG, DEFAULT_ENTRY_ROTATION, DEFAULT_ENTRY_SCALE } from '../config/defaults';\nimport { requiresJSAnimation } from './PathAnimator';\n\n/** Layout-aware default start positions */\nconst LAYOUT_ENTRY_DEFAULTS: Record<LayoutAlgorithm, EntryStartPosition> = {\n radial: 'center',\n spiral: 'center',\n grid: 'top',\n cluster: 'nearest-edge',\n random: 'nearest-edge',\n wave: 'left',\n honeycomb: 'center'\n};\n\nexport interface StartPosition {\n x: number;\n y: number;\n useScale?: boolean; // For center position, start with scale 0\n}\n\nexport interface AnimationParams {\n startTransform: string;\n duration: number;\n delay: number;\n easing: string;\n}\n\nexport class EntryAnimationEngine {\n private config: EntryAnimationConfig;\n private layoutAlgorithm: LayoutAlgorithm;\n private resolvedStartPosition: EntryStartPosition;\n private pathConfig: EntryPathConfig;\n private rotationConfig: EntryRotationConfig;\n private scaleConfig: EntryScaleConfig;\n\n constructor(config: EntryAnimationConfig, layoutAlgorithm: LayoutAlgorithm) {\n this.config = config;\n this.layoutAlgorithm = layoutAlgorithm;\n\n // Resolve the start position, using layout-aware defaults if not specified\n this.resolvedStartPosition = this.resolveStartPosition();\n\n // Resolve path config\n this.pathConfig = config.path || DEFAULT_PATH_CONFIG;\n\n // Resolve rotation config\n this.rotationConfig = config.rotation || DEFAULT_ENTRY_ROTATION;\n\n // Resolve scale config\n this.scaleConfig = config.scale || DEFAULT_ENTRY_SCALE;\n }\n\n /**\n * Get the effective start position, considering layout-aware defaults\n */\n private resolveStartPosition(): EntryStartPosition {\n // If explicitly configured, use that\n if (this.config.start.position) {\n return this.config.start.position;\n }\n // Otherwise use layout-aware default\n return LAYOUT_ENTRY_DEFAULTS[this.layoutAlgorithm] || 'nearest-edge';\n }\n\n /**\n * Calculate the starting position for an image's entry animation\n */\n calculateStartPosition(\n finalPosition: { x: number; y: number },\n imageSize: { width: number; height: number },\n containerBounds: ContainerBounds,\n imageIndex: number,\n totalImages: number\n ): StartPosition {\n const position = this.resolvedStartPosition;\n const offset = this.config.start.offset ?? 100;\n\n switch (position) {\n case 'nearest-edge':\n return this.calculateNearestEdge(finalPosition, imageSize, containerBounds, offset);\n\n case 'top':\n return this.calculateEdgePosition('top', finalPosition, imageSize, containerBounds, offset);\n\n case 'bottom':\n return this.calculateEdgePosition('bottom', finalPosition, imageSize, containerBounds, offset);\n\n case 'left':\n return this.calculateEdgePosition('left', finalPosition, imageSize, containerBounds, offset);\n\n case 'right':\n return this.calculateEdgePosition('right', finalPosition, imageSize, containerBounds, offset);\n\n case 'center':\n return this.calculateCenterPosition(containerBounds, finalPosition, imageSize);\n\n case 'random-edge':\n return this.calculateRandomEdge(finalPosition, imageSize, containerBounds, offset);\n\n case 'circular':\n return this.calculateCircularPosition(\n finalPosition,\n imageSize,\n containerBounds,\n imageIndex,\n totalImages\n );\n\n default:\n return this.calculateNearestEdge(finalPosition, imageSize, containerBounds, offset);\n }\n }\n\n /**\n * Calculate start position from the nearest edge (current default behavior)\n */\n private calculateNearestEdge(\n finalPosition: { x: number; y: number },\n imageSize: { width: number; height: number },\n containerBounds: ContainerBounds,\n offset: number\n ): StartPosition {\n // finalPosition now stores center position directly\n const centerX = finalPosition.x;\n const centerY = finalPosition.y;\n\n const distLeft = centerX;\n const distRight = containerBounds.width - centerX;\n const distTop = centerY;\n const distBottom = containerBounds.height - centerY;\n\n const minDist = Math.min(distLeft, distRight, distTop, distBottom);\n\n let startX = finalPosition.x;\n let startY = finalPosition.y;\n\n if (minDist === distLeft) {\n // Start from left edge\n startX = -(imageSize.width + offset);\n } else if (minDist === distRight) {\n // Start from right edge\n startX = containerBounds.width + offset;\n } else if (minDist === distTop) {\n // Start from top edge\n startY = -(imageSize.height + offset);\n } else {\n // Start from bottom edge\n startY = containerBounds.height + offset;\n }\n\n return { x: startX, y: startY };\n }\n\n /**\n * Calculate start position from a specific edge\n */\n private calculateEdgePosition(\n edge: 'top' | 'bottom' | 'left' | 'right',\n finalPosition: { x: number; y: number },\n imageSize: { width: number; height: number },\n containerBounds: ContainerBounds,\n offset: number\n ): StartPosition {\n let startX = finalPosition.x;\n let startY = finalPosition.y;\n\n switch (edge) {\n case 'top':\n startY = -(imageSize.height + offset);\n break;\n case 'bottom':\n startY = containerBounds.height + offset;\n break;\n case 'left':\n startX = -(imageSize.width + offset);\n break;\n case 'right':\n startX = containerBounds.width + offset;\n break;\n }\n\n return { x: startX, y: startY };\n }\n\n /**\n * Calculate start position from center with scale animation\n */\n private calculateCenterPosition(\n containerBounds: ContainerBounds,\n _finalPosition: { x: number; y: number },\n _imageSize: { width: number; height: number }\n ): StartPosition {\n // Start at center of container (using center position, not top-left)\n const centerX = containerBounds.width / 2;\n const centerY = containerBounds.height / 2;\n\n return {\n x: centerX,\n y: centerY,\n useScale: true // Signal to use scale animation from 0\n };\n }\n\n /**\n * Calculate start position from a random edge\n */\n private calculateRandomEdge(\n finalPosition: { x: number; y: number },\n imageSize: { width: number; height: number },\n containerBounds: ContainerBounds,\n offset: number\n ): StartPosition {\n const edges: ('top' | 'bottom' | 'left' | 'right')[] = ['top', 'bottom', 'left', 'right'];\n const randomEdge = edges[Math.floor(Math.random() * edges.length)];\n return this.calculateEdgePosition(randomEdge, finalPosition, imageSize, containerBounds, offset);\n }\n\n /**\n * Calculate start position on a circle around the container\n */\n private calculateCircularPosition(\n _finalPosition: { x: number; y: number },\n _imageSize: { width: number; height: number },\n containerBounds: ContainerBounds,\n imageIndex: number,\n totalImages: number\n ): StartPosition {\n const circularConfig = this.config.start.circular || {};\n const distribution = circularConfig.distribution || 'even';\n\n // Calculate radius\n let radius: number;\n const radiusConfig = circularConfig.radius || '120%';\n\n if (typeof radiusConfig === 'string' && radiusConfig.endsWith('%')) {\n // Percentage of container diagonal\n const percentage = parseFloat(radiusConfig) / 100;\n const diagonal = Math.sqrt(\n containerBounds.width ** 2 + containerBounds.height ** 2\n );\n radius = diagonal * percentage / 2;\n } else {\n radius = typeof radiusConfig === 'number' ? radiusConfig : 500;\n }\n\n // Calculate angle\n let angle: number;\n if (distribution === 'even') {\n angle = (imageIndex / totalImages) * 2 * Math.PI;\n } else {\n angle = Math.random() * 2 * Math.PI;\n }\n\n // Calculate position on circle, centered on container\n const centerX = containerBounds.width / 2;\n const centerY = containerBounds.height / 2;\n\n // Return center position on circle (not top-left)\n const startX = centerX + Math.cos(angle) * radius;\n const startY = centerY + Math.sin(angle) * radius;\n\n return { x: startX, y: startY };\n }\n\n /**\n * Get animation parameters for an image\n */\n getAnimationParams(_imageIndex: number): AnimationParams {\n const duration = this.config.timing.duration;\n const easing = this.config.easing;\n\n return {\n startTransform: '', // Will be computed by caller based on start position\n duration,\n delay: 0,\n easing\n };\n }\n\n /**\n * Build a CSS transform string for the start position\n * Uses pixel-based centering offset for reliable cross-browser behavior\n */\n buildStartTransform(\n startPosition: StartPosition,\n finalPosition: { x: number; y: number },\n finalRotation: number,\n finalScale: number,\n imageWidth?: number,\n imageHeight?: number,\n startRotation?: number,\n startScale?: number\n ): string {\n // Calculate translation from final to start position\n const translateX = startPosition.x - finalPosition.x;\n const translateY = startPosition.y - finalPosition.y;\n\n // Use start rotation if provided, otherwise use final rotation\n const rotation = startRotation !== undefined ? startRotation : finalRotation;\n\n // Use start scale if provided, otherwise use final scale\n const scale = startScale !== undefined ? startScale : finalScale;\n\n // Use pixel offset if dimensions provided\n const centerOffsetX = imageWidth !== undefined ? -imageWidth / 2 : 0;\n const centerOffsetY = imageHeight !== undefined ? -imageHeight / 2 : 0;\n const centerTranslate = imageWidth !== undefined\n ? `translate(${centerOffsetX}px, ${centerOffsetY}px)`\n : `translate(-50%, -50%)`;\n\n if (startPosition.useScale) {\n // For center position: start at center with scale 0\n return `${centerTranslate} translate(${translateX}px, ${translateY}px) rotate(${rotation}deg) scale(0)`;\n }\n\n // Standard entry: translate from edge, with configured start scale\n return `${centerTranslate} translate(${translateX}px, ${translateY}px) rotate(${rotation}deg) scale(${scale})`;\n }\n\n /**\n * Build the final CSS transform string\n * Uses pixel-based centering offset for reliable cross-browser behavior\n */\n buildFinalTransform(rotation: number, scale: number, imageWidth?: number, imageHeight?: number): string {\n // Use pixel offset if dimensions provided, otherwise fall back to percentage\n if (imageWidth !== undefined && imageHeight !== undefined) {\n const offsetX = -imageWidth / 2;\n const offsetY = -imageHeight / 2;\n return `translate(${offsetX}px, ${offsetY}px) rotate(${rotation}deg) scale(${scale})`;\n }\n return `translate(-50%, -50%) rotate(${rotation}deg) scale(${scale})`;\n }\n\n /**\n * Get the transition CSS for entry animation\n * For JS-animated paths, only animate opacity (transform handled by JS)\n */\n getTransitionCSS(): string {\n const duration = this.config.timing.duration;\n const easing = this.config.easing;\n\n // For paths that use JS animation, don't transition transform\n if (this.requiresJSAnimation()) {\n return `opacity ${duration}ms ease-out`;\n }\n\n return `opacity ${duration}ms ease-out, transform ${duration}ms ${easing}`;\n }\n\n /**\n * Check if the current path type requires JavaScript animation\n */\n requiresJSAnimation(): boolean {\n return requiresJSAnimation(this.pathConfig.type);\n }\n\n /**\n * Get the path configuration\n */\n getPathConfig(): EntryPathConfig {\n return this.pathConfig;\n }\n\n /**\n * Get the path type\n */\n getPathType(): EntryPathType {\n return this.pathConfig.type;\n }\n\n /**\n * Get animation timing configuration\n */\n getTiming(): { duration: number } {\n return {\n duration: this.config.timing.duration\n };\n }\n\n /**\n * Get the rotation configuration\n */\n getRotationConfig(): EntryRotationConfig {\n return this.rotationConfig;\n }\n\n /**\n * Get the rotation mode\n */\n getRotationMode(): EntryRotationMode {\n return this.rotationConfig.mode;\n }\n\n /**\n * Calculate the starting rotation for an entry animation\n * @param finalRotation - The final rotation from the layout\n * @returns The starting rotation in degrees\n */\n calculateStartRotation(finalRotation: number): number {\n const mode = this.rotationConfig.mode;\n\n switch (mode) {\n case 'none':\n // No rotation animation - start at final rotation\n return finalRotation;\n\n case 'settle': {\n // Start at a configured rotation and settle to final\n const startConfig = this.rotationConfig.startRotation;\n if (startConfig === undefined) {\n // Default: ±30° random offset from final\n return finalRotation + (Math.random() - 0.5) * 60;\n }\n if (typeof startConfig === 'number') {\n return startConfig;\n }\n // Range: random value between min and max\n const range = startConfig.max - startConfig.min;\n return startConfig.min + Math.random() * range;\n }\n\n case 'spin': {\n // Spin from a rotated position to final\n const spinCount = this.rotationConfig.spinCount ?? 1;\n const direction = this.resolveSpinDirection(finalRotation);\n return finalRotation + (spinCount * 360 * direction);\n }\n\n case 'random':\n // Random starting rotation (±30° from final)\n return finalRotation + (Math.random() - 0.5) * 60;\n\n case 'wobble':\n // Wobble is handled in JS animation, start at final rotation\n return finalRotation;\n\n default:\n return finalRotation;\n }\n }\n\n /**\n * Resolve spin direction based on config\n * @returns 1 for clockwise, -1 for counterclockwise\n */\n private resolveSpinDirection(finalRotation: number): number {\n const direction = this.rotationConfig.direction ?? 'auto';\n\n switch (direction) {\n case 'clockwise':\n return -1; // Negative rotation = clockwise spin to final\n case 'counterclockwise':\n return 1; // Positive rotation = counterclockwise spin to final\n case 'random':\n return Math.random() < 0.5 ? 1 : -1;\n case 'auto':\n default:\n // Auto: choose direction that reduces total rotation distance\n // If final rotation is positive, spin counterclockwise (add positive offset)\n return finalRotation >= 0 ? 1 : -1;\n }\n }\n\n /**\n * Check if the current rotation mode requires JavaScript animation\n * (as opposed to CSS transitions)\n */\n requiresJSRotation(): boolean {\n return this.rotationConfig.mode === 'wobble';\n }\n\n /**\n * Calculate wobble rotation for a given animation progress\n * @param progress - Animation progress from 0 to 1\n * @param finalRotation - The final rotation in degrees\n * @returns The current rotation in degrees\n */\n calculateWobbleRotation(progress: number, finalRotation: number): number {\n if (this.rotationConfig.mode !== 'wobble') {\n return finalRotation;\n }\n\n const wobbleConfig = this.rotationConfig.wobble || {\n amplitude: 15,\n frequency: 3,\n decay: true\n };\n\n const { amplitude, frequency, decay } = wobbleConfig;\n\n // Oscillation using sine wave\n const oscillation = Math.sin(progress * frequency * Math.PI * 2);\n\n // Apply decay if enabled (stronger decay toward end)\n const decayFactor = decay ? Math.pow(1 - progress, 2) : 1;\n\n // Calculate wobble offset\n const wobbleOffset = amplitude * oscillation * decayFactor;\n\n return finalRotation + wobbleOffset;\n }\n\n /**\n * Get the scale configuration\n */\n getScaleConfig(): EntryScaleConfig {\n return this.scaleConfig;\n }\n\n /**\n * Get the scale mode\n */\n getScaleMode(): EntryScaleMode {\n return this.scaleConfig.mode;\n }\n\n /**\n * Calculate the starting scale for an entry animation\n * @param finalScale - The final scale from the layout\n * @returns The starting scale\n */\n calculateStartScale(finalScale: number): number {\n const mode = this.scaleConfig.mode;\n\n switch (mode) {\n case 'none':\n // No scale animation - start at final scale\n return finalScale;\n\n case 'grow': {\n // Start smaller, grow to final\n const startScale = this.scaleConfig.startScale ?? 0.3;\n return startScale * finalScale; // Apply relative to final scale\n }\n\n case 'shrink': {\n // Start larger, shrink to final\n const startScale = this.scaleConfig.startScale ?? 1.5;\n return startScale * finalScale; // Apply relative to final scale\n }\n\n case 'pop':\n // Pop mode uses JS animation, start at final scale\n // (the overshoot/bounce is handled in the animation tick)\n return finalScale;\n\n case 'random': {\n // Random start scale in configured range\n const range = this.scaleConfig.range ?? { min: 0.5, max: 1.0 };\n const randomFactor = range.min + Math.random() * (range.max - range.min);\n return randomFactor * finalScale;\n }\n\n default:\n return finalScale;\n }\n }\n\n /**\n * Check if the current scale mode requires JavaScript animation\n * (as opposed to CSS transitions)\n */\n requiresJSScale(): boolean {\n return this.scaleConfig.mode === 'pop';\n }\n\n /**\n * Calculate pop scale for a given animation progress\n * @param progress - Animation progress from 0 to 1\n * @param finalScale - The final scale value\n * @returns The current scale value with bounce effect\n */\n calculatePopScale(progress: number, finalScale: number): number {\n if (this.scaleConfig.mode !== 'pop') {\n return finalScale;\n }\n\n const popConfig = this.scaleConfig.pop || {\n overshoot: 1.2,\n bounces: 1\n };\n\n const { overshoot, bounces } = popConfig;\n\n // Create keyframes for bounce effect\n // Similar to bounce path but for scale\n const keyframes = this.generateScaleBounceKeyframes(bounces, overshoot);\n\n // Find current segment\n let currentScale = finalScale;\n for (let i = 0; i < keyframes.length; i++) {\n if (progress <= keyframes[i].time) {\n const prevTime = i === 0 ? 0 : keyframes[i - 1].time;\n const prevScale = i === 0 ? finalScale : keyframes[i - 1].scale;\n const segmentProgress = (progress - prevTime) / (keyframes[i].time - prevTime);\n // Smooth easing within segment\n const easedProgress = this.easeOutQuad(segmentProgress);\n currentScale = prevScale + (keyframes[i].scale - prevScale) * easedProgress;\n break;\n }\n }\n\n return currentScale * finalScale;\n }\n\n /**\n * Generate keyframes for scale bounce animation\n */\n private generateScaleBounceKeyframes(\n bounces: number,\n overshoot: number\n ): Array<{ time: number; scale: number }> {\n const keyframes: Array<{ time: number; scale: number }> = [];\n\n // Reach overshoot at 50% of animation\n keyframes.push({ time: 0.5, scale: overshoot });\n\n // Add bounces\n let currentOvershoot = overshoot;\n const bounceDecay = 0.5; // Each bounce is 50% of previous\n const remainingTime = 0.5;\n const bounceTime = remainingTime / (bounces * 2);\n\n let currentTime = 0.5;\n for (let i = 0; i < bounces; i++) {\n // Undershoot (go below 1)\n const undershoot = 1 - (currentOvershoot - 1) * bounceDecay;\n currentTime += bounceTime;\n keyframes.push({ time: currentTime, scale: undershoot });\n\n // Overshoot again (smaller)\n currentOvershoot = 1 + (currentOvershoot - 1) * bounceDecay * bounceDecay;\n currentTime += bounceTime;\n if (i < bounces - 1) {\n keyframes.push({ time: currentTime, scale: currentOvershoot });\n }\n }\n\n // Final settle\n keyframes.push({ time: 1, scale: 1 });\n\n return keyframes;\n }\n\n /**\n * Easing function for smooth transitions\n */\n private easeOutQuad(t: number): number {\n return 1 - (1 - t) * (1 - t);\n }\n}\n","/**\n * IdleAnimationEngine.ts\n * Manages continuous ambient animations for idle images using the Web Animations API.\n *\n * Uses composite: 'add' for transform-based animations (wiggle, pulse, spin) so\n * idle animations layer on top of the base transform without replacing it.\n * Blink uses opacity with no composite.\n */\n\nimport type {\n IdleAnimationConfig,\n IdleWiggleConfig,\n IdlePulseConfig,\n IdleBlinkConfig,\n IdleSpinConfig\n} from '../config/types';\nimport {\n DEFAULT_IDLE_WIGGLE,\n DEFAULT_IDLE_PULSE,\n DEFAULT_IDLE_BLINK,\n DEFAULT_IDLE_SPIN\n} from '../config/defaults';\n\ninterface IdleEntry {\n element: HTMLElement;\n index: number;\n totalImages: number;\n animation: Animation | null;\n blinkAnimation: Animation | null;\n customTeardown: (() => void) | null;\n paused: boolean;\n stopped: boolean;\n startTimer: ReturnType<typeof setTimeout> | null;\n}\n\nexport class IdleAnimationEngine {\n private config: IdleAnimationConfig;\n private entries: Map<HTMLElement, IdleEntry> = new Map();\n private entryDurationMs: number;\n\n // Single rAF loop shared across all 'together'-sync animations.\n // Each frame, all active together-mode animations get the same currentTime.\n private togetherRafId: number | null = null;\n private togetherSpeed: number = 0;\n\n constructor(config: IdleAnimationConfig, entryDurationMs = 600) {\n this.config = config;\n this.entryDurationMs = entryDurationMs;\n }\n\n /**\n * Register an image element for idle animation.\n * Starts animation after entry duration completes.\n */\n register(element: HTMLElement, index: number, totalImages: number, entryDuration?: number): void {\n if (this.entries.has(element)) return;\n\n const delay = entryDuration ?? this.entryDurationMs;\n const startDelay = this.config.startDelay ?? delay;\n\n const entry: IdleEntry = {\n element,\n index,\n totalImages,\n animation: null,\n blinkAnimation: null,\n customTeardown: null,\n paused: false,\n stopped: false,\n startTimer: null\n };\n\n this.entries.set(element, entry);\n\n entry.startTimer = setTimeout(() => {\n entry.startTimer = null;\n if (!entry.stopped && !entry.paused) {\n this._startAnimation(entry);\n }\n }, startDelay);\n }\n\n /**\n * Pause idle animation for a specific image (set to neutral then pause).\n */\n pauseForImage(element: HTMLElement): void {\n const entry = this.entries.get(element);\n if (!entry) return;\n entry.paused = true;\n // Cancel start timer if still pending\n if (entry.startTimer !== null) {\n clearTimeout(entry.startTimer);\n entry.startTimer = null;\n }\n this._pauseEntry(entry);\n }\n\n /**\n * Resume idle animation for a specific image by starting a fresh animation.\n * Always restarts rather than resuming, to avoid Web Animations API\n * quirks with negative-delay animations after pause/cancel.\n */\n resumeForImage(element: HTMLElement): void {\n const entry = this.entries.get(element);\n if (!entry || entry.stopped) return;\n entry.paused = false;\n this._startAnimation(entry);\n }\n\n /**\n * Stop and remove idle animation for a specific image.\n */\n stopForImage(element: HTMLElement): void {\n const entry = this.entries.get(element);\n if (!entry) return;\n entry.stopped = true;\n if (entry.startTimer !== null) {\n clearTimeout(entry.startTimer);\n entry.startTimer = null;\n }\n this._cancelEntry(entry);\n this.entries.delete(element);\n }\n\n pauseAll(): void {\n for (const entry of this.entries.values()) {\n entry.paused = true;\n if (entry.startTimer !== null) {\n clearTimeout(entry.startTimer);\n entry.startTimer = null;\n }\n this._pauseEntry(entry);\n }\n }\n\n resumeAll(): void {\n for (const entry of this.entries.values()) {\n if (!entry.stopped) {\n entry.paused = false;\n this._startAnimation(entry);\n }\n }\n }\n\n stopAll(): void {\n for (const entry of this.entries.values()) {\n entry.stopped = true;\n if (entry.startTimer !== null) {\n clearTimeout(entry.startTimer);\n entry.startTimer = null;\n }\n this._cancelEntry(entry);\n }\n this.entries.clear();\n this._stopTogetherLoop();\n }\n\n // ──────────────────────────────────────────────────────────────────────────\n // Private helpers\n // ──────────────────────────────────────────────────────────────────────────\n\n private _startAnimation(entry: IdleEntry): void {\n const { type } = this.config;\n switch (type) {\n case 'wiggle': this._startWiggle(entry); break;\n case 'pulse': this._startPulse(entry); break;\n case 'blink': this._startBlink(entry); break;\n case 'spin': this._startSpin(entry); break;\n case 'custom': this._startCustom(entry); break;\n default: break;\n }\n }\n\n private _startWiggle(entry: IdleEntry): void {\n const cfg: IdleWiggleConfig = { ...DEFAULT_IDLE_WIGGLE, ...this.config.wiggle };\n\n const keyframes: Keyframe[] = [\n { transform: 'rotate(0deg)', offset: 0 },\n { transform: `rotate(${cfg.maxAngle}deg)`, offset: 0.25 },\n { transform: 'rotate(0deg)', offset: 0.5 },\n { transform: `rotate(${-cfg.maxAngle}deg)`, offset: 0.75 },\n { transform: 'rotate(0deg)', offset: 1 }\n ];\n\n if (cfg.sync === 'together') {\n entry.animation = entry.element.animate(keyframes, {\n duration: cfg.speed, iterations: Infinity, composite: 'add', fill: 'both'\n });\n entry.animation.pause();\n this._startTogetherLoop(cfg.speed);\n } else {\n entry.animation = entry.element.animate(keyframes, {\n duration: cfg.speed,\n delay: -(Math.random() * cfg.speed),\n iterations: Infinity,\n composite: 'add'\n });\n }\n }\n\n private _startPulse(entry: IdleEntry): void {\n const cfg: IdlePulseConfig = { ...DEFAULT_IDLE_PULSE, ...this.config.pulse };\n\n const keyframes: Keyframe[] = [\n { transform: 'scale(1)', offset: 0 },\n { transform: `scale(${cfg.maxScale})`, offset: 0.25 },\n { transform: 'scale(1)', offset: 0.5 },\n { transform: `scale(${cfg.minScale})`, offset: 0.75 },\n { transform: 'scale(1)', offset: 1 }\n ];\n\n if (cfg.sync === 'together') {\n entry.animation = entry.element.animate(keyframes, {\n duration: cfg.speed, iterations: Infinity, composite: 'add', fill: 'both'\n });\n entry.animation.pause();\n this._startTogetherLoop(cfg.speed);\n } else {\n entry.animation = entry.element.animate(keyframes, {\n duration: cfg.speed,\n delay: -(Math.random() * cfg.speed),\n iterations: Infinity,\n composite: 'add'\n });\n }\n }\n\n private _startBlink(entry: IdleEntry): void {\n const cfg: IdleBlinkConfig = { ...DEFAULT_IDLE_BLINK, ...this.config.blink };\n const delay = -(Math.random() * cfg.speed);\n\n // Use the element's current opacity as the \"visible\" state so blink\n // respects the configured default (and hover) opacity.\n const onOpacity = parseFloat(getComputedStyle(entry.element).opacity) || 1;\n\n let keyframes: Keyframe[];\n let options: KeyframeAnimationOptions;\n\n if (cfg.style === 'fade') {\n keyframes = [\n { opacity: onOpacity, offset: 0 },\n { opacity: 0, offset: 0.5 },\n { opacity: onOpacity, offset: 1 }\n ];\n options = {\n duration: cfg.speed,\n delay,\n iterations: Infinity,\n easing: 'ease-in-out'\n };\n } else {\n // snap (default)\n keyframes = [\n { opacity: onOpacity, offset: 0 },\n { opacity: onOpacity, offset: cfg.onRatio },\n { opacity: 0, offset: Math.min(cfg.onRatio + 0.01, 0.99) },\n { opacity: 0, offset: 0.99 },\n { opacity: onOpacity, offset: 1 }\n ];\n options = {\n duration: cfg.speed,\n delay,\n iterations: Infinity\n };\n }\n\n entry.blinkAnimation = entry.element.animate(keyframes, options);\n }\n\n private _startSpin(entry: IdleEntry): void {\n const cfg: IdleSpinConfig = { ...DEFAULT_IDLE_SPIN, ...this.config.spin };\n const endDeg = cfg.direction === 'clockwise' ? 360 : -360;\n\n entry.animation = entry.element.animate(\n [{ transform: 'rotate(0deg)' }, { transform: `rotate(${endDeg}deg)` }],\n {\n duration: cfg.speed,\n iterations: Infinity,\n easing: 'linear',\n composite: 'add'\n }\n );\n }\n\n private _startCustom(entry: IdleEntry): void {\n const fn = this.config.custom;\n if (!fn) return;\n\n const result = fn({ element: entry.element, index: entry.index, totalImages: entry.totalImages });\n\n if (typeof result === 'function') {\n entry.customTeardown = result;\n } else if (result && typeof (result as Animation).play === 'function') {\n entry.animation = result as Animation;\n }\n }\n\n private _startTogetherLoop(speed: number): void {\n this.togetherSpeed = speed;\n if (this.togetherRafId !== null) return; // already ticking\n const tick = () => {\n const t = performance.now() % this.togetherSpeed;\n for (const entry of this.entries.values()) {\n if (!entry.stopped && !entry.paused && entry.animation) {\n entry.animation.currentTime = t;\n }\n }\n this.togetherRafId = requestAnimationFrame(tick);\n };\n this.togetherRafId = requestAnimationFrame(tick);\n }\n\n private _stopTogetherLoop(): void {\n if (this.togetherRafId !== null) {\n cancelAnimationFrame(this.togetherRafId);\n this.togetherRafId = null;\n }\n }\n\n private _pauseEntry(entry: IdleEntry): void {\n // Cancel rather than pause — avoids Web Animations API quirks with\n // negative-delay infinite animations. resumeForImage() always restarts fresh.\n if (entry.animation) {\n entry.animation.cancel();\n entry.animation = null;\n }\n if (entry.blinkAnimation) {\n entry.blinkAnimation.cancel();\n entry.blinkAnimation = null;\n }\n }\n\n private _cancelEntry(entry: IdleEntry): void {\n if (entry.animation) {\n entry.animation.cancel();\n entry.animation = null;\n }\n if (entry.blinkAnimation) {\n entry.blinkAnimation.cancel();\n entry.blinkAnimation = null;\n }\n if (entry.customTeardown) {\n entry.customTeardown();\n entry.customTeardown = null;\n }\n }\n}\n","/**\n * RandomPlacementLayout.ts\n * Generates random overlapping layouts for image cloud\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, ImageConfig } from '../config/types';\n\ninterface RandomLayoutOptions extends Partial<LayoutConfig> {\n fixedHeight?: number;\n}\n\nexport class RandomPlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n\n constructor(config: LayoutConfig, imageConfig: ImageConfig = {}) {\n this.config = config;\n this.imageConfig = imageConfig;\n }\n\n /**\n * Generate random layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides (includes fixedHeight)\n * @returns Array of layout objects with position, rotation, scale\n */\n generate(imageCount: number, containerBounds: ContainerBounds, options: RandomLayoutOptions = {}): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n\n const padding = this.config.spacing.padding;\n // Use fixedHeight if provided, otherwise use default 200\n const baseImageSize = options.fixedHeight ?? 200;\n\n // Get rotation config from image config\n const rotationMode = this.imageConfig.rotation?.mode ?? 'none';\n const minRotation = this.imageConfig.rotation?.range?.min ?? -15;\n const maxRotation = this.imageConfig.rotation?.range?.max ?? 15;\n\n // Get variance config from image config\n const varianceMin = this.imageConfig.sizing?.variance?.min ?? 1.0;\n const varianceMax = this.imageConfig.sizing?.variance?.max ?? 1.0;\n const hasVariance = varianceMin !== 1.0 || varianceMax !== 1.0;\n\n // Calculate safe bounds for center positions (accounting for half image size and padding)\n // Use 16:9 aspect ratio (1.78) as maximum to handle most landscape images\n const estAspectRatio = 1.5; // 3:2 - balanced for mixed portrait/landscape\n const halfWidth = (baseImageSize * estAspectRatio) / 2;\n const halfHeight = baseImageSize / 2;\n\n const maxX = width - padding - halfWidth;\n const maxY = height - padding - halfHeight;\n const minX = padding + halfWidth;\n const minY = padding + halfHeight;\n\n for (let i = 0; i < imageCount; i++) {\n // Random center position within safe bounds\n const x = this.random(minX, maxX);\n const y = this.random(minY, maxY);\n\n // Random rotation within range (only when mode is random)\n const rotation = rotationMode === 'random' ? this.random(minRotation, maxRotation) : 0;\n\n // Random size variance\n const scale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const scaledImageSize = baseImageSize * scale;\n\n const layout: ImageLayout = {\n id: i,\n x,\n y,\n rotation,\n scale,\n baseSize: scaledImageSize\n };\n\n layouts.push(layout);\n }\n\n return layouts;\n }\n\n /**\n * Utility: Generate random number between min and max\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random number in range\n */\n private random(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n }\n}\n","/**\n * RadialPlacementLayout.ts\n * Generates concentric radial layouts for image cloud\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, RadialAlgorithmConfig, ImageConfig } from '../config/types';\nimport { DEFAULT_RADIAL_CONFIG } from '../config/defaults';\n\ninterface RadialLayoutOptions extends Partial<LayoutConfig> {\n fixedHeight?: number;\n}\n\nexport class RadialPlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n\n constructor(config: LayoutConfig, imageConfig: ImageConfig = {}) {\n this.config = config;\n this.imageConfig = imageConfig;\n }\n\n /**\n * Generate radial layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides\n * @returns Array of layout objects with position, rotation, scale\n */\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options: RadialLayoutOptions = {}\n ): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n // Use fixedHeight if provided, otherwise use default 200\n const baseImageSize = options.fixedHeight ?? 200;\n\n // Get rotation config from image config\n const rotationMode = this.imageConfig.rotation?.mode ?? 'none';\n const minRotation = this.imageConfig.rotation?.range?.min ?? -15;\n const maxRotation = this.imageConfig.rotation?.range?.max ?? 15;\n\n // Get variance config from image config\n const varianceMin = this.imageConfig.sizing?.variance?.min ?? 1.0;\n const varianceMax = this.imageConfig.sizing?.variance?.max ?? 1.0;\n const hasVariance = varianceMin !== 1.0 || varianceMax !== 1.0;\n\n // Get scale decay from layout config\n const scaleDecay = this.config.scaleDecay ?? 0;\n\n const radialConfig: RadialAlgorithmConfig = {\n ...DEFAULT_RADIAL_CONFIG,\n ...this.config.radial,\n };\n\n // Use override fixedHeight if provided, else baseImageSize\n const imageSize = options.fixedHeight ?? baseImageSize;\n const cx = width / 2;\n const cy = height / 2;\n\n // Calculate max rings for scale decay calculation\n const estimatedMaxRings = Math.ceil(Math.sqrt(imageCount));\n\n const padding = this.config.spacing.padding ?? 50;\n const maxRadius = Math.max(imageSize * 0.8, Math.min(\n cx - padding - imageSize / 2,\n cy - padding - imageSize / 2\n ));\n\n // Add center image (using center position)\n if (imageCount > 0) {\n // Apply variance to center image\n const varianceScale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const centerSize = imageSize * varianceScale;\n\n layouts.push({\n id: 0,\n x: cx,\n y: cy,\n rotation: rotationMode === 'random' ? this.random(minRotation * 0.33, maxRotation * 0.33) : 0, // Less rotation for center\n scale: varianceScale,\n baseSize: centerSize,\n zIndex: 100 // Center image is highest\n });\n }\n\n let processedCount = 1;\n let currentRing = 1;\n\n while (processedCount < imageCount) {\n // Calculate scale decay for this ring (center is largest, outer rings smaller)\n const normalizedRing = currentRing / estimatedMaxRings;\n const ringScale = scaleDecay > 0\n ? 1 - (normalizedRing * scaleDecay * 0.5) // Max 50% size reduction\n : 1.0;\n\n // Ring settings\n // Scale X more than Y to create horizontal oval shape\n const ringStep = Math.max(imageSize * 0.8, (maxRadius / estimatedMaxRings) * 1.5 / radialConfig.tightness);\n const radiusY = currentRing * ringStep;\n const radiusX = radiusY * 1.5; // Horizontal stretching factor\n\n const circumference = Math.PI * (3 * (radiusX + radiusY) - Math.sqrt((3 * radiusX + radiusY) * (radiusX + 3 * radiusY))); // Ramanujan's approximation\n\n const estimatedItemWidth = this.estimateWidth(imageSize);\n // Increase density by ~40% (1.1 -> 0.7)\n const itemsInRing = Math.floor(circumference / (estimatedItemWidth * 0.7));\n\n if (itemsInRing === 0) {\n currentRing++;\n continue;\n }\n\n const angleStep = (2 * Math.PI) / itemsInRing;\n\n // Add offset of 20 degrees per ring\n const ringOffset = currentRing * (20 * Math.PI / 180);\n\n for (let i = 0; i < itemsInRing && processedCount < imageCount; i++) {\n const angle = (i * angleStep) + ringOffset;\n\n // Apply variance and scale decay\n const varianceScale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const combinedScale = ringScale * varianceScale;\n const scaledImageSize = imageSize * combinedScale;\n\n // Calculate center position of image using elliptical formula (store center, not top-left)\n let x = cx + Math.cos(angle) * radiusX;\n let y = cy + Math.sin(angle) * radiusY;\n\n // Boundary Clamping - clamp center position with conservative estimate\n // Use 16:9 aspect ratio (1.78) as maximum to handle most landscape images\n const estAspectRatio = 1.5; // 3:2 - balanced for mixed portrait/landscape\n const halfWidth = (scaledImageSize * estAspectRatio) / 2;\n const halfHeight = scaledImageSize / 2;\n\n // Clamp X (center position)\n if (x - halfWidth < padding) {\n x = padding + halfWidth;\n } else if (x + halfWidth > width - padding) {\n x = width - padding - halfWidth;\n }\n\n // Clamp Y (center position)\n if (y - halfHeight < padding) {\n y = padding + halfHeight;\n } else if (y + halfHeight > height - padding) {\n y = height - padding - halfHeight;\n }\n\n const rotation = rotationMode === 'random' ? this.random(minRotation, maxRotation) : 0;\n\n layouts.push({\n id: processedCount,\n x,\n y,\n rotation,\n scale: combinedScale,\n baseSize: scaledImageSize,\n zIndex: Math.max(1, 100 - currentRing) // Outer rings have lower z-index\n });\n\n processedCount++;\n }\n\n currentRing++;\n }\n\n return layouts;\n }\n\n /**\n * Estimate image width based on height\n * Assumes landscape aspect ratio (approximately 1.4:1)\n * @param height - Image height\n * @returns Estimated width\n */\n private estimateWidth(height: number): number {\n // Assume landscape aspect ratio approx 4:3 or 16:9 on average\n // Using 1.4 ratio\n return height * 1.4;\n }\n\n /**\n * Utility: Generate random number between min and max\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random number in range\n */\n private random(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n }\n}","/**\n * GridPlacementLayout.ts\n * Generates grid-based layouts with optional stagger and jitter\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, GridAlgorithmConfig, ImageConfig } from '../config/types';\n\ninterface GridLayoutOptions extends Partial<LayoutConfig> {\n fixedHeight?: number;\n}\n\nconst DEFAULT_GRID_CONFIG: GridAlgorithmConfig = {\n columns: 'auto',\n rows: 'auto',\n stagger: 'none',\n jitter: 0,\n overlap: 0,\n fillDirection: 'row',\n alignment: 'center',\n gap: 10,\n overflowOffset: 0.25\n};\n\n// Offset pattern for stacking overflow images within cells\n// Each overflow layer cycles through these directions\n// Corners first, then cardinal directions\nconst OVERFLOW_OFFSET_PATTERN = [\n { x: 1, y: 1 }, // bottom-right\n { x: -1, y: -1 }, // upper-left\n { x: 1, y: -1 }, // upper-right\n { x: -1, y: 1 }, // bottom-left\n { x: -1, y: 0 }, // left\n { x: 1, y: 0 }, // right\n { x: 0, y: -1 }, // up\n { x: 0, y: 1 }, // down\n];\n\nexport class GridPlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n\n constructor(config: LayoutConfig, imageConfig: ImageConfig = {}) {\n this.config = config;\n this.imageConfig = imageConfig;\n }\n\n /**\n * Generate grid layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides (includes fixedHeight)\n * @returns Array of layout objects with position, rotation, scale\n */\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options: GridLayoutOptions = {}\n ): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n\n const gridConfig = { ...DEFAULT_GRID_CONFIG, ...this.config.grid };\n const padding = this.config.spacing.padding;\n // Use fixedHeight if provided, otherwise use base size from config\n const baseImageSize = options.fixedHeight ?? 200;\n\n // Get rotation config from image config\n const rotationMode = this.imageConfig.rotation?.mode ?? 'none';\n\n // Get variance config from image config\n const varianceMin = this.imageConfig.sizing?.variance?.min ?? 1.0;\n const varianceMax = this.imageConfig.sizing?.variance?.max ?? 1.0;\n const hasVariance = varianceMin !== 1.0 || varianceMax !== 1.0;\n\n // Calculate available space\n const availableWidth = width - (2 * padding);\n const availableHeight = height - (2 * padding);\n\n // Calculate grid dimensions\n const { columns, rows } = this.calculateGridDimensions(\n imageCount,\n availableWidth,\n availableHeight,\n baseImageSize,\n gridConfig\n );\n\n // For stagger layouts, we need n+0.5 cells to fit in available space\n // Calculate cell size accounting for this extra half-cell\n const hasRowStagger = gridConfig.stagger === 'row';\n const hasColumnStagger = gridConfig.stagger === 'column';\n\n // Effective columns/rows for sizing: add 0.5 for stagger\n const effectiveColumns = hasRowStagger ? columns + 0.5 : columns;\n const effectiveRows = hasColumnStagger ? rows + 0.5 : rows;\n\n // Calculate cell size to fit effective count in available space\n const cellWidth = (availableWidth - (gridConfig.gap * (columns - 1))) / effectiveColumns;\n const cellHeight = (availableHeight - (gridConfig.gap * (rows - 1))) / effectiveRows;\n\n // Stagger offset is half a cell\n const staggerOffsetX = hasRowStagger ? cellWidth / 2 : 0;\n const staggerOffsetY = hasColumnStagger ? cellHeight / 2 : 0;\n\n // Calculate image size with overlap factor\n // overlap: 0 = fit in cell, 0.5 = 50% larger, 1.0 = 2x cell size\n const overlapMultiplier = 1 + gridConfig.overlap;\n const cellBasedSize = Math.min(cellWidth, cellHeight) * overlapMultiplier;\n\n // Use fixedHeight if provided, otherwise use cell-based calculation\n // For grid layouts, we respect fixedHeight but cap at cell-based size to avoid overflow\n const imageSize = options.fixedHeight\n ? Math.min(options.fixedHeight, cellBasedSize)\n : cellBasedSize;\n\n // Calculate total grid dimensions for alignment (include stagger offset)\n const totalGridWidth = columns * cellWidth + (columns - 1) * gridConfig.gap + staggerOffsetX;\n const totalGridHeight = rows * cellHeight + (rows - 1) * gridConfig.gap + staggerOffsetY;\n\n // Center the grid in the container\n const gridOffsetX = padding + (availableWidth - totalGridWidth) / 2;\n const gridOffsetY = padding + (availableHeight - totalGridHeight) / 2;\n\n // Detect overflow mode: when both dimensions are fixed and we have more images than cells\n const cellCount = columns * rows;\n const hasFixedGrid = gridConfig.columns !== 'auto' && gridConfig.rows !== 'auto';\n const isOverflowMode = hasFixedGrid && imageCount > cellCount;\n\n if (typeof window !== 'undefined') {\n (window as any).__gridOverflowDebug = {\n gridConfigColumns: gridConfig.columns,\n gridConfigRows: gridConfig.rows,\n columns, rows, cellCount,\n hasFixedGrid, imageCount, isOverflowMode\n };\n }\n\n // Track stack depth for each cell (used in overflow mode)\n const cellStackCount: number[] = isOverflowMode ? new Array(cellCount).fill(0) : [];\n\n // Calculate overflow offset in pixels\n const overflowOffsetPx = Math.min(cellWidth, cellHeight) * gridConfig.overflowOffset;\n\n for (let i = 0; i < imageCount; i++) {\n let col: number;\n let row: number;\n let stackLayer = 0; // 0 = base image, 1+ = overflow layers\n\n if (isOverflowMode && i >= cellCount) {\n // Overflow image: determine target cell and stack layer\n const overflowIndex = i - cellCount;\n const targetCell = overflowIndex % cellCount;\n stackLayer = Math.floor(overflowIndex / cellCount) + 1;\n cellStackCount[targetCell]++;\n\n // Get cell coordinates from target cell index\n if (gridConfig.fillDirection === 'row') {\n col = targetCell % columns;\n row = Math.floor(targetCell / columns);\n } else {\n row = targetCell % rows;\n col = Math.floor(targetCell / rows);\n }\n } else {\n // Base image: normal cell assignment\n if (gridConfig.fillDirection === 'row') {\n col = i % columns;\n row = Math.floor(i / columns);\n } else {\n row = i % rows;\n col = Math.floor(i / rows);\n }\n }\n\n // Base cell position (center of cell)\n let cellCenterX = gridOffsetX + col * (cellWidth + gridConfig.gap) + cellWidth / 2;\n let cellCenterY = gridOffsetY + row * (cellHeight + gridConfig.gap) + cellHeight / 2;\n\n // Apply stagger offset\n if (gridConfig.stagger === 'row' && row % 2 === 1) {\n cellCenterX += cellWidth / 2;\n } else if (gridConfig.stagger === 'column' && col % 2 === 1) {\n cellCenterY += cellHeight / 2;\n }\n\n // Apply overflow offset for stacked images\n if (stackLayer > 0) {\n const patternIndex = (stackLayer - 1) % OVERFLOW_OFFSET_PATTERN.length;\n const pattern = OVERFLOW_OFFSET_PATTERN[patternIndex];\n cellCenterX += pattern.x * overflowOffsetPx;\n cellCenterY += pattern.y * overflowOffsetPx;\n }\n\n // Apply jitter (random offset within cell bounds)\n if (gridConfig.jitter > 0) {\n const maxJitterX = (cellWidth / 2) * gridConfig.jitter;\n const maxJitterY = (cellHeight / 2) * gridConfig.jitter;\n cellCenterX += this.random(-maxJitterX, maxJitterX);\n cellCenterY += this.random(-maxJitterY, maxJitterY);\n }\n\n // Store center position directly (not top-left)\n let x = cellCenterX;\n let y = cellCenterY;\n\n // Handle incomplete row alignment (only for non-overflow mode)\n if (!isOverflowMode && gridConfig.fillDirection === 'row') {\n const itemsInLastRow = imageCount % columns || columns;\n const isLastRow = row === Math.floor((imageCount - 1) / columns);\n\n if (isLastRow && itemsInLastRow < columns) {\n const lastRowWidth = itemsInLastRow * cellWidth + (itemsInLastRow - 1) * gridConfig.gap;\n let alignmentOffset = 0;\n\n if (gridConfig.alignment === 'center') {\n alignmentOffset = (totalGridWidth - lastRowWidth) / 2;\n } else if (gridConfig.alignment === 'end') {\n alignmentOffset = totalGridWidth - lastRowWidth;\n }\n\n x += alignmentOffset;\n }\n }\n\n // Apply variance to create non-uniform look\n const varianceScale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const scaledImageSize = imageSize * varianceScale;\n\n // Boundary clamping for center-based positioning\n // Use 1.5 multiplier (3:2 aspect) as reasonable middle ground for mixed portrait/landscape\n const estAspectRatio = 1.5;\n const halfWidth = (scaledImageSize * estAspectRatio) / 2;\n const halfHeight = scaledImageSize / 2;\n const minX = padding + halfWidth;\n const maxX = width - padding - halfWidth;\n const minY = padding + halfHeight;\n const maxY = height - padding - halfHeight;\n\n x = Math.max(minX, Math.min(x, maxX));\n y = Math.max(minY, Math.min(y, maxY));\n\n // Apply rotation when mode is random\n // If jitter > 0, scale rotation by jitter factor; otherwise use full rotation range\n let rotation = 0;\n if (rotationMode === 'random') {\n const minRotation = this.imageConfig.rotation?.range?.min ?? -15;\n const maxRotation = this.imageConfig.rotation?.range?.max ?? 15;\n if (gridConfig.jitter > 0) {\n // Scale rotation by jitter factor for more subtle effect\n rotation = this.random(minRotation * gridConfig.jitter, maxRotation * gridConfig.jitter);\n } else {\n // Full rotation range even without jitter\n rotation = this.random(minRotation, maxRotation);\n }\n }\n\n // Calculate z-index: in overflow mode, overflow images go BELOW base images\n // Base images get higher z-index so they appear on top\n // Overflow images get progressively lower z-index so they flow under\n let zIndex: number;\n if (isOverflowMode && stackLayer > 0) {\n // Overflow images: lower z-index, decreasing with each layer\n // Layer 1 = 50, Layer 2 = 49, etc.\n zIndex = 50 - stackLayer;\n } else {\n // Base images: higher z-index (100+)\n zIndex = isOverflowMode ? 100 + i : i + 1;\n }\n\n layouts.push({\n id: i,\n x,\n y,\n rotation,\n scale: varianceScale,\n baseSize: scaledImageSize,\n zIndex\n });\n }\n\n return layouts;\n }\n\n /**\n * Calculate optimal grid dimensions based on image count and container\n */\n private calculateGridDimensions(\n imageCount: number,\n availableWidth: number,\n availableHeight: number,\n _baseImageSize: number,\n config: GridAlgorithmConfig\n ): { columns: number; rows: number } {\n let columns: number;\n let rows: number;\n\n if (config.columns !== 'auto' && config.rows !== 'auto') {\n columns = config.columns;\n rows = config.rows;\n } else if (config.columns !== 'auto') {\n columns = config.columns;\n rows = Math.ceil(imageCount / columns);\n } else if (config.rows !== 'auto') {\n rows = config.rows;\n columns = Math.ceil(imageCount / rows);\n } else {\n // Auto-calculate: try to fill the space while maintaining reasonable aspect ratio\n const aspectRatio = availableWidth / availableHeight;\n const imageAspectRatio = 1.4; // Assumed landscape\n\n // Calculate ideal columns based on aspect ratio\n columns = Math.max(1, Math.round(Math.sqrt(imageCount * aspectRatio / imageAspectRatio)));\n rows = Math.ceil(imageCount / columns);\n\n // Ensure we don't have too many empty cells\n while (columns > 1 && (columns - 1) * rows >= imageCount) {\n columns--;\n }\n }\n\n return { columns: Math.max(1, columns), rows: Math.max(1, rows) };\n }\n\n /**\n * Utility: Generate random number between min and max\n */\n private random(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n }\n}\n","/**\n * SpiralPlacementLayout.ts\n * Generates spiral layouts (golden, archimedean, logarithmic)\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, SpiralAlgorithmConfig, ImageConfig } from '../config/types';\n\ninterface SpiralLayoutOptions extends Partial<LayoutConfig> {\n fixedHeight?: number;\n}\n\n// Golden angle in radians (~137.5 degrees)\nconst GOLDEN_ANGLE = Math.PI * (3 - Math.sqrt(5));\n\nconst DEFAULT_SPIRAL_CONFIG: SpiralAlgorithmConfig = {\n spiralType: 'golden',\n direction: 'counterclockwise',\n tightness: 1.0,\n scaleDecay: 0,\n startAngle: 0\n};\n\nexport class SpiralPlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n\n constructor(config: LayoutConfig, imageConfig: ImageConfig = {}) {\n this.config = config;\n this.imageConfig = imageConfig;\n }\n\n /**\n * Generate spiral layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides (includes fixedHeight)\n * @returns Array of layout objects with position, rotation, scale\n */\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options: SpiralLayoutOptions = {}\n ): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n\n const spiralConfig = { ...DEFAULT_SPIRAL_CONFIG, ...this.config.spiral };\n const padding = this.config.spacing.padding;\n // Use fixedHeight if provided, otherwise use base size from config\n const baseImageSize = options.fixedHeight ?? 200;\n\n // Get rotation config from image config\n const rotationMode = this.imageConfig.rotation?.mode ?? 'none';\n const minRotation = this.imageConfig.rotation?.range?.min ?? -15;\n const maxRotation = this.imageConfig.rotation?.range?.max ?? 15;\n\n // Get variance config from image config\n const varianceMin = this.imageConfig.sizing?.variance?.min ?? 1.0;\n const varianceMax = this.imageConfig.sizing?.variance?.max ?? 1.0;\n const hasVariance = varianceMin !== 1.0 || varianceMax !== 1.0;\n\n // Get scale decay from layout config (overrides spiral-specific config)\n const scaleDecay = this.config.scaleDecay ?? spiralConfig.scaleDecay;\n\n // Center of the spiral\n const cx = width / 2;\n const cy = height / 2;\n\n // Calculate available radius (distance from center to edge)\n const maxRadius = Math.min(\n cx - padding - baseImageSize / 2,\n cy - padding - baseImageSize / 2\n );\n\n // Direction multiplier (1 for counterclockwise, -1 for clockwise)\n const directionMultiplier = spiralConfig.direction === 'clockwise' ? -1 : 1;\n\n for (let i = 0; i < imageCount; i++) {\n // Calculate angle based on spiral type\n let angle: number;\n let radius: number;\n\n if (spiralConfig.spiralType === 'golden') {\n // Golden spiral: each point separated by golden angle\n // Creates optimal distribution like sunflower seeds\n angle = i * GOLDEN_ANGLE * directionMultiplier + spiralConfig.startAngle;\n // Radius grows with square root of index for even distribution\n radius = this.calculateGoldenRadius(i, imageCount, maxRadius, spiralConfig.tightness);\n } else if (spiralConfig.spiralType === 'archimedean') {\n // Archimedean spiral: r = a + b*θ (constant spacing between arms)\n const theta = i * 0.5 * spiralConfig.tightness;\n angle = theta * directionMultiplier + spiralConfig.startAngle;\n radius = this.calculateArchimedeanRadius(theta, imageCount, maxRadius, spiralConfig.tightness);\n } else {\n // Logarithmic spiral: r = a * e^(b*θ) (self-similar, appears in nature)\n const theta = i * 0.3 * spiralConfig.tightness;\n angle = theta * directionMultiplier + spiralConfig.startAngle;\n radius = this.calculateLogarithmicRadius(theta, imageCount, maxRadius, spiralConfig.tightness);\n }\n\n // Convert polar to cartesian coordinates (store center position)\n const x = cx + Math.cos(angle) * radius;\n const y = cy + Math.sin(angle) * radius;\n\n // Calculate scale based on decay (center images larger) - use scaleDecay from image config\n const normalizedRadius = radius / maxRadius;\n const decayScale = scaleDecay > 0\n ? 1 - (normalizedRadius * scaleDecay * 0.5) // Max 50% size reduction\n : 1.0;\n\n // Apply variance\n const varianceScale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const combinedScale = decayScale * varianceScale;\n\n // Apply scaled image size\n const scaledImageSize = baseImageSize * combinedScale;\n\n // Clamp center positions to keep images within bounds\n // Use 16:9 aspect ratio (1.78) as maximum to handle most landscape images\n const estAspectRatio = 1.5; // 3:2 - balanced for mixed portrait/landscape\n const halfWidth = (scaledImageSize * estAspectRatio) / 2;\n const halfHeight = scaledImageSize / 2;\n const minX = padding + halfWidth;\n const maxX = width - padding - halfWidth;\n const minY = padding + halfHeight;\n const maxY = height - padding - halfHeight;\n\n const clampedX = Math.max(minX, Math.min(x, maxX));\n const clampedY = Math.max(minY, Math.min(y, maxY));\n\n // Rotation based on mode\n let rotation = 0;\n if (rotationMode === 'random') {\n const baseRotation = (angle * 180 / Math.PI) % 360;\n const rotationVariance = this.random(minRotation, maxRotation);\n rotation = spiralConfig.spiralType === 'golden'\n ? rotationVariance // Pure random for golden (more organic)\n : (baseRotation * 0.1 + rotationVariance * 0.9); // Slight directional bias for others\n } else if (rotationMode === 'tangent') {\n // Tangent rotation: align image to spiral curve\n rotation = this.calculateSpiralTangent(angle, radius, spiralConfig);\n }\n\n // Z-index: center images on top\n const zIndex = imageCount - i;\n\n layouts.push({\n id: i,\n x: clampedX,\n y: clampedY,\n rotation,\n scale: combinedScale,\n baseSize: scaledImageSize,\n zIndex\n });\n }\n\n return layouts;\n }\n\n /**\n * Calculate tangent angle for spiral curve at given position\n * This aligns the image along the spiral's direction of travel\n */\n private calculateSpiralTangent(\n angle: number,\n radius: number,\n spiralConfig: SpiralAlgorithmConfig\n ): number {\n // For different spiral types, the tangent calculation varies\n // The tangent angle is the derivative of the spiral equation\n\n let tangentAngle: number;\n\n if (spiralConfig.spiralType === 'golden') {\n // Golden spiral tangent is approximately perpendicular to radial line\n // Plus a small offset based on the golden angle\n tangentAngle = angle + Math.PI / 2;\n } else if (spiralConfig.spiralType === 'archimedean') {\n // Archimedean spiral: dr/dθ = constant = b\n // tan(ψ) = r / (dr/dθ) = r / b\n // For tightness = 1, b ≈ 1\n const b = 1 / spiralConfig.tightness;\n const psi = Math.atan(radius / b);\n tangentAngle = angle + psi;\n } else {\n // Logarithmic spiral: tangent makes constant angle with radial line\n // This angle depends on the growth rate b\n const b = 0.15 / spiralConfig.tightness;\n const psi = Math.atan(1 / b); // Constant pitch angle\n tangentAngle = angle + psi;\n }\n\n // Convert to degrees and normalize\n const degrees = (tangentAngle * 180 / Math.PI) % 360;\n\n // Adjust so 0 degrees means pointing right\n return degrees - 90;\n }\n\n /**\n * Calculate radius for golden spiral (Vogel's model)\n * Creates even distribution like sunflower seeds\n */\n private calculateGoldenRadius(\n index: number,\n totalImages: number,\n maxRadius: number,\n tightness: number\n ): number {\n // Vogel's model: r = c * sqrt(n)\n // Scaling factor to fit within maxRadius\n const scalingFactor = maxRadius / Math.sqrt(totalImages);\n const radius = scalingFactor * Math.sqrt(index) / tightness;\n return Math.min(radius, maxRadius);\n }\n\n /**\n * Calculate radius for Archimedean spiral\n * r = a + b*θ (constant spacing between arms)\n */\n private calculateArchimedeanRadius(\n theta: number,\n totalImages: number,\n maxRadius: number,\n tightness: number\n ): number {\n // Scale so that the last point is at maxRadius\n const maxTheta = totalImages * 0.5 * tightness;\n const normalizedTheta = theta / maxTheta;\n return normalizedTheta * maxRadius;\n }\n\n /**\n * Calculate radius for logarithmic (equiangular) spiral\n * r = a * e^(b*θ)\n */\n private calculateLogarithmicRadius(\n theta: number,\n totalImages: number,\n maxRadius: number,\n tightness: number\n ): number {\n // Parameters for logarithmic spiral\n const a = maxRadius * 0.05; // Starting radius\n const b = 0.15 / tightness; // Growth rate\n\n const radius = a * Math.exp(b * theta);\n\n // Normalize to fit within maxRadius\n const maxTheta = totalImages * 0.3 * tightness;\n const maxComputedRadius = a * Math.exp(b * maxTheta);\n\n return (radius / maxComputedRadius) * maxRadius;\n }\n\n /**\n * Utility: Generate random number between min and max\n */\n private random(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n }\n}\n","/**\n * ClusterPlacementLayout.ts\n * Generates organic cluster layouts with natural groupings\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, ClusterAlgorithmConfig, ImageConfig } from '../config/types';\n\ninterface ClusterCenter {\n x: number;\n y: number;\n spread: number; // Actual spread for this cluster (may vary if density='varied')\n}\n\ninterface ClusterLayoutOptions extends Partial<LayoutConfig> {\n fixedHeight?: number;\n}\n\nconst DEFAULT_CLUSTER_CONFIG: ClusterAlgorithmConfig = {\n clusterCount: 'auto',\n clusterSpread: 150,\n clusterSpacing: 200,\n density: 'uniform',\n overlap: 0.3,\n distribution: 'gaussian'\n};\n\nexport class ClusterPlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n\n constructor(config: LayoutConfig, imageConfig: ImageConfig = {}) {\n this.config = config;\n this.imageConfig = imageConfig;\n }\n\n /**\n * Generate cluster layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides (includes fixedHeight)\n * @returns Array of layout objects with position, rotation, scale\n */\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options: ClusterLayoutOptions = {}\n ): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n\n const clusterConfig = { ...DEFAULT_CLUSTER_CONFIG, ...this.config.cluster };\n const padding = this.config.spacing.padding;\n // Use fixedHeight if provided, otherwise use default 200\n const baseImageSize = options.fixedHeight ?? 200;\n\n // Get rotation config from image config\n const rotationMode = this.imageConfig.rotation?.mode ?? 'none';\n const minRotation = this.imageConfig.rotation?.range?.min ?? -15;\n const maxRotation = this.imageConfig.rotation?.range?.max ?? 15;\n\n // Get variance config from image config\n const varianceMin = this.imageConfig.sizing?.variance?.min ?? 1.0;\n const varianceMax = this.imageConfig.sizing?.variance?.max ?? 1.0;\n const hasVariance = varianceMin !== 1.0 || varianceMax !== 1.0;\n\n // Calculate number of clusters\n const clusterCount = this.calculateClusterCount(\n imageCount,\n clusterConfig.clusterCount,\n width,\n height,\n clusterConfig.clusterSpacing\n );\n\n // Generate cluster centers with spacing constraints\n const clusterCenters = this.generateClusterCenters(\n clusterCount,\n width,\n height,\n padding,\n clusterConfig\n );\n\n // Assign images to clusters (round-robin for even distribution)\n const imagesPerCluster = new Array(clusterCount).fill(0);\n for (let i = 0; i < imageCount; i++) {\n imagesPerCluster[i % clusterCount]++;\n }\n\n let imageIndex = 0;\n\n // Place images in each cluster\n for (let clusterIdx = 0; clusterIdx < clusterCount; clusterIdx++) {\n const cluster = clusterCenters[clusterIdx];\n const imagesInThisCluster = imagesPerCluster[clusterIdx];\n\n for (let i = 0; i < imagesInThisCluster; i++) {\n // Calculate position within cluster\n let offsetX: number;\n let offsetY: number;\n\n if (clusterConfig.distribution === 'gaussian') {\n // Gaussian distribution - most images near center, fewer at edges\n offsetX = this.gaussianRandom() * cluster.spread;\n offsetY = this.gaussianRandom() * cluster.spread;\n } else {\n // Uniform distribution within circle\n const angle = this.random(0, Math.PI * 2);\n const distance = this.random(0, cluster.spread);\n offsetX = Math.cos(angle) * distance;\n offsetY = Math.sin(angle) * distance;\n }\n\n // Apply overlap factor - pulls images closer to center and increases size\n const overlapMultiplier = 1 + clusterConfig.overlap * 0.5;\n const sizeMultiplier = 1 + clusterConfig.overlap * 0.3;\n\n // Reduce offset based on overlap (images cluster tighter)\n offsetX /= overlapMultiplier;\n offsetY /= overlapMultiplier;\n\n // Apply variance\n const varianceScale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const combinedScale = sizeMultiplier * varianceScale;\n\n // Calculate image size with overlap factor and variance\n const imageSize = baseImageSize * combinedScale;\n\n // Store center position (not top-left)\n let x = cluster.x + offsetX;\n let y = cluster.y + offsetY;\n\n // Boundary clamping with center position\n // Use 16:9 aspect ratio (1.78) as maximum to handle most landscape images\n const estAspectRatio = 1.5; // 3:2 - balanced for mixed portrait/landscape\n const halfWidth = (imageSize * estAspectRatio) / 2;\n const halfHeight = imageSize / 2;\n x = Math.max(padding + halfWidth, Math.min(x, width - padding - halfWidth));\n y = Math.max(padding + halfHeight, Math.min(y, height - padding - halfHeight));\n\n // Rotation - more variance for organic feel (only when mode is random)\n const rotation = rotationMode === 'random' ? this.random(minRotation, maxRotation) : 0;\n\n // Z-index: images closer to cluster center are on top\n const distanceFromCenter = Math.sqrt(offsetX * offsetX + offsetY * offsetY);\n const normalizedDistance = distanceFromCenter / cluster.spread;\n const zIndex = Math.round((1 - normalizedDistance) * 50) + 1;\n\n layouts.push({\n id: imageIndex,\n x,\n y,\n rotation,\n scale: combinedScale,\n baseSize: imageSize,\n zIndex\n });\n\n imageIndex++;\n }\n }\n\n return layouts;\n }\n\n /**\n * Calculate optimal number of clusters based on image count and container\n */\n private calculateClusterCount(\n imageCount: number,\n configCount: number | 'auto',\n width: number,\n height: number,\n clusterSpacing: number\n ): number {\n if (configCount !== 'auto') {\n return Math.max(1, Math.min(configCount, imageCount));\n }\n\n // Auto-calculate based on container size and image count\n // Aim for 5-15 images per cluster\n const idealImagesPerCluster = 8;\n const countByImages = Math.max(1, Math.ceil(imageCount / idealImagesPerCluster));\n\n // Also consider container size - how many clusters can fit\n const countBySpace = Math.floor(\n (width / clusterSpacing) * (height / clusterSpacing) * 0.6\n );\n\n return Math.max(1, Math.min(countByImages, countBySpace, 10));\n }\n\n /**\n * Generate cluster center positions with spacing constraints\n */\n private generateClusterCenters(\n count: number,\n width: number,\n height: number,\n padding: number,\n config: ClusterAlgorithmConfig\n ): ClusterCenter[] {\n const centers: ClusterCenter[] = [];\n const maxAttempts = 100;\n\n // Available area for cluster centers\n const minX = padding + config.clusterSpread;\n const maxX = width - padding - config.clusterSpread;\n const minY = padding + config.clusterSpread;\n const maxY = height - padding - config.clusterSpread;\n\n for (let i = 0; i < count; i++) {\n let bestCandidate: ClusterCenter | null = null;\n let bestMinDistance = -1;\n\n // Try multiple random positions and pick the one with best spacing\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const candidate = {\n x: this.random(minX, maxX),\n y: this.random(minY, maxY),\n spread: this.calculateClusterSpread(config)\n };\n\n // Calculate minimum distance to existing clusters\n let minDistance = Infinity;\n for (const existing of centers) {\n const dx = candidate.x - existing.x;\n const dy = candidate.y - existing.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n minDistance = Math.min(minDistance, distance);\n }\n\n // First cluster or better spacing\n if (centers.length === 0 || minDistance > bestMinDistance) {\n bestCandidate = candidate;\n bestMinDistance = minDistance;\n }\n\n // Good enough spacing found\n if (minDistance >= config.clusterSpacing) {\n break;\n }\n }\n\n if (bestCandidate) {\n centers.push(bestCandidate);\n }\n }\n\n return centers;\n }\n\n /**\n * Calculate spread for a cluster (may vary if density='varied')\n */\n private calculateClusterSpread(config: ClusterAlgorithmConfig): number {\n if (config.density === 'uniform') {\n return config.clusterSpread;\n }\n\n // Varied density: spread varies between 50% and 150% of config value\n return config.clusterSpread * this.random(0.5, 1.5);\n }\n\n /**\n * Generate a random number with approximately Gaussian distribution\n * Using Box-Muller transform\n */\n private gaussianRandom(): number {\n let u = 0, v = 0;\n while (u === 0) u = Math.random();\n while (v === 0) v = Math.random();\n\n const value = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);\n\n // Clamp to reasonable range (-3 to 3 std deviations)\n return Math.max(-3, Math.min(3, value)) / 3;\n }\n\n /**\n * Utility: Generate random number between min and max\n */\n private random(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n }\n}\n","/**\n * WavePlacementLayout.ts\n * Generates wave-based layouts for image cloud\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, ImageConfig } from '../config/types';\nimport { DEFAULT_WAVE_CONFIG } from '../config/defaults';\n\ninterface WaveLayoutOptions extends Partial<LayoutConfig> {\n fixedHeight?: number;\n}\n\nexport class WavePlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n\n constructor(config: LayoutConfig, imageConfig: ImageConfig = {}) {\n this.config = config;\n this.imageConfig = imageConfig;\n }\n\n /**\n * Generate wave layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides\n * @returns Array of layout objects with position, rotation, scale\n */\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options: WaveLayoutOptions = {}\n ): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n // Use fixedHeight if provided, otherwise use default 200\n const baseImageSize = options.fixedHeight ?? 200;\n const padding = this.config.spacing.padding ?? 50;\n\n // Get rotation config from image config\n const rotationMode = this.imageConfig.rotation?.mode ?? 'none';\n const minRotation = this.imageConfig.rotation?.range?.min ?? -15;\n const maxRotation = this.imageConfig.rotation?.range?.max ?? 15;\n\n // Get variance config from image config\n const varianceMin = this.imageConfig.sizing?.variance?.min ?? 1.0;\n const varianceMax = this.imageConfig.sizing?.variance?.max ?? 1.0;\n const hasVariance = varianceMin !== 1.0 || varianceMax !== 1.0;\n\n // Use override fixedHeight if provided, else baseImageSize\n const imageSize = options.fixedHeight ?? baseImageSize;\n\n // Get wave configuration, merging user config with defaults\n const waveConfig = {\n ...DEFAULT_WAVE_CONFIG,\n ...this.config.wave\n };\n\n const { rows, amplitude, frequency, phaseShift, synchronization } = waveConfig;\n\n // Calculate images per row (distribute evenly)\n const imagesPerRow = Math.ceil(imageCount / rows);\n\n // Estimate image width based on height and typical aspect ratio\n const estAspectRatio = 1.5; // 3:2 - balanced for mixed portrait/landscape\n const estImageWidth = imageSize * estAspectRatio;\n const halfImageWidth = estImageWidth / 2;\n\n // Calculate available horizontal space (accounting for image width at edges)\n // This ensures images don't get compressed against the edges\n const startX = padding + halfImageWidth;\n const endX = width - padding - halfImageWidth;\n const availableWidth = endX - startX;\n\n // Distribute images evenly between startX and endX\n const horizontalSpacing = imagesPerRow > 1 ? availableWidth / (imagesPerRow - 1) : 0;\n\n // Calculate vertical distribution to fill the screen\n // Row centerlines need room for amplitude swing above and below\n const minCenterY = padding + amplitude + (imageSize / 2);\n const maxCenterY = height - padding - amplitude - (imageSize / 2);\n const availableHeight = maxCenterY - minCenterY;\n\n // Calculate spacing between row centerlines\n const rowSpacing = rows > 1 ? availableHeight / (rows - 1) : 0;\n\n let imageIndex = 0;\n\n for (let rowIndex = 0; rowIndex < rows && imageIndex < imageCount; rowIndex++) {\n // Calculate base Y position (centerline) for this row - evenly distributed\n const baseY = rows === 1\n ? (minCenterY + maxCenterY) / 2 // Single row: center vertically\n : minCenterY + (rowIndex * rowSpacing);\n\n // Calculate phase offset based on synchronization mode\n let phase = 0;\n if (synchronization === 'offset') {\n phase = rowIndex * phaseShift;\n } else if (synchronization === 'alternating') {\n phase = rowIndex * Math.PI;\n }\n // If 'synchronized', phase remains 0\n\n // Place images along this wave row\n for (let imgInRow = 0; imgInRow < imagesPerRow && imageIndex < imageCount; imgInRow++) {\n // Calculate center position - evenly distributed within available space\n // For single image, center it; otherwise distribute from startX to endX\n const centerX = imagesPerRow === 1\n ? (startX + endX) / 2\n : startX + (imgInRow * horizontalSpacing);\n\n // Calculate wave displacement based on center position\n const waveY = this.calculateWaveY(centerX, width, amplitude, frequency, phase);\n\n // Store center position directly (CSS transform will handle centering)\n const x = centerX;\n const y = baseY + waveY;\n\n // Apply variance\n const varianceScale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const scaledImageSize = imageSize * varianceScale;\n\n // Calculate rotation based on image.rotation.mode\n let rotation = 0;\n if (rotationMode === 'tangent') {\n // Follow wave tangent - images rotate to align with wave curve\n rotation = this.calculateRotation(centerX, width, amplitude, frequency, phase);\n } else if (rotationMode === 'random') {\n // Random rotation within configured range\n rotation = this.random(minRotation, maxRotation);\n }\n // If mode is 'none', rotation stays 0\n\n // Clamp center positions to keep images within bounds\n // Use 16:9 aspect ratio (1.78) as maximum to handle most landscape images\n const estAspectRatio = 1.5; // 3:2 - balanced for mixed portrait/landscape\n const halfWidth = (scaledImageSize * estAspectRatio) / 2;\n const halfHeight = scaledImageSize / 2;\n const minX = padding + halfWidth;\n const maxX = width - padding - halfWidth;\n const minY = padding + halfHeight;\n const maxY = height - padding - halfHeight;\n\n layouts.push({\n id: imageIndex,\n x: Math.max(minX, Math.min(x, maxX)),\n y: Math.max(minY, Math.min(y, maxY)),\n rotation,\n scale: varianceScale,\n baseSize: scaledImageSize,\n zIndex: imageIndex + 1\n });\n\n imageIndex++;\n }\n }\n\n return layouts;\n }\n\n /**\n * Calculate Y position displacement on wave curve\n * @param x - Horizontal position\n * @param containerWidth - Container width\n * @param amplitude - Wave amplitude\n * @param frequency - Wave frequency\n * @param phase - Phase offset\n * @returns Y displacement from baseline\n */\n private calculateWaveY(\n x: number,\n containerWidth: number,\n amplitude: number,\n frequency: number,\n phase: number\n ): number {\n // Normalize x to [0, 1] range\n const normalizedX = x / containerWidth;\n\n // Calculate wave: y = amplitude * sin(frequency * x * 2π + phase)\n return amplitude * Math.sin(frequency * normalizedX * 2 * Math.PI + phase);\n }\n\n /**\n * Calculate rotation based on wave tangent\n * @param x - Horizontal position\n * @param containerWidth - Container width\n * @param amplitude - Wave amplitude\n * @param frequency - Wave frequency\n * @param phase - Phase offset\n * @returns Rotation angle in degrees\n */\n private calculateRotation(\n x: number,\n containerWidth: number,\n amplitude: number,\n frequency: number,\n phase: number\n ): number {\n // Normalize x to [0, 1] range\n const normalizedX = x / containerWidth;\n\n // Derivative of sine wave: amplitude * frequency * cos(frequency * x * 2π + phase)\n const derivative = amplitude * frequency * 2 * Math.PI * Math.cos(frequency * normalizedX * 2 * Math.PI + phase) / containerWidth;\n\n // Convert derivative to rotation angle in degrees\n return Math.atan(derivative) * (180 / Math.PI);\n }\n\n /**\n * Estimate image width based on height\n /**\n * Utility: Generate random number between min and max\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random number in range\n */\n private random(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n }\n}\n","/**\n * hexagonGeometry.ts\n * Single source of truth for hexagonal grid math.\n * Used by clipPathGenerator.ts and HoneycombPlacementLayout.ts.\n */\n\n/** Reference height for the canonical hexagon definition (100px) */\nexport const HEXAGON_REF_HEIGHT = 100;\n\n/**\n * Regular flat-top hexagon reference points (refHeight = 100).\n * Circumradius r = 100/√3 so that height = r√3 = 100 exactly.\n * Width = 2r = 200/√3 ≈ 115.47. All 6 vertices are equidistant from center.\n *\n * x = r/2 = 50/√3 ≈ 28.87 (upper/lower inner)\n * x = 3r/2 = 150/√3 ≈ 86.60 (upper/lower outer)\n * x = 2r = 200/√3 ≈ 115.47 (mid right vertex)\n */\nconst _R = 100 / Math.sqrt(3);\nexport const HEXAGON_REF_POINTS: Array<[number, number]> = [\n [_R / 2, 0 ], // upper-left\n [3 * _R / 2, 0 ], // upper-right\n [2 * _R, 50 ], // right ← tiling key vertex\n [3 * _R / 2, 100], // lower-right\n [_R / 2, 100], // lower-left\n [0, 50 ], // left\n];\n\n/**\n * Col-step ratio = x of upper-right vertex / refHeight = (3r/2) / 100 = √3/2 ≈ 0.866.\n * (Previously 0.75 with the non-regular hexagon.)\n */\nexport const HEXAGON_COL_STEP_RATIO = HEXAGON_REF_POINTS[1][0] / HEXAGON_REF_HEIGHT; // √3/2 ≈ 0.866\n\n/** Row-offset ratio = y of right vertex / refHeight = 50/100 = 0.50 (unchanged) */\nexport const HEXAGON_ROW_OFFSET_RATIO = HEXAGON_REF_POINTS[2][1] / HEXAGON_REF_HEIGHT; // 0.50\n\n/**\n * Returns tiling parameters for a hexagon of the given height.\n * Derived from the canonical reference points, not hardcoded constants.\n */\nexport function getHexTilingParams(hexH: number): { colStep: number; rowOffset: number } {\n return {\n colStep: HEXAGON_COL_STEP_RATIO * hexH,\n rowOffset: HEXAGON_ROW_OFFSET_RATIO * hexH,\n };\n}\n\n/**\n * Converts cube coordinates (cx, cy, cz) to pixel center position.\n * @param cx - Cube coord x (cx + cy + cz = 0)\n * @param cy - Cube coord y\n * @param cz - Cube coord z\n * @param originX - Pixel origin X (container center)\n * @param originY - Pixel origin Y (container center)\n * @param hexH - Visual hex height (= imageHeight for height-relative clip path)\n */\nexport function hexCubeToPixel(\n cx: number, cy: number, _cz: number,\n originX: number, originY: number,\n hexH: number\n): { px: number; py: number } {\n const { colStep } = getHexTilingParams(hexH);\n return {\n px: originX + colStep * cx,\n py: originY + hexH * (cy + cx / 2),\n };\n}\n\n/** 6 cube direction vectors for clockwise ring traversal starting from top */\nexport const HEX_RING_DIRECTIONS: Array<[number, number, number]> = [\n [+1, 0, -1],\n [ 0, +1, -1],\n [-1, +1, 0],\n [-1, 0, +1],\n [ 0, -1, +1],\n [+1, -1, 0],\n];\n\n/**\n * Returns ordered cube coordinates for all cells in ring k, clockwise from the top.\n * Ring 0: [(0,0,0)]. Ring k: 6k cells.\n */\nexport function getHexRingCells(ring: number): Array<[number, number, number]> {\n if (ring === 0) return [[0, 0, 0]];\n const cells: Array<[number, number, number]> = [];\n let [cx, cy, cz] = [0, -ring, ring]; // start at top\n for (const [dx, dy, dz] of HEX_RING_DIRECTIONS) {\n for (let step = 0; step < ring; step++) {\n cells.push([cx, cy, cz]);\n cx += dx; cy += dy; cz += dz;\n }\n }\n return cells;\n}\n","/**\n * HoneycombPlacementLayout.ts\n * Places images in hexagonal rings, filling outward clockwise from center-top.\n * Default/hover clip paths are forced to hexagon height-relative by mergeConfig().\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, ImageConfig } from '../config/types';\nimport { DEFAULT_HONEYCOMB_CONFIG } from '../config/defaults';\nimport { getHexRingCells, hexCubeToPixel } from '../utils/hexagonGeometry';\n\nexport class HoneycombPlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n\n // imageConfig intentionally not stored — honeycomb forces uniform sizing (rotation/variance\n // would break hex tiling). Kept as parameter for interface compatibility.\n constructor(config: LayoutConfig, _imageConfig: ImageConfig = {}) {\n this.config = config;\n }\n\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options: Partial<LayoutConfig> & { fixedHeight?: number } = {}\n ): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n const containerCX = width / 2;\n const containerCY = height / 2;\n\n const baseImageSize = options.fixedHeight ?? 200;\n\n // Merge honeycomb config with defaults\n const honeycombConfig = {\n ...DEFAULT_HONEYCOMB_CONFIG,\n ...this.config.honeycomb\n };\n\n const spacing = honeycombConfig.spacing ?? 0;\n\n // hexH is the pitch (hex height + gap). Since the clip path is height-relative,\n // the hex's visual height equals baseImageSize. We add spacing on top.\n // Note: baseImageSize is already capped to fit the container by LayoutEngine.calculateAdaptiveSize().\n const hexH = baseImageSize + spacing;\n\n let placed = 0;\n let ring = 0;\n\n while (placed < imageCount) {\n const cells = getHexRingCells(ring);\n\n for (const [cx, cy, cz] of cells) {\n if (placed >= imageCount) break;\n\n const { px, py } = hexCubeToPixel(cx, cy, cz, containerCX, containerCY, hexH);\n\n layouts.push({\n id: placed,\n x: px,\n y: py,\n rotation: 0,\n scale: 1.0,\n baseSize: baseImageSize,\n // Inner rings render above outer rings\n zIndex: Math.max(1, 100 - ring)\n });\n\n placed++;\n }\n\n ring++;\n }\n\n return layouts;\n }\n}\n","/**\n * LayoutEngine.ts\n * Generates layouts for image cloud using placement strategies\n *\n * Public API:\n * - generateLayout(imageCount, containerBounds, options)\n * - getOriginalState(imageId)\n * - reset()\n * - updateConfig(newConfig)\n */\n\nimport type { LayoutConfig, ImageLayout, ContainerBounds, PlacementLayout, AdaptiveSizingResult, ImageConfig, FixedModeHeight, ResponsiveBreakpoints } from '../config/types';\nimport { RandomPlacementLayout } from '../layouts/RandomPlacementLayout';\nimport { RadialPlacementLayout } from '../layouts/RadialPlacementLayout';\nimport { GridPlacementLayout } from '../layouts/GridPlacementLayout';\nimport { SpiralPlacementLayout } from '../layouts/SpiralPlacementLayout';\nimport { ClusterPlacementLayout } from '../layouts/ClusterPlacementLayout';\nimport { WavePlacementLayout } from '../layouts/WavePlacementLayout';\nimport { HoneycombPlacementLayout } from '../layouts/HoneycombPlacementLayout';\n\nexport interface LayoutEngineConfig {\n layout: LayoutConfig;\n image: ImageConfig;\n}\n\nexport class LayoutEngine {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n private layouts: Map<number, ImageLayout>;\n private placementLayout: PlacementLayout;\n\n constructor(config: LayoutEngineConfig) {\n this.config = config.layout;\n this.imageConfig = config.image;\n\n this.layouts = new Map(); // Store original states by image ID\n\n // Initialize placement layout strategy\n this.placementLayout = this.initLayout();\n }\n\n /**\n * Initialize the appropriate placement layout based on config type\n * @returns Initialized placement layout\n */\n private initLayout(): PlacementLayout {\n switch (this.config.algorithm) {\n case 'radial':\n return new RadialPlacementLayout(this.config, this.imageConfig);\n case 'grid':\n return new GridPlacementLayout(this.config, this.imageConfig);\n case 'spiral':\n return new SpiralPlacementLayout(this.config, this.imageConfig);\n case 'cluster':\n return new ClusterPlacementLayout(this.config, this.imageConfig);\n case 'wave':\n return new WavePlacementLayout(this.config, this.imageConfig);\n case 'honeycomb':\n return new HoneycombPlacementLayout(this.config, this.imageConfig);\n case 'random':\n default:\n return new RandomPlacementLayout(this.config, this.imageConfig);\n }\n }\n\n /**\n * Generate layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides for configuration (e.g. fixedHeight)\n * @returns Array of layout objects with position, rotation, scale\n */\n generateLayout(imageCount: number, containerBounds: ContainerBounds, options: Partial<LayoutConfig> = {}): ImageLayout[] {\n const layouts = this.placementLayout.generate(imageCount, containerBounds, options);\n\n // Store layouts for state retrieval\n layouts.forEach(layout => {\n this.layouts.set(layout.id, layout);\n });\n\n return layouts;\n }\n\n /**\n * Get the original layout state for an image\n * @param imageId - The image ID (number or string)\n * @returns Original layout state or undefined if not found\n */\n getOriginalState(imageId: number | string): ImageLayout | undefined {\n return this.layouts.get(Number(imageId));\n }\n\n /**\n * Reset all stored layouts\n */\n reset(): void {\n this.layouts.clear();\n }\n\n /**\n * Update config dynamically (useful for responsive changes)\n * @param newConfig - Updated configuration\n */\n updateConfig(newConfig: Partial<LayoutEngineConfig>): void {\n // Update layout config\n if (newConfig.layout) {\n Object.assign(this.config, newConfig.layout);\n\n // Reinitialize placement layout if algorithm changed\n if (newConfig.layout.algorithm && newConfig.layout.algorithm !== this.config.algorithm) {\n this.placementLayout = this.initLayout();\n }\n }\n\n // Update image config\n if (newConfig.image) {\n Object.assign(this.imageConfig, newConfig.image);\n }\n }\n\n /**\n * Get responsive breakpoints from layout config\n */\n private getBreakpoints(): ResponsiveBreakpoints {\n return this.config.responsive ?? {\n mobile: { maxWidth: 767 },\n tablet: { maxWidth: 1199 }\n };\n }\n\n /**\n * Resolve breakpoint name based on viewport width\n */\n resolveBreakpoint(viewportWidth: number): 'mobile' | 'tablet' | 'screen' {\n const breakpoints = this.getBreakpoints();\n\n if (viewportWidth <= breakpoints.mobile.maxWidth) {\n return 'mobile';\n }\n if (viewportWidth <= breakpoints.tablet.maxWidth) {\n return 'tablet';\n }\n return 'screen';\n }\n\n /**\n * Resolve the effective base height based on image config and current viewport\n * @param viewportWidth - Current viewport width\n * @returns Resolved base height or undefined if should auto-calculate (adaptive mode)\n */\n resolveBaseHeight(viewportWidth: number): number | undefined {\n const sizing = this.imageConfig.sizing;\n\n // If mode is adaptive (or not set), return undefined to signal auto-calculation\n if (!sizing || sizing.mode === 'adaptive') {\n return undefined;\n }\n\n // Fixed or responsive mode - use the height property\n const height = sizing.height;\n\n if (height === undefined) {\n return undefined; // No height specified, fall back to adaptive\n }\n\n if (typeof height === 'number') {\n return height;\n }\n\n // Responsive height for fixed mode\n const responsiveHeight = height as FixedModeHeight;\n const breakpoint = this.resolveBreakpoint(viewportWidth);\n\n // Fallback chain: specific breakpoint -> higher breakpoints -> any defined\n if (breakpoint === 'mobile') {\n return responsiveHeight.mobile ?? responsiveHeight.tablet ?? responsiveHeight.screen;\n }\n if (breakpoint === 'tablet') {\n return responsiveHeight.tablet ?? responsiveHeight.screen ?? responsiveHeight.mobile;\n }\n // screen\n return responsiveHeight.screen ?? responsiveHeight.tablet ?? responsiveHeight.mobile;\n }\n\n /**\n * Calculate adaptive image size based on container dimensions and image count\n * @param containerBounds - Container dimensions {width, height}\n * @param imageCount - Number of images to display\n * @param maxHeight - Maximum height constraint (upper bound)\n * @param viewportWidth - Current viewport width for baseHeight resolution\n * @returns Calculated sizing result with height\n */\n calculateAdaptiveSize(\n containerBounds: ContainerBounds,\n imageCount: number,\n maxHeight: number,\n viewportWidth: number\n ): AdaptiveSizingResult {\n const sizing = this.imageConfig.sizing;\n\n // Check if user specified a fixed height in image config\n const userBaseHeight = this.resolveBaseHeight(viewportWidth);\n\n // If user specified baseHeight (fixed/responsive mode), use it directly\n // Don't clamp to maxHeight since user explicitly chose this value\n if (userBaseHeight !== undefined) {\n return { height: userBaseHeight };\n }\n\n // Adaptive mode - auto-calculate based on container and image count\n const minSize = sizing?.minSize ?? 50;\n const maxSize = sizing?.maxSize ?? 400;\n const targetCoverage = this.config.targetCoverage ?? 0.6;\n const densityFactor = this.config.densityFactor ?? 1.0;\n\n const { width, height } = containerBounds;\n\n // Calculate area-based optimal size\n const containerArea = width * height;\n const targetArea = containerArea * targetCoverage;\n const areaPerImage = targetArea / imageCount;\n\n // Calculate height from area assuming 1.4 aspect ratio (landscape images)\n const aspectRatio = 1.4;\n let calculatedHeight = Math.sqrt(areaPerImage / aspectRatio);\n\n // Apply density factor\n calculatedHeight *= densityFactor;\n\n // Clamp to maximum height constraint\n calculatedHeight = Math.min(calculatedHeight, maxHeight);\n\n // Apply min/max constraints\n let finalHeight = this.clamp(calculatedHeight, minSize, maxSize);\n\n // 'minimize' behavior: force fit below minimum if needed (0.05 floor)\n if (finalHeight === minSize && calculatedHeight < minSize) {\n // Minimum floor is 5% of calculated size\n const floor = Math.max(minSize * 0.05, 20);\n finalHeight = Math.max(floor, calculatedHeight);\n }\n\n // Honeycomb: cap so the outermost ring stays within the container.\n // Must apply here (not in generate()) so img.style.height matches tiling pitch.\n if (this.config.algorithm === 'honeycomb') {\n finalHeight = Math.min(finalHeight, this.honeycombMaxImageHeight(imageCount, containerBounds));\n }\n\n return { height: finalHeight };\n }\n\n /**\n * Returns the largest image height at which all honeycomb rings fit within the container.\n * Spacing is 0 for this calculation (user spacing is additive on top of the image height;\n * any non-zero spacing only makes the constraint tighter).\n */\n private honeycombMaxImageHeight(imageCount: number, containerBounds: ContainerBounds): number {\n if (imageCount <= 1) return Infinity;\n\n // Find outermost ring: cells in rings 0..k = 1 + 3k(k+1)\n let maxRing = 0;\n let total = 1;\n while (total < imageCount) {\n maxRing++;\n total += 6 * maxRing;\n }\n\n const padding = this.config.spacing?.padding ?? 50;\n const spacing = this.config.honeycomb?.spacing ?? 0;\n const containerCX = containerBounds.width / 2;\n const containerCY = containerBounds.height / 2;\n\n // colStep = √3/2 ≈ 0.866 * hexH; hex half-width = 1/√3 ≈ 0.577 * imageSize\n const COL_STEP_RATIO = Math.sqrt(3) / 2;\n const HALF_WIDTH_RATIO = 1 / Math.sqrt(3);\n\n // Vertical: containerCY - maxRing*(size+spacing) - size/2 >= padding\n const maxV = (containerCY - padding - spacing * maxRing) / (maxRing + 0.5);\n\n // Horizontal: containerCX - COL_STEP_RATIO*maxRing*(size+spacing) - HALF_WIDTH_RATIO*size >= padding\n const maxH = (containerCX - padding - COL_STEP_RATIO * spacing * maxRing)\n / (COL_STEP_RATIO * maxRing + HALF_WIDTH_RATIO);\n\n return Math.max(10, Math.min(maxV, maxH));\n }\n\n /**\n * Utility: Clamp a value between min and max\n */\n private clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n }\n}","/**\n * Type definitions for Image Gallery Library\n */\n\n// ============================================================================\n// Core Data Types\n// ============================================================================\n\nexport interface ImageLayout {\n id: number;\n x: number;\n y: number;\n rotation: number;\n scale: number;\n baseSize: number;\n zIndex?: number;\n}\n\nexport interface ContainerBounds {\n width: number;\n height: number;\n}\n\nexport interface ResponsiveHeight {\n minWidth: number;\n height: number;\n}\n\nexport interface TransformParams {\n x?: number;\n y?: number;\n rotation?: number;\n scale?: number;\n}\n\n// ============================================================================\n// Loader Configuration\n// ============================================================================\n\n// === Static source types (shape-based, no 'type' discriminant) ===\n\nexport interface StaticUrlsSource {\n urls: string[];\n}\n\nexport interface StaticPathSource {\n path: string;\n files: string[];\n}\n\nexport interface StaticJsonSource {\n json: string;\n}\n\nexport type StaticSource = StaticUrlsSource | StaticPathSource | StaticJsonSource;\n\n// === Google Drive source types (shape-based) ===\n\nexport interface GoogleDriveFolderSource {\n folders: string[];\n recursive?: boolean;\n}\n\nexport interface GoogleDriveFilesSource {\n files: string[];\n}\n\nexport type GoogleDriveSource = GoogleDriveFolderSource | GoogleDriveFilesSource;\n\n// === Loader configs (inner config objects) ===\n\nexport interface StaticLoaderInnerConfig {\n sources: StaticSource[];\n validateUrls?: boolean;\n validationTimeout?: number;\n validationMethod?: 'head' | 'simple' | 'none';\n allowedExtensions?: string[];\n debugLogging?: boolean;\n}\n\nexport interface GoogleDriveLoaderInnerConfig {\n apiKey: string;\n sources: GoogleDriveSource[];\n apiEndpoint?: string;\n allowedExtensions?: string[];\n debugLogging?: boolean;\n}\n\n// === Loader entries (key-based identification) ===\n\nexport interface StaticLoaderEntry {\n static: StaticLoaderInnerConfig;\n}\n\nexport interface GoogleDriveLoaderEntry {\n googleDrive: GoogleDriveLoaderInnerConfig;\n}\n\nexport type LoaderEntry = StaticLoaderEntry | GoogleDriveLoaderEntry;\n\n// === Shared loader config ===\n\nexport interface SharedLoaderConfig {\n validateUrls?: boolean;\n validationTimeout?: number;\n validationMethod?: 'head' | 'simple' | 'none';\n allowedExtensions?: string[];\n}\n\n// === Debug configuration ===\n\nexport interface DebugConfig {\n enabled?: boolean; // General debug logging (was top-level `debug`)\n centers?: boolean; // Center position markers (was `layout.debugCenters`)\n loaders?: boolean; // Loader debug output (was `config.loaders.debugLogging`)\n}\n\n// === Config section ===\n\nexport interface ConfigSection {\n loaders?: SharedLoaderConfig;\n debug?: DebugConfig;\n}\n\n// ============================================================================\n// Image Configuration (sizing and rotation)\n// ============================================================================\n\n/**\n * Sizing mode:\n * - 'fixed': single explicit height for all breakpoints\n * - 'responsive': different heights per breakpoint (mobile/tablet/screen)\n * - 'adaptive': auto-calculates based on container size and image count\n */\nexport type SizingMode = 'fixed' | 'responsive' | 'adaptive';\n\n/**\n * Fixed mode height configuration with responsive breakpoints\n * At least one of mobile, tablet, or screen is required\n */\nexport interface FixedModeHeight {\n mobile?: number; // Height for mobile viewports (< responsive.mobile.maxWidth)\n tablet?: number; // Height for tablet viewports (< responsive.tablet.maxWidth)\n screen?: number; // Height for desktop/large screens (>= responsive.tablet.maxWidth)\n}\n\n/**\n * Legacy responsive base height configuration (kept for backward compatibility)\n */\nexport interface ResponsiveBaseHeight {\n default: number; // Base height for large screens\n tablet?: number; // Height for tablet\n mobile?: number; // Height for mobile\n}\n\n/**\n * Image variance configuration\n * Controls random size variation applied to images\n */\nexport interface ImageVarianceConfig {\n min: number; // 0.25-1 (e.g., 0.8)\n max: number; // 1-1.75 (e.g., 1.2)\n}\n\n/**\n * Image sizing configuration\n */\nexport interface ImageSizingConfig {\n mode: SizingMode; // Required: 'fixed', 'responsive', or 'adaptive'\n height?: number | FixedModeHeight; // Fixed/responsive mode: explicit heights\n minSize?: number; // Adaptive mode only: minimum image height (default: 50)\n maxSize?: number; // Adaptive mode only: maximum image height (default: 400)\n variance?: ImageVarianceConfig; // Size variance (min: 0.25-1, max: 1-1.75)\n}\n\n/**\n * Image rotation mode\n */\nexport type ImageRotationMode = 'none' | 'random' | 'tangent';\n\n/**\n * Image rotation range configuration\n */\nexport interface ImageRotationRange {\n min: number; // Negative degrees (-180 to 0)\n max: number; // Positive degrees (0 to 180)\n}\n\n/**\n * Image rotation configuration\n */\nexport interface ImageRotationConfig {\n mode: ImageRotationMode; // default: 'none'\n range?: ImageRotationRange; // Range for random mode\n}\n\n/**\n * Combined image configuration\n */\nexport interface ImageConfig {\n sizing?: ImageSizingConfig;\n rotation?: ImageRotationConfig;\n}\n\n// ============================================================================\n// Layout Configuration\n// ============================================================================\n\nexport interface AdaptiveSizingResult {\n height: number; // Calculated image height\n}\n\n/**\n * Responsive breakpoints configuration for layout\n * Defines viewport width thresholds for mobile and tablet\n */\nexport interface ResponsiveBreakpoints {\n mobile: { maxWidth: number }; // Default: 767\n tablet: { maxWidth: number }; // Default: 1199\n // screen is implicitly > tablet.maxWidth\n}\n\n// Legacy responsive height uses the existing ResponsiveHeight interface (minWidth, height)\n\nexport interface LayoutSpacingConfig {\n padding: number;\n}\n\n// Legacy interface kept for backward compatibility in LayoutEngine\nexport interface LegacyLayoutRotationConfig {\n enabled: boolean;\n range: {\n min: number;\n max: number;\n };\n}\n\n// ============================================================================\n// Algorithm-Specific Configuration\n// ============================================================================\n\nexport interface GridAlgorithmConfig {\n columns: number | 'auto';\n rows: number | 'auto';\n stagger: 'none' | 'row' | 'column';\n jitter: number;\n overlap: number;\n fillDirection: 'row' | 'column';\n alignment: 'start' | 'center' | 'end';\n gap: number;\n overflowOffset: number; // 0-0.5, percentage of cell size for stacking overflow images (default: 0.25)\n}\n\nexport interface RadialAlgorithmConfig {\n tightness: number; // Ring spacing multiplier (default: 1.0). Higher = more spread, lower = tighter.\n}\n\nexport interface SpiralAlgorithmConfig {\n spiralType: 'golden' | 'archimedean' | 'logarithmic';\n direction: 'clockwise' | 'counterclockwise';\n tightness: number;\n scaleDecay: number;\n startAngle: number;\n}\n\nexport interface ClusterAlgorithmConfig {\n clusterCount: number | 'auto';\n clusterSpread: number;\n clusterSpacing: number;\n density: 'uniform' | 'varied';\n overlap: number;\n distribution: 'gaussian' | 'uniform';\n}\n\nexport interface WaveAlgorithmConfig {\n rows: number;\n amplitude: number;\n frequency: number;\n phaseShift: number;\n synchronization: 'offset' | 'synchronized' | 'alternating';\n // Note: Image rotation along wave is now controlled via image.rotation.mode = 'tangent'\n}\n\nexport type LayoutAlgorithm = 'random' | 'radial' | 'grid' | 'spiral' | 'cluster' | 'wave' | 'honeycomb';\n\nexport interface HoneycombAlgorithmConfig {\n spacing?: number; // extra gap in pixels beyond edge-to-edge (default: 0)\n}\n\nexport interface LayoutConfig {\n algorithm: LayoutAlgorithm;\n spacing: LayoutSpacingConfig;\n scaleDecay?: number; // For Radial/Spiral - progressive size reduction (0-1, default: 0)\n responsive?: ResponsiveBreakpoints; // Viewport width breakpoints (mobile/tablet)\n targetCoverage?: number; // 0-1, for adaptive sizing (default: 0.6)\n densityFactor?: number; // Controls center point spacing (default: 1.0)\n radial?: RadialAlgorithmConfig;\n grid?: GridAlgorithmConfig;\n spiral?: SpiralAlgorithmConfig;\n cluster?: ClusterAlgorithmConfig;\n wave?: WaveAlgorithmConfig;\n honeycomb?: HoneycombAlgorithmConfig;\n}\n\n// ============================================================================\n// Animation Configuration\n// ============================================================================\n\n// Entry Path Types\nexport type EntryPathType = 'linear' | 'arc' | 'bounce' | 'elastic' | 'wave';\n\nexport interface BouncePathConfig {\n overshoot: number; // 0.1-0.3, how far past target (default: 0.15)\n bounces: 1 | 2 | 3; // number of bounces before settling (default: 1)\n decayRatio: number; // 0.3-0.7, each bounce is this % of previous (default: 0.5)\n}\n\nexport type BouncePreset = 'energetic' | 'playful' | 'subtle';\n\nexport interface ElasticPathConfig {\n stiffness: number; // 100-500, higher = faster oscillation (default: 200)\n damping: number; // 10-50, higher = fewer oscillations (default: 20)\n mass: number; // 0.5-3, higher = slower, more momentum (default: 1)\n oscillations: number; // 2-5, visible oscillation count (default: 3)\n}\n\nexport type ElasticPreset = 'gentle' | 'bouncy' | 'wobbly' | 'snappy';\n\nexport interface WavePathConfig {\n amplitude: number; // 20-100px, wave height (default: 40)\n frequency: number; // 1-4, number of complete waves (default: 2)\n decay: boolean; // true = wave diminishes toward target (default: true)\n decayRate: number; // 0.5-1, how fast amplitude decreases (default: 0.8)\n phase: number; // 0-2π, starting phase offset (default: 0)\n}\n\nexport type WavePathPreset = 'gentle' | 'playful' | 'serpentine' | 'flutter';\n\nexport interface EntryPathConfig {\n type: EntryPathType;\n // Preset shortcuts (type-specific)\n bouncePreset?: BouncePreset;\n elasticPreset?: ElasticPreset;\n wavePreset?: WavePathPreset;\n // Type-specific detailed configs (override presets)\n bounce?: Partial<BouncePathConfig>;\n elastic?: Partial<ElasticPathConfig>;\n wave?: Partial<WavePathConfig>;\n}\n\n// Entry Rotation Types\nexport type EntryRotationMode = 'none' | 'settle' | 'spin' | 'wobble' | 'random';\n\nexport interface EntryRotationConfig {\n mode: EntryRotationMode;\n startRotation?: number | { min: number; max: number }; // For 'settle' mode\n spinCount?: number; // For 'spin' mode (default: 1)\n direction?: 'clockwise' | 'counterclockwise' | 'auto' | 'random'; // For 'spin' mode\n wobble?: {\n amplitude: number; // degrees of oscillation (default: 15)\n frequency: number; // oscillations during animation (default: 3)\n decay: boolean; // whether oscillation diminishes (default: true)\n };\n}\n\n// Entry Scale Types\nexport type EntryScaleMode = 'none' | 'grow' | 'shrink' | 'pop' | 'random';\n\nexport interface EntryScalePopConfig {\n overshoot: number; // How much to overshoot final scale (1.1-1.5, default: 1.2)\n bounces: number; // Number of bounces before settling (1-3, default: 1)\n}\n\nexport interface EntryScaleConfig {\n mode: EntryScaleMode;\n startScale?: number; // Fixed start scale for grow/shrink (0.1-4.0)\n range?: { min: number; max: number }; // Random range for 'random' mode\n pop?: EntryScalePopConfig; // Config for 'pop' mode\n}\n\nexport type EntryStartPosition =\n | 'nearest-edge'\n | 'top'\n | 'bottom'\n | 'left'\n | 'right'\n | 'center'\n | 'random-edge'\n | 'circular';\n\nexport interface EntryCircularConfig {\n radius?: number | string; // pixels or percentage like '120%', default: '120%' of container diagonal\n distribution?: 'even' | 'random'; // default: 'even'\n}\n\nexport interface EntryStartConfig {\n position: EntryStartPosition;\n offset?: number; // pixels beyond edge, default: 100\n circular?: EntryCircularConfig;\n}\n\nexport interface EntryTimingConfig {\n duration: number; // default: 600ms\n}\n\nexport interface EntryAnimationConfig {\n start: EntryStartConfig;\n timing: EntryTimingConfig;\n easing: string; // CSS easing, default: 'cubic-bezier(0.25, 1, 0.5, 1)'\n path?: EntryPathConfig; // Animation path type (linear, bounce, elastic, wave)\n rotation?: EntryRotationConfig; // Entry rotation animation\n scale?: EntryScaleConfig; // Entry scale animation\n}\n\nexport interface AnimationEasingConfig {\n default: string;\n bounce: string;\n focus: string;\n}\n\nexport interface AnimationQueueConfig {\n enabled: boolean;\n interval: number;\n}\n\nexport interface AnimationConfig {\n duration: number;\n easing: AnimationEasingConfig;\n queue: AnimationQueueConfig;\n entry?: EntryAnimationConfig;\n idle?: IdleAnimationConfig;\n}\n\n// ============================================================================\n// Idle Animation Configuration\n// ============================================================================\n\nexport type IdleAnimationType = 'wiggle' | 'pulse' | 'blink' | 'spin' | 'custom' | 'none';\nexport type IdleSyncMode = 'together' | 'random';\n\nexport interface IdleWiggleConfig {\n maxAngle: number;\n speed: number;\n sync: IdleSyncMode;\n}\n\nexport interface IdlePulseConfig {\n minScale: number;\n maxScale: number;\n speed: number;\n sync: IdleSyncMode;\n}\n\nexport interface IdleBlinkConfig {\n onRatio: number;\n speed: number;\n style: 'snap' | 'fade';\n}\n\nexport interface IdleSpinConfig {\n speed: number;\n direction: 'clockwise' | 'counterclockwise';\n}\n\nexport interface IdleCustomContext {\n element: HTMLElement;\n index: number;\n totalImages: number;\n}\n\nexport type IdleCustomAnimationFn = (ctx: IdleCustomContext) => Animation | (() => void);\n\nexport interface IdleAnimationConfig {\n type: IdleAnimationType;\n wiggle?: IdleWiggleConfig;\n pulse?: IdlePulseConfig;\n blink?: IdleBlinkConfig;\n spin?: IdleSpinConfig;\n custom?: IdleCustomAnimationFn;\n startDelay?: number;\n}\n\n// ============================================================================\n// Interaction Configuration\n// ============================================================================\n\nexport interface FocusInteractionConfig {\n scalePercent: number; // Percentage of container (0-1 as fraction, 1-100 as percent)\n zIndex: number;\n animationDuration?: number;\n}\n\nexport interface NavigationInteractionConfig {\n keyboard?: boolean;\n swipe?: boolean;\n mouseWheel?: boolean;\n}\n\nexport interface InteractionConfig {\n focus: FocusInteractionConfig;\n navigation?: NavigationInteractionConfig;\n dragging?: boolean;\n}\n\n// ============================================================================\n// UI Configuration\n// ============================================================================\n\nexport interface UIConfig {\n showLoadingSpinner: boolean;\n showImageCounter?: boolean;\n loadingElement?: string | HTMLElement;\n errorElement?: string | HTMLElement;\n counterElement?: string | HTMLElement;\n showNavButtons?: boolean;\n prevButtonElement?: string | HTMLElement;\n nextButtonElement?: string | HTMLElement;\n showFocusOutline?: boolean;\n}\n\n// ============================================================================\n// Main Gallery Configuration\n// ============================================================================\n\nexport interface ImageCloudConfig {\n loaders: LoaderEntry[];\n config: ConfigSection;\n image: ImageConfig;\n layout: LayoutConfig;\n animation: AnimationConfig;\n interaction: InteractionConfig;\n ui: UIConfig;\n styling?: ImageStylingConfig;\n}\n\n// Backwards compatibility alias\nexport type GalleryConfig = ImageCloudConfig;\n\nexport interface ImageCloudOptions {\n container?: string | HTMLElement;\n images?: string[];\n loaders?: LoaderEntry[];\n config?: ConfigSection;\n image?: Partial<ImageConfig>;\n layout?: Partial<LayoutConfig>;\n animation?: Partial<AnimationConfig>;\n interaction?: Partial<InteractionConfig>;\n ui?: Partial<UIConfig>;\n /** @deprecated Use `ui` instead of `rendering.ui` */\n rendering?: { ui?: Partial<UIConfig> };\n styling?: Partial<ImageStylingConfig>;\n}\n\n// Backwards compatibility alias\nexport type ImageGalleryOptions = ImageCloudOptions;\n\n// ============================================================================\n// Legacy Configuration Types (for backward compatibility)\n// ============================================================================\n\nexport interface LegacyLayoutConfig {\n baseImageSize?: number;\n rotationRange?: { min: number; max: number };\n type?: 'random' | 'radial';\n sizeVarianceMin?: number;\n sizeVarianceMax?: number;\n responsiveHeights?: ResponsiveHeight[];\n padding?: number;\n minSpacing?: number;\n minRotation?: number;\n maxRotation?: number;\n}\n\nexport interface LegacyAnimationConfig {\n duration?: number;\n queueInterval?: number;\n easing?: string;\n bounceEasing?: string;\n}\n\nexport interface LegacyZoomConfig {\n focusScale?: number;\n mobileScale?: number; // Deprecated: use scaleTo/scalePercent instead\n focusZIndex?: number;\n}\n\nexport interface LegacyConfig {\n layout?: LegacyLayoutConfig;\n animation?: LegacyAnimationConfig;\n zoom?: LegacyZoomConfig;\n googleDrive?: {\n apiKey?: string;\n apiEndpoint?: string;\n imageExtensions?: string[];\n };\n breakpoints?: {\n mobile?: number;\n tablet?: number;\n desktop?: number;\n };\n isMobile?: () => boolean;\n ui?: {\n showLoadingSpinner?: boolean;\n };\n}\n\nexport interface LegacyImageGalleryOptions {\n containerId?: string;\n loaderType?: 'googleDrive' | 'static';\n folderUrl?: string;\n googleDrive?: {\n apiKey?: string;\n };\n}\n\n// ============================================================================\n// Interface Dependencies\n// ============================================================================\n\nexport interface PlacementLayout {\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options?: Partial<LayoutConfig>\n ): ImageLayout[];\n}\n\n/**\n * ImageFilter interface for filtering images by extension\n * Implemented by the ImageFilter class in loaders/ImageFilter.ts\n */\nexport interface IImageFilter {\n isAllowed(filename: string): boolean;\n getAllowedExtensions(): string[];\n}\n\n/**\n * ImageLoader interface with consistent lifecycle pattern:\n * 1. Constructor - Initialize with required parameters, throw if missing\n * 2. prepare(filter) - Async discovery of images, accepts filter\n * 3. imagesLength() - Return count of images (after prepare)\n * 4. imageURLs() - Return ordered list of URLs (after prepare)\n */\nexport interface ImageLoader {\n /**\n * Async preparation - discovers images and applies filter\n * Succeeds even if 0 images found (gallery handles empty state)\n * @param filter - Filter to apply to discovered images\n */\n prepare(filter: IImageFilter): Promise<void>;\n\n /**\n * Get the number of discovered images\n * @throws Error if called before prepare() completes\n */\n imagesLength(): number;\n\n /**\n * Get the ordered list of image URLs\n * @throws Error if called before prepare() completes\n */\n imageURLs(): string[];\n\n /**\n * Check if the loader has been prepared\n */\n isPrepared(): boolean;\n}\n\nexport interface GoogleDriveFile {\n id: string;\n name: string;\n mimeType: string;\n parents?: string[];\n}\n\nexport interface GoogleDriveResponse {\n files: GoogleDriveFile[];\n nextPageToken?: string;\n}\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];\n};\n\n// ============================================================================\n// Image Styling Configuration\n// ============================================================================\n\nexport type ShadowPreset = 'none' | 'sm' | 'md' | 'lg' | 'glow';\n\nexport type BorderStyle =\n | 'solid' // Most common - continuous line\n | 'dashed' // Series of dashes\n | 'dotted' // Series of dots\n | 'double' // Two parallel lines\n | 'none' // No border\n | 'groove' // 3D carved into page\n | 'ridge' // 3D raised from page\n | 'inset' // 3D embedded look\n | 'outset' // 3D raised look\n | 'hidden'; // Same as none (for table border conflict resolution)\n\nexport interface BorderConfig {\n width?: number; // pixels, default: 0\n color?: string; // CSS color, default: '#000'\n radius?: number; // pixels, default: 8\n style?: BorderStyle; // default: 'solid'\n}\n\nexport interface DropShadowConfig {\n x: number;\n y: number;\n blur: number;\n color: string;\n}\n\nexport interface FilterConfig {\n grayscale?: number; // 0-1\n blur?: number; // pixels\n brightness?: number; // multiplier (1 = normal)\n contrast?: number; // multiplier\n saturate?: number; // multiplier\n opacity?: number; // 0-1\n sepia?: number; // 0-1\n hueRotate?: number; // degrees\n invert?: number; // 0-1\n dropShadow?: DropShadowConfig | string;\n}\n\nexport interface OutlineConfig {\n width?: number; // pixels\n color?: string; // CSS color\n style?: BorderStyle; // reuses BorderStyle type\n offset?: number; // pixels\n}\n\nexport type ClipPathShape =\n | 'circle'\n | 'square'\n | 'triangle'\n | 'pentagon'\n | 'hexagon'\n | 'octagon'\n | 'diamond';\n\nexport type ClipPathMode = 'percent' | 'height-relative';\n\nexport interface ClipPathConfig {\n shape: ClipPathShape;\n mode?: ClipPathMode; // defaults to 'percent'\n}\n\nexport interface ImageStyleState {\n // CSS class names (space-separated string or array)\n className?: string | string[];\n\n // Border (shorthand applies to all sides)\n border?: BorderConfig;\n borderTop?: Partial<BorderConfig>;\n borderRight?: Partial<BorderConfig>;\n borderBottom?: Partial<BorderConfig>;\n borderLeft?: Partial<BorderConfig>;\n\n // Per-corner border radius (overrides border.radius for specific corners)\n borderRadiusTopLeft?: number;\n borderRadiusTopRight?: number;\n borderRadiusBottomRight?: number;\n borderRadiusBottomLeft?: number;\n\n // Shadow (preset name or custom CSS string)\n shadow?: ShadowPreset | string;\n\n // Filters\n filter?: FilterConfig;\n\n // Other properties\n opacity?: number; // 0-1\n cursor?: string; // CSS cursor value\n outline?: OutlineConfig;\n objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';\n aspectRatio?: string; // e.g., '16/9', '1/1'\n clipPath?: ClipPathShape | string | ClipPathConfig; // Predefined shape, custom clip-path string, or config object\n}\n\nexport interface ImageStylingConfig {\n default?: ImageStyleState;\n hover?: Partial<ImageStyleState>; // inherits from default\n focused?: Partial<ImageStyleState>; // inherits from default\n}\n\n// ============================================================================\n// Focus Animation Types (Cross-Animation Support)\n// ============================================================================\n\n/**\n * State machine states for zoom/focus animations\n */\nexport enum ZoomState {\n IDLE = 'idle', // No focus, no animations\n FOCUSING = 'focusing', // Single image animating in\n FOCUSED = 'focused', // Stable focused state\n UNFOCUSING = 'unfocusing', // Single image animating out\n CROSS_ANIMATING = 'cross_animating' // Two images: one out, one in\n}\n\n/**\n * Handle for a cancellable animation using Web Animations API\n */\nexport interface AnimationHandle {\n id: string;\n element: HTMLElement;\n animation: Animation;\n fromState: TransformParams;\n toState: TransformParams;\n startTime: number;\n duration: number;\n}\n\n/**\n * Snapshot of an element's current transform state\n * Used for capturing position mid-animation\n */\nexport interface AnimationSnapshot {\n x: number;\n y: number;\n rotation: number;\n scale: number;\n}\n\n/**\n * Tracks an image that is currently animating\n */\nexport interface AnimatingImage {\n element: HTMLElement;\n originalState: ImageLayout;\n animationHandle: AnimationHandle;\n direction: 'in' | 'out';\n originalWidth?: number;\n originalHeight?: number;\n}","/**\n * Maps predefined shape names to CSS clip-path values.\n * Supports both percentage-based (responsive) and height-relative (aspect-ratio aware) modes.\n */\n\nimport type { ClipPathShape } from '../config/types';\nimport { HEXAGON_REF_HEIGHT, HEXAGON_REF_POINTS } from './hexagonGeometry';\n\nconst CLIP_PATH_SHAPES: Record<ClipPathShape, string> = {\n // Geometric shapes - uses percentages for responsive sizing\n circle: 'circle(50%)',\n square: 'polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)',\n triangle: 'polygon(50% 0%, 100% 100%, 0% 100%)',\n pentagon: 'polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%)',\n hexagon: 'polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%)',\n octagon: 'polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%)',\n diamond: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)'\n};\n\n/**\n * Height-relative shapes: normalized at a reference height of 100px\n * Points are in pixels relative to the reference height\n * When applied, coordinates scale based on actual image height\n */\ninterface HeightRelativeShape {\n refHeight: number;\n points: Array<[number, number]>; // [x, y] coordinates\n}\n\nconst CLIP_PATH_SHAPES_HEIGHT_RELATIVE: Record<ClipPathShape, HeightRelativeShape> = {\n // Circle - uses radius in pixels (refHeight of 100px = 50px radius)\n circle: {\n refHeight: 100,\n points: [] // Special case: handled separately\n },\n // Square - maintains perfect aspect ratio (always 1:1)\n square: {\n refHeight: 100,\n points: [[0, 0], [100, 0], [100, 100], [0, 100]]\n },\n // Triangle - isosceles triangle\n triangle: {\n refHeight: 100,\n points: [[50, 0], [100, 100], [0, 100]]\n },\n // Pentagon - regular pentagon\n pentagon: {\n refHeight: 100,\n points: [[50, 0], [100, 38], [82, 100], [18, 100], [0, 38]]\n },\n // Hexagon - regular hexagon (reference points imported from hexagonGeometry)\n hexagon: {\n refHeight: HEXAGON_REF_HEIGHT,\n points: HEXAGON_REF_POINTS\n },\n // Octagon - regular octagon\n octagon: {\n refHeight: 100,\n points: [[30, 0], [70, 0], [100, 30], [100, 70], [70, 100], [30, 100], [0, 70], [0, 30]]\n },\n // Diamond - 45-degree rotated square\n diamond: {\n refHeight: 100,\n points: [[50, 0], [100, 50], [50, 100], [0, 50]]\n }\n};\n\n/**\n * Resolves a shape name or custom clip-path string to a valid CSS clip-path value.\n * @param shape - Predefined shape name or custom clip-path string\n * @returns Valid CSS clip-path value\n */\nexport function getClipPath(shape: ClipPathShape | string | undefined): string | undefined {\n if (!shape) return undefined;\n\n // Check if it's a predefined shape\n if (shape in CLIP_PATH_SHAPES) {\n return CLIP_PATH_SHAPES[shape as ClipPathShape];\n }\n\n // Treat as custom clip-path string (e.g., 'polygon(...)' or 'inset(...)')\n return shape;\n}\n\n/**\n * Returns available predefined shape names for UI/documentation.\n */\nexport function getAvailableShapes(): ClipPathShape[] {\n return Object.keys(CLIP_PATH_SHAPES) as ClipPathShape[];\n}\n\n/**\n * Calculates height-relative clip-path string for a given shape and image dimensions.\n * Scales the reference shape definition by (imageHeight / refHeight) and centers it horizontally.\n * @param shape - Predefined shape name\n * @param imageHeight - Actual image height in pixels\n * @param imageWidth - Actual image width in pixels (used to center the shape horizontally)\n * @returns CSS clip-path value (e.g., 'circle(50px)' or 'polygon(...)')\n */\nexport function calculateHeightRelativeClipPath(shape: ClipPathShape, imageHeight: number, imageWidth?: number): string {\n const shapeDef = CLIP_PATH_SHAPES_HEIGHT_RELATIVE[shape];\n if (!shapeDef) return '';\n\n const scale = imageHeight / shapeDef.refHeight;\n\n // Special case: circle uses circle() function with radius\n if (shape === 'circle') {\n const radius = Math.round(50 * scale * 100) / 100; // Round to 2 decimals\n return `circle(${radius}px)`;\n }\n\n // Calculate offsets to center the shape's bounding box within the image.\n // Compute the bounding box from the actual points (handles non-square shapes\n // like the regular hexagon whose width ≠ refHeight).\n const xs = shapeDef.points.map(([x]) => x);\n const ys = shapeDef.points.map(([, y]) => y);\n const shapeCenterX = ((Math.min(...xs) + Math.max(...xs)) / 2) * scale;\n const shapeCenterY = ((Math.min(...ys) + Math.max(...ys)) / 2) * scale;\n\n // Fallback imageWidth: use the scaled bounding box width when unknown\n const scaledBBoxWidth = (Math.max(...xs) - Math.min(...xs)) * scale;\n\n // Image's center\n const imageCenterX = (imageWidth ?? scaledBBoxWidth) / 2;\n const imageCenterY = imageHeight / 2;\n\n // Offsets to move shape center to image center\n const horizontalOffset = imageCenterX - shapeCenterX;\n const verticalOffset = imageCenterY - shapeCenterY;\n\n // For polygon shapes, scale all points and format as polygon()\n const scaledPoints = shapeDef.points.map(([x, y]) => {\n const scaledX = Math.round((x * scale + horizontalOffset) * 100) / 100;\n const scaledY = Math.round((y * scale + verticalOffset) * 100) / 100;\n return `${scaledX}px ${scaledY}px`;\n });\n\n return `polygon(${scaledPoints.join(', ')})`;\n}\n","/**\n * Style utilities for image styling configuration\n */\n\nimport type { ImageStyleState, FilterConfig, BorderConfig, ShadowPreset, DropShadowConfig, ClipPathConfig, ClipPathShape } from '../config/types';\nimport { SHADOW_PRESETS } from '../config/defaults';\nimport { getClipPath, calculateHeightRelativeClipPath } from './clipPathGenerator';\n\n/**\n * Check if a value is a known shadow preset\n */\nfunction isShadowPreset(value: string): value is ShadowPreset {\n return value in SHADOW_PRESETS;\n}\n\n/**\n * Resolve shadow value - converts preset names to CSS values,\n * passes custom CSS strings through directly\n */\nexport function resolveShadow(shadow: ShadowPreset | string | undefined): string {\n if (!shadow) return SHADOW_PRESETS.md;\n if (isShadowPreset(shadow)) return SHADOW_PRESETS[shadow];\n return shadow; // Custom CSS string\n}\n\n/**\n * Build CSS filter string from FilterConfig\n */\nexport function buildFilterString(filter: FilterConfig | undefined): string {\n if (!filter) return '';\n\n const parts: string[] = [];\n\n if (filter.grayscale !== undefined) {\n parts.push(`grayscale(${filter.grayscale})`);\n }\n if (filter.blur !== undefined) {\n parts.push(`blur(${filter.blur}px)`);\n }\n if (filter.brightness !== undefined) {\n parts.push(`brightness(${filter.brightness})`);\n }\n if (filter.contrast !== undefined) {\n parts.push(`contrast(${filter.contrast})`);\n }\n if (filter.saturate !== undefined) {\n parts.push(`saturate(${filter.saturate})`);\n }\n if (filter.opacity !== undefined) {\n parts.push(`opacity(${filter.opacity})`);\n }\n if (filter.sepia !== undefined) {\n parts.push(`sepia(${filter.sepia})`);\n }\n if (filter.hueRotate !== undefined) {\n parts.push(`hue-rotate(${filter.hueRotate}deg)`);\n }\n if (filter.invert !== undefined) {\n parts.push(`invert(${filter.invert})`);\n }\n if (filter.dropShadow !== undefined) {\n if (typeof filter.dropShadow === 'string') {\n parts.push(`drop-shadow(${filter.dropShadow})`);\n } else {\n const ds = filter.dropShadow as DropShadowConfig;\n parts.push(`drop-shadow(${ds.x}px ${ds.y}px ${ds.blur}px ${ds.color})`);\n }\n }\n\n return parts.join(' ');\n}\n\n/**\n * Build border CSS for a single side\n */\nfunction buildSingleBorder(config: BorderConfig | undefined): string {\n if (!config || config.style === 'none' || config.width === 0) {\n return 'none';\n }\n const width = config.width ?? 0;\n const style = config.style ?? 'solid';\n const color = config.color ?? '#000000';\n return `${width}px ${style} ${color}`;\n}\n\n/**\n * CSS properties object type for style application\n */\nexport interface StyleProperties {\n borderRadius?: string;\n borderTopLeftRadius?: string;\n borderTopRightRadius?: string;\n borderBottomRightRadius?: string;\n borderBottomLeftRadius?: string;\n border?: string;\n borderTop?: string;\n borderRight?: string;\n borderBottom?: string;\n borderLeft?: string;\n boxShadow?: string;\n filter?: string;\n opacity?: string;\n cursor?: string;\n outline?: string;\n outlineOffset?: string;\n objectFit?: string;\n aspectRatio?: string;\n clipPath?: string;\n overflow?: string;\n}\n\n/**\n * Build complete style properties object from ImageStyleState\n * @param state - Image style state configuration\n * @param imageHeight - Optional image height for height-relative clip-path calculations\n * @param imageWidth - Optional image width for centering height-relative clip-path shapes\n */\nexport function buildStyleProperties(state: ImageStyleState | undefined, imageHeight?: number, imageWidth?: number): StyleProperties {\n if (!state) return {};\n\n const styles: StyleProperties = {};\n\n // Border radius - check for per-corner overrides first\n const hasPerCornerRadius = state.borderRadiusTopLeft !== undefined ||\n state.borderRadiusTopRight !== undefined ||\n state.borderRadiusBottomRight !== undefined ||\n state.borderRadiusBottomLeft !== undefined;\n\n if (hasPerCornerRadius) {\n // Use per-corner radius with fallback to base radius\n const baseRadius = state.border?.radius ?? 0;\n if (state.borderRadiusTopLeft !== undefined) {\n styles.borderTopLeftRadius = `${state.borderRadiusTopLeft}px`;\n } else if (baseRadius) {\n styles.borderTopLeftRadius = `${baseRadius}px`;\n }\n if (state.borderRadiusTopRight !== undefined) {\n styles.borderTopRightRadius = `${state.borderRadiusTopRight}px`;\n } else if (baseRadius) {\n styles.borderTopRightRadius = `${baseRadius}px`;\n }\n if (state.borderRadiusBottomRight !== undefined) {\n styles.borderBottomRightRadius = `${state.borderRadiusBottomRight}px`;\n } else if (baseRadius) {\n styles.borderBottomRightRadius = `${baseRadius}px`;\n }\n if (state.borderRadiusBottomLeft !== undefined) {\n styles.borderBottomLeftRadius = `${state.borderRadiusBottomLeft}px`;\n } else if (baseRadius) {\n styles.borderBottomLeftRadius = `${baseRadius}px`;\n }\n } else if (state.border?.radius !== undefined) {\n styles.borderRadius = `${state.border.radius}px`;\n }\n\n // Check if any per-side border is defined\n const hasPerSideBorder = state.borderTop || state.borderRight || state.borderBottom || state.borderLeft;\n\n if (hasPerSideBorder) {\n // Merge base border with per-side overrides\n const baseBorder = state.border || {};\n\n const topBorder = { ...baseBorder, ...state.borderTop };\n const rightBorder = { ...baseBorder, ...state.borderRight };\n const bottomBorder = { ...baseBorder, ...state.borderBottom };\n const leftBorder = { ...baseBorder, ...state.borderLeft };\n\n styles.borderTop = buildSingleBorder(topBorder);\n styles.borderRight = buildSingleBorder(rightBorder);\n styles.borderBottom = buildSingleBorder(bottomBorder);\n styles.borderLeft = buildSingleBorder(leftBorder);\n } else if (state.border) {\n // Apply uniform border\n styles.border = buildSingleBorder(state.border);\n }\n\n // Shadow\n if (state.shadow !== undefined) {\n styles.boxShadow = resolveShadow(state.shadow);\n }\n\n // Filter - always set to ensure hover filters are properly cleared on mouseleave\n const filterStr = buildFilterString(state.filter);\n styles.filter = filterStr || 'none';\n\n // Opacity\n if (state.opacity !== undefined) {\n styles.opacity = String(state.opacity);\n }\n\n // Cursor\n if (state.cursor !== undefined) {\n styles.cursor = state.cursor;\n }\n\n // Outline\n if (state.outline && state.outline.style !== 'none' && (state.outline.width ?? 0) > 0) {\n const width = state.outline.width ?? 0;\n const style = state.outline.style ?? 'solid';\n const color = state.outline.color ?? '#000000';\n styles.outline = `${width}px ${style} ${color}`;\n if (state.outline.offset !== undefined) {\n styles.outlineOffset = `${state.outline.offset}px`;\n }\n }\n\n // Object fit\n if (state.objectFit !== undefined) {\n styles.objectFit = state.objectFit;\n }\n\n // Aspect ratio\n if (state.aspectRatio !== undefined) {\n styles.aspectRatio = state.aspectRatio;\n }\n\n // Clip path (cropping)\n if (state.clipPath !== undefined) {\n let clipPathValue: string | undefined;\n\n // Check if clipPath is a config object with height-relative mode\n const isConfig = typeof state.clipPath === 'object' && state.clipPath !== null && 'shape' in state.clipPath;\n const config = isConfig ? (state.clipPath as ClipPathConfig) : undefined;\n\n if (config?.mode === 'height-relative' && imageHeight) {\n // Use height-relative calculation if mode is specified and imageHeight is available\n clipPathValue = calculateHeightRelativeClipPath(config.shape, imageHeight, imageWidth);\n } else {\n // Fall back to standard clip-path resolution\n const clipPathInput = isConfig && config ? config.shape : state.clipPath;\n clipPathValue = getClipPath(clipPathInput as ClipPathShape | string);\n }\n\n if (clipPathValue) {\n // When 'none' is specified, use 'unset' to clear any inherited clip-path\n if (clipPathValue === 'none') {\n styles.clipPath = 'unset';\n } else {\n styles.clipPath = clipPathValue;\n styles.overflow = 'hidden'; // Ensure clean boundaries\n }\n }\n }\n\n return styles;\n}\n\n/**\n * Apply style properties to an HTML element\n */\nexport function applyStylesToElement(element: HTMLElement, styles: StyleProperties): void {\n if (styles.borderRadius !== undefined) element.style.borderRadius = styles.borderRadius;\n if (styles.borderTopLeftRadius !== undefined) element.style.borderTopLeftRadius = styles.borderTopLeftRadius;\n if (styles.borderTopRightRadius !== undefined) element.style.borderTopRightRadius = styles.borderTopRightRadius;\n if (styles.borderBottomRightRadius !== undefined) element.style.borderBottomRightRadius = styles.borderBottomRightRadius;\n if (styles.borderBottomLeftRadius !== undefined) element.style.borderBottomLeftRadius = styles.borderBottomLeftRadius;\n if (styles.border !== undefined) element.style.border = styles.border;\n if (styles.borderTop !== undefined) element.style.borderTop = styles.borderTop;\n if (styles.borderRight !== undefined) element.style.borderRight = styles.borderRight;\n if (styles.borderBottom !== undefined) element.style.borderBottom = styles.borderBottom;\n if (styles.borderLeft !== undefined) element.style.borderLeft = styles.borderLeft;\n if (styles.boxShadow !== undefined) element.style.boxShadow = styles.boxShadow;\n if (styles.filter !== undefined) element.style.filter = styles.filter;\n if (styles.opacity !== undefined) element.style.opacity = styles.opacity;\n if (styles.cursor !== undefined) element.style.cursor = styles.cursor;\n if (styles.outline !== undefined) element.style.outline = styles.outline;\n if (styles.outlineOffset !== undefined) element.style.outlineOffset = styles.outlineOffset;\n if (styles.objectFit !== undefined) element.style.objectFit = styles.objectFit;\n if (styles.aspectRatio !== undefined) element.style.aspectRatio = styles.aspectRatio;\n if (styles.clipPath !== undefined) element.style.clipPath = styles.clipPath;\n if (styles.overflow !== undefined) element.style.overflow = styles.overflow;\n}\n\n/**\n * Build and apply style properties for a given state with image dimensions\n * This is useful for height-relative clip-path calculations which depend on image height and width\n * @param element - HTML element to apply styles to\n * @param state - Image style state configuration\n * @param imageHeight - Optional image height for height-relative clip-path calculations\n * @param imageWidth - Optional image width for centering height-relative clip-path shapes\n */\nexport function applyStylesToElementWithState(element: HTMLElement, state: ImageStyleState | undefined, imageHeight?: number, imageWidth?: number): void {\n const styles = buildStyleProperties(state, imageHeight, imageWidth);\n applyStylesToElement(element, styles);\n}\n\n/**\n * Resolve className to a space-separated string\n */\nexport function resolveClassName(className: string | string[] | undefined): string {\n if (!className) return '';\n if (Array.isArray(className)) return className.join(' ');\n return className;\n}\n\n/**\n * Apply className to element (additive with existing classes)\n */\nexport function applyClassNameToElement(element: HTMLElement, className: string | string[] | undefined): void {\n const resolved = resolveClassName(className);\n if (resolved) {\n resolved.split(' ').forEach(cls => {\n if (cls.trim()) element.classList.add(cls.trim());\n });\n }\n}\n\n/**\n * Remove className from element\n */\nexport function removeClassNameFromElement(element: HTMLElement, className: string | string[] | undefined): void {\n const resolved = resolveClassName(className);\n if (resolved) {\n resolved.split(' ').forEach(cls => {\n if (cls.trim()) element.classList.remove(cls.trim());\n });\n }\n}\n","/**\n * ZoomEngine.ts\n * Manages zoom/focus behavior for image cloud with cross-animation support\n *\n * Public API:\n * - focusImage(imageElement, containerBounds, originalState)\n * - unfocusImage()\n * - getCurrentFocus()\n * - swapFocus(newImageElement, containerBounds, originalState)\n * - isFocused(imageElement)\n * - isAnimating()\n * - getState()\n * - reset()\n */\n\nimport type {\n FocusInteractionConfig,\n ContainerBounds,\n ImageLayout,\n TransformParams,\n ImageStylingConfig,\n AnimationHandle,\n AnimatingImage\n} from '../config/types';\nimport { ZoomState } from '../config/types';\nimport { AnimationEngine } from './AnimationEngine';\nimport { applyClassNameToElement, removeClassNameFromElement, buildStyleProperties } from '../utils/styleUtils';\n\ninterface FocusData {\n element: HTMLElement;\n originalState: ImageLayout;\n focusTransform: TransformParams;\n originalZIndex: string;\n originalWidth: number;\n originalHeight: number;\n focusWidth: number;\n focusHeight: number;\n}\n\n// Z-index constants for layering during animations\nconst Z_INDEX = {\n DEFAULT: '',\n UNFOCUSING: 999,\n FOCUSING: 1000,\n FOCUSED: 1000\n};\n\nexport class ZoomEngine {\n private config: FocusInteractionConfig;\n private animationEngine: AnimationEngine;\n\n // State machine\n private state: ZoomState = ZoomState.IDLE;\n private currentFocus: HTMLElement | null = null;\n private focusData: FocusData | null = null;\n\n // Animation tracking for cross-animation\n private outgoing: AnimatingImage | null = null;\n private incoming: AnimatingImage | null = null;\n\n // Generation counter to handle concurrent calls\n private focusGeneration: number = 0;\n\n // Styling\n private styling?: ImageStylingConfig;\n private focusedClassName: string | string[] | undefined;\n\n // Callback fired when an unfocus animation fully completes\n private onUnfocusComplete: ((element: HTMLElement) => void) | null = null;\n\n constructor(config: FocusInteractionConfig, animationEngine: AnimationEngine, styling?: ImageStylingConfig) {\n this.config = config;\n this.animationEngine = animationEngine;\n this.styling = styling;\n\n // Store focused class name for on-demand class management\n this.focusedClassName = styling?.focused?.className;\n }\n\n /**\n * Set callback to be fired when an unfocus animation fully completes.\n */\n setOnUnfocusCompleteCallback(callback: ((element: HTMLElement) => void) | null): void {\n this.onUnfocusComplete = callback;\n }\n\n /**\n * Get current state machine state\n */\n getState(): ZoomState {\n return this.state;\n }\n\n /**\n * Check if any animation is in progress\n */\n isAnimating(): boolean {\n return this.state !== ZoomState.IDLE && this.state !== ZoomState.FOCUSED;\n }\n\n /**\n * Normalize scalePercent value\n */\n private normalizeScalePercent(value: number): number {\n return value > 1 ? value / 100 : value;\n }\n\n /**\n * Calculate target dimensions for focused image\n * Returns actual pixel dimensions instead of scale factor for sharper rendering\n */\n private calculateFocusDimensions(imageWidth: number, imageHeight: number, containerBounds: ContainerBounds): { width: number; height: number } {\n const normalizedPercent = this.normalizeScalePercent(this.config.scalePercent);\n const targetHeight = containerBounds.height * normalizedPercent;\n const aspectRatio = imageWidth / imageHeight;\n\n let focusHeight = targetHeight;\n let focusWidth = focusHeight * aspectRatio;\n\n const maxWidth = containerBounds.width * normalizedPercent;\n if (focusWidth > maxWidth) {\n focusWidth = maxWidth;\n focusHeight = focusWidth / aspectRatio;\n }\n\n return { width: focusWidth, height: focusHeight };\n }\n\n /**\n * Calculate the transform needed to center an image (position only, no scale)\n * Scale is handled by animating actual dimensions for sharper rendering\n */\n private calculateFocusTransform(\n containerBounds: ContainerBounds,\n originalState: ImageLayout\n ): TransformParams {\n const centerX = containerBounds.width / 2;\n const centerY = containerBounds.height / 2;\n\n const targetX = centerX - originalState.x;\n const targetY = centerY - originalState.y;\n\n return {\n x: targetX,\n y: targetY,\n rotation: 0,\n scale: 1 // No scale transform - dimensions are animated instead\n };\n }\n\n /**\n * Build transform string for dimension-based zoom (no scale in transform)\n */\n private buildDimensionZoomTransform(params: TransformParams): string {\n const transforms: string[] = ['translate(-50%, -50%)'];\n\n if (params.x !== undefined || params.y !== undefined) {\n const x = params.x ?? 0;\n const y = params.y ?? 0;\n transforms.push(`translate(${x}px, ${y}px)`);\n }\n\n if (params.rotation !== undefined) {\n transforms.push(`rotate(${params.rotation}deg)`);\n }\n\n // Note: scale is intentionally omitted - we animate width/height instead\n\n return transforms.join(' ');\n }\n\n /**\n * Create a Web Animation that animates both transform (position) and dimensions\n * This provides sharper zoom by re-rendering at target size instead of scaling pixels\n */\n private animateWithDimensions(\n element: HTMLElement,\n fromTransform: TransformParams,\n toTransform: TransformParams,\n fromWidth: number,\n fromHeight: number,\n toWidth: number,\n toHeight: number,\n duration: number\n ): Animation {\n const fromTransformStr = this.buildDimensionZoomTransform(fromTransform);\n const toTransformStr = this.buildDimensionZoomTransform(toTransform);\n\n // Clear any CSS transitions to avoid conflicts\n element.style.transition = 'none';\n\n // Create Web Animation with both transform and dimensions\n const animation = element.animate(\n [\n {\n transform: fromTransformStr,\n width: `${fromWidth}px`,\n height: `${fromHeight}px`\n },\n {\n transform: toTransformStr,\n width: `${toWidth}px`,\n height: `${toHeight}px`\n }\n ],\n {\n duration: duration,\n easing: 'cubic-bezier(0.4, 0, 0.2, 1)',\n fill: 'forwards'\n }\n );\n\n return animation;\n }\n\n /**\n * Apply focused styling to an element\n * Applies all focused styling properties, classes, and z-index\n */\n private applyFocusedStyling(element: HTMLElement, zIndex: number): void {\n element.style.zIndex = String(zIndex);\n element.classList.add('fbn-ic-focused');\n applyClassNameToElement(element, this.focusedClassName);\n\n // Apply all styling properties (opacity, filters, borders, shadows, etc.)\n // for the focused state\n if (this.styling?.focused) {\n const styles = buildStyleProperties(this.styling.focused, element.offsetHeight, element.offsetWidth);\n // Apply styles directly, skipping clip-path and overflow (updated in startClipPathAnimation)\n if (styles.borderRadius !== undefined) element.style.borderRadius = styles.borderRadius;\n if (styles.borderTopLeftRadius !== undefined) element.style.borderTopLeftRadius = styles.borderTopLeftRadius;\n if (styles.borderTopRightRadius !== undefined) element.style.borderTopRightRadius = styles.borderTopRightRadius;\n if (styles.borderBottomRightRadius !== undefined) element.style.borderBottomRightRadius = styles.borderBottomRightRadius;\n if (styles.borderBottomLeftRadius !== undefined) element.style.borderBottomLeftRadius = styles.borderBottomLeftRadius;\n if (styles.border !== undefined) element.style.border = styles.border;\n if (styles.borderTop !== undefined) element.style.borderTop = styles.borderTop;\n if (styles.borderRight !== undefined) element.style.borderRight = styles.borderRight;\n if (styles.borderBottom !== undefined) element.style.borderBottom = styles.borderBottom;\n if (styles.borderLeft !== undefined) element.style.borderLeft = styles.borderLeft;\n if (styles.boxShadow !== undefined) element.style.boxShadow = styles.boxShadow;\n if (styles.filter !== undefined) element.style.filter = styles.filter;\n if (styles.opacity !== undefined) element.style.opacity = styles.opacity;\n if (styles.cursor !== undefined) element.style.cursor = styles.cursor;\n if (styles.outline !== undefined) element.style.outline = styles.outline;\n if (styles.outlineOffset !== undefined) element.style.outlineOffset = styles.outlineOffset;\n if (styles.objectFit !== undefined) element.style.objectFit = styles.objectFit;\n if (styles.aspectRatio !== undefined) element.style.aspectRatio = styles.aspectRatio;\n }\n }\n\n /**\n * Remove focused styling from an element\n * Restores default styling properties, removes classes, and resets z-index\n */\n private removeFocusedStyling(element: HTMLElement, originalZIndex: string): void {\n element.style.zIndex = originalZIndex;\n element.classList.remove('fbn-ic-focused');\n removeClassNameFromElement(element, this.focusedClassName);\n\n // Restore default styling properties (opacity, filters, borders, shadows, etc.)\n if (this.styling?.default) {\n const styles = buildStyleProperties(this.styling.default, element.offsetHeight, element.offsetWidth);\n // Apply styles directly, skipping clip-path and overflow (updated in startClipPathAnimation)\n if (styles.borderRadius !== undefined) element.style.borderRadius = styles.borderRadius;\n if (styles.borderTopLeftRadius !== undefined) element.style.borderTopLeftRadius = styles.borderTopLeftRadius;\n if (styles.borderTopRightRadius !== undefined) element.style.borderTopRightRadius = styles.borderTopRightRadius;\n if (styles.borderBottomRightRadius !== undefined) element.style.borderBottomRightRadius = styles.borderBottomRightRadius;\n if (styles.borderBottomLeftRadius !== undefined) element.style.borderBottomLeftRadius = styles.borderBottomLeftRadius;\n if (styles.border !== undefined) element.style.border = styles.border;\n if (styles.borderTop !== undefined) element.style.borderTop = styles.borderTop;\n if (styles.borderRight !== undefined) element.style.borderRight = styles.borderRight;\n if (styles.borderBottom !== undefined) element.style.borderBottom = styles.borderBottom;\n if (styles.borderLeft !== undefined) element.style.borderLeft = styles.borderLeft;\n if (styles.boxShadow !== undefined) element.style.boxShadow = styles.boxShadow;\n if (styles.filter !== undefined) element.style.filter = styles.filter;\n if (styles.opacity !== undefined) element.style.opacity = styles.opacity;\n if (styles.cursor !== undefined) element.style.cursor = styles.cursor;\n if (styles.outline !== undefined) element.style.outline = styles.outline;\n if (styles.outlineOffset !== undefined) element.style.outlineOffset = styles.outlineOffset;\n if (styles.objectFit !== undefined) element.style.objectFit = styles.objectFit;\n if (styles.aspectRatio !== undefined) element.style.aspectRatio = styles.aspectRatio;\n }\n }\n\n /**\n * Continuously update clip-path during animation based on current element dimensions\n * This ensures clip-path changes smoothly as width/height animate\n */\n private startClipPathAnimation(element: HTMLElement, handle: AnimationHandle, isToFocused: boolean): void {\n // Determine which styling config to use\n // If focused is explicitly defined but has no clipPath, don't fall back to default clipPath\n let styleConfig: any = isToFocused\n ? (this.styling?.focused ?? this.styling?.default)\n : this.styling?.default;\n\n // If focused config is explicitly set but missing clipPath, explicitly set it to undefined\n // to prevent inheriting default clipPath\n if (isToFocused && this.styling?.focused && this.styling.focused.clipPath === undefined) {\n styleConfig = { ...styleConfig, clipPath: undefined };\n }\n\n const updateClipPath = () => {\n // Use actual animated element dimensions (both width and height are being animated)\n const currentHeight = element.offsetHeight;\n const currentWidth = element.offsetWidth;\n\n // Build style properties with current dimensions to get updated clip-path\n const styles = buildStyleProperties(styleConfig, currentHeight, currentWidth);\n\n // Apply clip-path - clear it if not defined\n if (styles.clipPath !== undefined) {\n element.style.clipPath = styles.clipPath;\n } else {\n // No clip-path defined - clear any inherited clip-path\n element.style.clipPath = 'unset';\n }\n if (styles.overflow !== undefined) {\n element.style.overflow = styles.overflow;\n }\n\n // Continue updating if animation is still running\n if (handle.animation.playState === 'running') {\n requestAnimationFrame(updateClipPath);\n }\n };\n\n // Start the update loop\n requestAnimationFrame(updateClipPath);\n }\n\n /**\n * Start focus animation for an image using dimension-based zoom\n * Animates actual width/height for sharper rendering instead of transform scale\n * @param fromTransform - Optional starting transform (for mid-animation reversals)\n * @param fromDimensions - Optional starting dimensions (for mid-animation reversals)\n */\n private startFocusAnimation(\n element: HTMLElement,\n containerBounds: ContainerBounds,\n originalState: ImageLayout,\n fromTransform?: TransformParams,\n fromDimensions?: { width: number; height: number }\n ): AnimatingImage {\n const originalZIndex = element.style.zIndex || '';\n const originalWidth = element.offsetWidth;\n const originalHeight = element.offsetHeight;\n\n // Calculate target dimensions and position\n const focusDimensions = this.calculateFocusDimensions(originalWidth, originalHeight, containerBounds);\n const focusTransform = this.calculateFocusTransform(containerBounds, originalState);\n\n // Cancel any existing animation\n this.animationEngine.cancelAllAnimations(element);\n\n // Get animation duration\n const duration = this.config.animationDuration ?? 600;\n\n // Apply focused styling (classes and z-index only)\n this.applyFocusedStyling(element, Z_INDEX.FOCUSING);\n\n // Start animation from provided state or original position\n const startTransform: TransformParams = fromTransform ?? {\n x: 0,\n y: 0,\n rotation: originalState.rotation,\n scale: 1 // No scale - using dimensions\n };\n\n const startWidth = fromDimensions?.width ?? originalWidth;\n const startHeight = fromDimensions?.height ?? originalHeight;\n\n // Create dimension-based animation\n const animation = this.animateWithDimensions(\n element,\n startTransform,\n focusTransform,\n startWidth,\n startHeight,\n focusDimensions.width,\n focusDimensions.height,\n duration\n );\n\n // Create animation handle\n const handle: AnimationHandle = {\n id: `focus-${Date.now()}`,\n element,\n animation,\n fromState: startTransform,\n toState: focusTransform,\n startTime: performance.now(),\n duration\n };\n\n // Store focus data for later (including dimensions for unfocus)\n this.focusData = {\n element,\n originalState,\n focusTransform,\n originalZIndex,\n originalWidth,\n originalHeight,\n focusWidth: focusDimensions.width,\n focusHeight: focusDimensions.height\n };\n\n // Start continuous clip-path animation to follow dimension changes\n this.startClipPathAnimation(element, handle, true);\n\n return {\n element,\n originalState,\n animationHandle: handle,\n direction: 'in' as const,\n originalWidth,\n originalHeight\n };\n }\n\n /**\n * Start unfocus animation for an image using dimension-based zoom\n * Animates back to original dimensions for consistent behavior\n * @param fromDimensions - Optional starting dimensions (for mid-animation reversals)\n */\n private startUnfocusAnimation(\n element: HTMLElement,\n originalState: ImageLayout,\n fromTransform?: TransformParams,\n fromDimensions?: { width: number; height: number }\n ): AnimatingImage {\n // Set z-index for unfocusing (below incoming)\n element.style.zIndex = String(Z_INDEX.UNFOCUSING);\n\n // Cancel any existing animation\n this.animationEngine.cancelAllAnimations(element);\n\n // Get animation duration\n const duration = this.config.animationDuration ?? 600;\n\n // Remove focused classes but keep z-index high during animation\n // (z-index will be reset after animation completes)\n element.classList.remove('fbn-ic-focused');\n removeClassNameFromElement(element, this.focusedClassName);\n\n // Start from current focused state (or provided state for interrupted animations)\n const startTransform = fromTransform ?? this.focusData?.focusTransform ?? { x: 0, y: 0, rotation: 0, scale: 1 };\n const startWidth = fromDimensions?.width ?? this.focusData?.focusWidth ?? element.offsetWidth;\n const startHeight = fromDimensions?.height ?? this.focusData?.focusHeight ?? element.offsetHeight;\n\n // Target is original position and dimensions\n const toState: TransformParams = {\n x: 0,\n y: 0,\n rotation: originalState.rotation,\n scale: 1 // No scale - using dimensions\n };\n\n const targetWidth = this.focusData?.originalWidth ?? element.offsetWidth;\n const targetHeight = this.focusData?.originalHeight ?? element.offsetHeight;\n\n // Create dimension-based animation\n const animation = this.animateWithDimensions(\n element,\n startTransform,\n toState,\n startWidth,\n startHeight,\n targetWidth,\n targetHeight,\n duration\n );\n\n // Create animation handle\n const handle: AnimationHandle = {\n id: `unfocus-${Date.now()}`,\n element,\n animation,\n fromState: startTransform,\n toState: toState,\n startTime: performance.now(),\n duration\n };\n\n // Start continuous clip-path animation to follow dimension changes back to default\n this.startClipPathAnimation(element, handle, false);\n\n return {\n element,\n originalState,\n animationHandle: handle,\n direction: 'out' as const,\n originalWidth: targetWidth,\n originalHeight: targetHeight\n };\n }\n\n /**\n * Capture the current visual state of an element mid-animation, BEFORE cancelling.\n *\n * The computed matrix.e/f include the -50%/-50% centering offset resolved to pixels.\n * buildDimensionZoomTransform prepends its own translate(-50%,-50%), so passing raw\n * matrix.e/f doubles the centering and produces the wrong starting position.\n *\n * This method extracts the PURE positional offset (pureX = matrix.e + 0.5*midWidth)\n * and commits width/height/transform to inline styles before the animation is cancelled,\n * preventing any visual snap.\n *\n * Must be called while the animation is still running (offsetWidth reflects animated size).\n * Caller is responsible for calling animationEngine.cancelAllAnimations() afterwards.\n */\n private captureMidAnimationState(element: HTMLElement): {\n transform: TransformParams;\n dimensions: { width: number; height: number };\n } {\n const computed = getComputedStyle(element);\n const matrix = new DOMMatrix(computed.transform);\n const midWidth = element.offsetWidth;\n const midHeight = element.offsetHeight;\n\n // Remove the -50%/-50% centering that is baked into matrix.e/f\n const pureX = matrix.e + midWidth * 0.5;\n const pureY = matrix.f + midHeight * 0.5;\n const rotation = Math.atan2(matrix.b, matrix.a) * (180 / Math.PI);\n\n // Commit current visual state to inline styles so cancelling the animation\n // does not cause the element to snap to a different size/position\n element.style.width = `${midWidth}px`;\n element.style.height = `${midHeight}px`;\n element.style.transform = `translate(-50%, -50%) translate(${pureX}px, ${pureY}px) rotate(${rotation}deg)`;\n element.style.transition = 'none';\n\n return {\n transform: { x: pureX, y: pureY, rotation, scale: 1 },\n dimensions: { width: midWidth, height: midHeight }\n };\n }\n\n /**\n * Handle animation completion\n */\n private async waitForAnimation(handle: AnimationHandle): Promise<void> {\n try {\n await handle.animation.finished;\n } catch {\n // Animation was cancelled - this is expected during interruption\n }\n }\n\n /**\n * Reset an element instantly to its original position and dimensions (no animation)\n */\n private resetElementInstantly(\n element: HTMLElement,\n originalState: ImageLayout,\n originalZIndex: string,\n originalWidth?: number,\n originalHeight?: number\n ): void {\n // Cancel any active animation (including completed animations with fill: 'forwards')\n this.animationEngine.cancelAllAnimations(element);\n\n // Build transform string for original position (no scale - using dimensions)\n const transforms = ['translate(-50%, -50%)'];\n transforms.push('translate(0px, 0px)');\n transforms.push(`rotate(${originalState.rotation}deg)`);\n // No scale in transform - dimensions handle sizing\n\n element.style.transition = 'none';\n element.style.transform = transforms.join(' ');\n\n // Restore original dimensions if provided\n if (originalWidth !== undefined && originalHeight !== undefined) {\n element.style.width = `${originalWidth}px`;\n element.style.height = `${originalHeight}px`;\n }\n\n // Remove focused styling (clip-path will be updated when animating)\n this.removeFocusedStyling(element, originalZIndex);\n }\n\n /**\n * Focus (zoom) an image to center of container\n * Implements cross-animation when swapping focus\n */\n async focusImage(\n imageElement: HTMLElement,\n containerBounds: ContainerBounds,\n originalState: ImageLayout\n ): Promise<void> {\n // Same image clicked while already focused - unfocus it\n if (this.currentFocus === imageElement && this.state === ZoomState.FOCUSED) {\n return this.unfocusImage();\n }\n\n // Same image clicked while it's animating in - reverse to unfocus\n if (this.incoming?.element === imageElement && this.state === ZoomState.FOCUSING) {\n // Capture mid-animation state BEFORE cancelling (offsetWidth reflects animated size;\n // pure positional offset strips the -50% centering already baked into matrix.e/f)\n const { transform: fromTransform, dimensions: fromDimensions } =\n this.captureMidAnimationState(imageElement);\n this.animationEngine.cancelAllAnimations(imageElement);\n\n this.outgoing = this.startUnfocusAnimation(\n imageElement,\n this.incoming.originalState,\n fromTransform,\n fromDimensions\n );\n this.incoming = null;\n this.state = ZoomState.UNFOCUSING;\n\n await this.waitForAnimation(this.outgoing.animationHandle);\n this.removeFocusedStyling(this.outgoing.element, this.focusData?.originalZIndex || '');\n this.outgoing = null;\n this.currentFocus = null;\n this.focusData = null;\n this.state = ZoomState.IDLE;\n return;\n }\n\n // Increment generation to invalidate any previous in-flight calls\n const myGeneration = ++this.focusGeneration;\n\n switch (this.state) {\n case ZoomState.IDLE:\n // Simple focus - no current focus\n this.state = ZoomState.FOCUSING;\n this.incoming = this.startFocusAnimation(imageElement, containerBounds, originalState);\n\n await this.waitForAnimation(this.incoming.animationHandle);\n\n // Check if we're still the active generation\n if (this.focusGeneration !== myGeneration) return;\n\n this.currentFocus = imageElement;\n this.incoming = null;\n this.state = ZoomState.FOCUSED;\n break;\n\n case ZoomState.FOCUSED:\n // Cross-animation: unfocus current while focusing new\n this.state = ZoomState.CROSS_ANIMATING;\n\n // Start outgoing animation for currently focused image\n if (this.currentFocus && this.focusData) {\n this.outgoing = this.startUnfocusAnimation(\n this.currentFocus,\n this.focusData.originalState\n );\n }\n\n // Start incoming animation for new image\n this.incoming = this.startFocusAnimation(imageElement, containerBounds, originalState);\n\n // Wait for both animations\n await Promise.all([\n this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),\n this.waitForAnimation(this.incoming.animationHandle)\n ]);\n\n // Check if we're still the active generation\n if (this.focusGeneration !== myGeneration) {\n return;\n }\n\n // Cleanup outgoing\n if (this.outgoing) {\n const completedOutgoing = this.outgoing.element;\n this.removeFocusedStyling(completedOutgoing, this.outgoing.originalState.zIndex?.toString() || '');\n this.outgoing = null;\n this.onUnfocusComplete?.(completedOutgoing);\n }\n\n this.currentFocus = imageElement;\n this.incoming = null;\n this.state = ZoomState.FOCUSED;\n break;\n\n case ZoomState.FOCUSING:\n // New image clicked while another is focusing\n // Cancel the incoming and start new focus\n if (this.incoming) {\n this.animationEngine.cancelAnimation(this.incoming.animationHandle, false);\n this.resetElementInstantly(\n this.incoming.element,\n this.incoming.originalState,\n this.focusData?.originalZIndex || '',\n this.focusData?.originalWidth,\n this.focusData?.originalHeight\n );\n this.incoming = null;\n }\n\n // Start focusing the new image\n this.incoming = this.startFocusAnimation(imageElement, containerBounds, originalState);\n\n await this.waitForAnimation(this.incoming.animationHandle);\n\n // Check if we're still the active generation\n if (this.focusGeneration !== myGeneration) return;\n\n this.currentFocus = imageElement;\n this.incoming = null;\n this.state = ZoomState.FOCUSED;\n break;\n\n case ZoomState.UNFOCUSING:\n // New image clicked while current is unfocusing\n // Let outgoing continue, start cross-animation\n this.state = ZoomState.CROSS_ANIMATING;\n this.incoming = this.startFocusAnimation(imageElement, containerBounds, originalState);\n\n await Promise.all([\n this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),\n this.waitForAnimation(this.incoming.animationHandle)\n ]);\n\n // Check if we're still the active generation\n if (this.focusGeneration !== myGeneration) return;\n\n if (this.outgoing) {\n const completedOutgoing = this.outgoing.element;\n this.removeFocusedStyling(completedOutgoing, this.outgoing.originalState.zIndex?.toString() || '');\n this.outgoing = null;\n this.onUnfocusComplete?.(completedOutgoing);\n }\n\n this.currentFocus = imageElement;\n this.incoming = null;\n this.state = ZoomState.FOCUSED;\n break;\n\n case ZoomState.CROSS_ANIMATING:\n // Handle click during cross-animation\n\n // If clicking the same image that's animating in, ignore (it's already targeting focus)\n if (this.incoming?.element === imageElement) {\n return;\n }\n\n // If clicking the same image that's animating out, let it become the new focus target\n // (reverse direction - it should animate back to focused)\n if (this.outgoing?.element === imageElement) {\n // Capture mid-animation state for outgoing before cancelling\n const { transform: fromTransform, dimensions: fromDimensions } =\n this.captureMidAnimationState(imageElement);\n this.animationEngine.cancelAllAnimations(imageElement);\n\n // Redirect current incoming to become outgoing\n if (this.incoming) {\n const { transform: incomingFrom, dimensions: incomingFromDimensions } =\n this.captureMidAnimationState(this.incoming.element);\n this.animationEngine.cancelAllAnimations(this.incoming.element);\n this.outgoing = this.startUnfocusAnimation(\n this.incoming.element,\n this.incoming.originalState,\n incomingFrom,\n incomingFromDimensions\n );\n } else {\n this.outgoing = null;\n }\n\n // Start new incoming for the clicked (formerly outgoing) image\n // Use fromTransform and fromDimensions to continue from current state\n this.incoming = this.startFocusAnimation(imageElement, containerBounds, originalState, fromTransform, fromDimensions);\n\n await Promise.all([\n this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),\n this.waitForAnimation(this.incoming.animationHandle)\n ]);\n\n if (this.focusGeneration !== myGeneration) return;\n\n if (this.outgoing) {\n const completedOutgoing = this.outgoing.element;\n this.removeFocusedStyling(completedOutgoing, this.outgoing.originalState.zIndex?.toString() || '');\n this.outgoing = null;\n this.onUnfocusComplete?.(completedOutgoing);\n }\n\n this.currentFocus = imageElement;\n this.incoming = null;\n this.state = ZoomState.FOCUSED;\n return;\n }\n\n // Third different image clicked during cross-animation\n // 1. Reset outgoing instantly\n // 2. Redirect incoming to become outgoing\n // 3. Start new incoming\n\n // Reset outgoing instantly\n if (this.outgoing) {\n this.animationEngine.cancelAnimation(this.outgoing.animationHandle, false);\n this.resetElementInstantly(\n this.outgoing.element,\n this.outgoing.originalState,\n this.outgoing.originalState.zIndex?.toString() || '',\n this.outgoing.originalWidth,\n this.outgoing.originalHeight\n );\n this.outgoing = null;\n }\n\n // Redirect incoming to outgoing (animate from current position back to original)\n if (this.incoming) {\n const { transform: fromTransform, dimensions: fromDimensions } =\n this.captureMidAnimationState(this.incoming.element);\n this.animationEngine.cancelAllAnimations(this.incoming.element);\n\n this.outgoing = this.startUnfocusAnimation(\n this.incoming.element,\n this.incoming.originalState,\n fromTransform,\n fromDimensions\n );\n }\n\n // Start new incoming\n this.incoming = this.startFocusAnimation(imageElement, containerBounds, originalState);\n\n // Wait for both\n await Promise.all([\n this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),\n this.waitForAnimation(this.incoming.animationHandle)\n ]);\n\n // Check if we're still the active generation\n if (this.focusGeneration !== myGeneration) return;\n\n if (this.outgoing) {\n const completedOutgoing = this.outgoing.element;\n this.removeFocusedStyling(completedOutgoing, this.outgoing.originalState.zIndex?.toString() || '');\n this.outgoing = null;\n this.onUnfocusComplete?.(completedOutgoing);\n }\n\n this.currentFocus = imageElement;\n this.incoming = null;\n this.state = ZoomState.FOCUSED;\n break;\n }\n }\n\n /**\n * Unfocus current image, returning it to original position\n */\n async unfocusImage(): Promise<void> {\n // Already animating out - ignore duplicate requests (e.g. ESC pressed twice)\n if (this.state === ZoomState.UNFOCUSING) {\n return;\n }\n\n // Increment generation to invalidate any previous in-flight calls\n const myGeneration = ++this.focusGeneration;\n\n if (!this.currentFocus || !this.focusData) {\n // Handle case where we're in FOCUSING state\n if (this.incoming && this.state === ZoomState.FOCUSING) {\n const { transform: fromTransform, dimensions: fromDimensions } =\n this.captureMidAnimationState(this.incoming.element);\n this.animationEngine.cancelAllAnimations(this.incoming.element);\n\n this.outgoing = this.startUnfocusAnimation(\n this.incoming.element,\n this.incoming.originalState,\n fromTransform,\n fromDimensions\n );\n this.incoming = null;\n this.state = ZoomState.UNFOCUSING;\n\n await this.waitForAnimation(this.outgoing.animationHandle);\n\n if (this.focusGeneration !== myGeneration) return;\n\n const completedFocusing = this.outgoing.element;\n this.removeFocusedStyling(completedFocusing, this.focusData?.originalZIndex || '');\n this.outgoing = null;\n this.focusData = null;\n this.state = ZoomState.IDLE;\n this.onUnfocusComplete?.(completedFocusing);\n }\n return;\n }\n\n // Handle cross-animation cancellation (ESC during cross-animation)\n if (this.state === ZoomState.CROSS_ANIMATING) {\n // Cancel incoming and animate it back\n if (this.incoming) {\n const { transform: fromTransform, dimensions: fromDimensions } =\n this.captureMidAnimationState(this.incoming.element);\n this.animationEngine.cancelAllAnimations(this.incoming.element);\n\n // Start unfocus for incoming from its current position\n const incomingUnfocus = this.startUnfocusAnimation(\n this.incoming.element,\n this.incoming.originalState,\n fromTransform,\n fromDimensions\n );\n\n // Wait for both outgoing and the redirected incoming\n await Promise.all([\n this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),\n this.waitForAnimation(incomingUnfocus.animationHandle)\n ]);\n\n if (this.focusGeneration !== myGeneration) return;\n\n // Cleanup\n let outgoingElement: HTMLElement | null = null;\n if (this.outgoing) {\n outgoingElement = this.outgoing.element;\n this.removeFocusedStyling(outgoingElement, this.outgoing.originalState.zIndex?.toString() || '');\n }\n const incomingUnfocusElement = incomingUnfocus.element;\n this.removeFocusedStyling(incomingUnfocusElement, this.incoming.originalState.zIndex?.toString() || '');\n\n this.outgoing = null;\n this.incoming = null;\n this.currentFocus = null;\n this.focusData = null;\n this.state = ZoomState.IDLE;\n\n if (outgoingElement) this.onUnfocusComplete?.(outgoingElement);\n this.onUnfocusComplete?.(incomingUnfocusElement);\n return;\n }\n }\n\n // Normal unfocus from FOCUSED state\n this.state = ZoomState.UNFOCUSING;\n const element = this.currentFocus;\n const originalState = this.focusData.originalState;\n const originalZIndex = this.focusData.originalZIndex;\n\n this.outgoing = this.startUnfocusAnimation(element, originalState);\n\n await this.waitForAnimation(this.outgoing.animationHandle);\n\n if (this.focusGeneration !== myGeneration) return;\n\n this.removeFocusedStyling(element, originalZIndex);\n this.outgoing = null;\n this.currentFocus = null;\n this.focusData = null;\n this.state = ZoomState.IDLE;\n this.onUnfocusComplete?.(element);\n }\n\n /**\n * Swap focus from current image to a new one (alias for focusImage with cross-animation)\n */\n async swapFocus(\n newImageElement: HTMLElement,\n containerBounds: ContainerBounds,\n originalState: ImageLayout\n ): Promise<void> {\n return this.focusImage(newImageElement, containerBounds, originalState);\n }\n\n /**\n * Get currently focused image element\n */\n getCurrentFocus(): HTMLElement | null {\n return this.currentFocus;\n }\n\n /**\n * Check if an image is currently focused (stable state)\n */\n isFocused(imageElement: HTMLElement): boolean {\n return this.currentFocus === imageElement && this.state === ZoomState.FOCUSED;\n }\n\n /**\n * Check if an image is the target of current focus animation\n */\n isTargetingFocus(imageElement: HTMLElement): boolean {\n return this.incoming?.element === imageElement;\n }\n\n /**\n * Check if an image is involved in any focus/animation state\n * Returns true if the image is focused, animating in, or animating out\n * Useful for hover state management - don't apply hover to animating images\n */\n isInvolved(imageElement: HTMLElement): boolean {\n return (\n this.currentFocus === imageElement ||\n this.incoming?.element === imageElement ||\n this.outgoing?.element === imageElement\n );\n }\n\n /**\n * Apply a temporary horizontal drag offset to the focused image\n * Used during swipe gestures for visual feedback\n */\n setDragOffset(offset: number): void {\n if (!this.currentFocus || !this.focusData || this.state !== ZoomState.FOCUSED) return;\n\n const element = this.currentFocus;\n const focusTransform = this.focusData.focusTransform;\n\n // Build transform with additional horizontal offset\n const transforms: string[] = ['translate(-50%, -50%)'];\n const x = (focusTransform.x ?? 0) + offset;\n const y = focusTransform.y ?? 0;\n transforms.push(`translate(${x}px, ${y}px)`);\n if (focusTransform.rotation !== undefined) {\n transforms.push(`rotate(${focusTransform.rotation}deg)`);\n }\n\n element.style.transition = 'none';\n element.style.transform = transforms.join(' ');\n }\n\n /**\n * Clear the drag offset, optionally animating back to center\n * @param animate - If true, animate back to center; if false, snap instantly\n * @param duration - Animation duration in ms (default 150)\n */\n clearDragOffset(animate: boolean, duration: number = 150): void {\n if (!this.currentFocus || !this.focusData || this.state !== ZoomState.FOCUSED) return;\n\n const element = this.currentFocus;\n const focusTransform = this.focusData.focusTransform;\n\n // Build the centered transform (no offset)\n const transforms: string[] = ['translate(-50%, -50%)'];\n const x = focusTransform.x ?? 0;\n const y = focusTransform.y ?? 0;\n transforms.push(`translate(${x}px, ${y}px)`);\n if (focusTransform.rotation !== undefined) {\n transforms.push(`rotate(${focusTransform.rotation}deg)`);\n }\n const centeredTransform = transforms.join(' ');\n\n if (animate) {\n element.style.transition = `transform ${duration}ms ease-out`;\n element.style.transform = centeredTransform;\n // Clear transition after animation completes\n setTimeout(() => {\n if (this.currentFocus === element) {\n element.style.transition = 'none';\n }\n }, duration);\n } else {\n element.style.transition = 'none';\n element.style.transform = centeredTransform;\n }\n }\n\n /**\n * Reset zoom state (cancels all animations)\n */\n reset(): void {\n // Cancel any active animations\n if (this.outgoing) {\n this.animationEngine.cancelAnimation(this.outgoing.animationHandle, false);\n this.resetElementInstantly(\n this.outgoing.element,\n this.outgoing.originalState,\n this.outgoing.originalState.zIndex?.toString() || '',\n this.outgoing.originalWidth,\n this.outgoing.originalHeight\n );\n }\n\n if (this.incoming) {\n this.animationEngine.cancelAnimation(this.incoming.animationHandle, false);\n this.resetElementInstantly(\n this.incoming.element,\n this.incoming.originalState,\n this.focusData?.originalZIndex || '',\n this.focusData?.originalWidth,\n this.focusData?.originalHeight\n );\n }\n\n if (this.currentFocus && this.focusData) {\n this.resetElementInstantly(\n this.currentFocus,\n this.focusData.originalState,\n this.focusData.originalZIndex,\n this.focusData.originalWidth,\n this.focusData.originalHeight\n );\n }\n\n this.state = ZoomState.IDLE;\n this.currentFocus = null;\n this.focusData = null;\n this.outgoing = null;\n this.incoming = null;\n }\n}\n","/**\n * SwipeEngine.ts\n * Handles touch swipe gestures for navigating between focused images\n *\n * Public API:\n * - enable() - Start listening for touch events\n * - disable() - Stop listening (when no image focused)\n * - destroy() - Clean up all event listeners\n * - setDragCallback(callback) - Set callback for drag offset updates\n */\n\n// Constants for swipe detection\nconst SWIPE_THRESHOLD_PX = 50;\nconst SWIPE_VELOCITY_THRESHOLD = 0.5; // px/ms\nconst SWIPE_MIN_DISTANCE_FOR_VELOCITY = 20;\nconst DRAG_DAMPING = 0.3;\nconst SNAP_BACK_DURATION_MS = 150;\nconst HORIZONTAL_ANGLE_THRESHOLD_DEG = 30;\n\ninterface SwipeCallbacks {\n onNext: () => void;\n onPrev: () => void;\n onDragOffset: (offset: number) => void;\n onDragEnd: (navigated: boolean) => void;\n}\n\ninterface TouchState {\n startX: number;\n startY: number;\n startTime: number;\n currentX: number;\n isDragging: boolean;\n isHorizontalSwipe: boolean | null; // null = not determined yet\n}\n\nexport class SwipeEngine {\n private container: HTMLElement;\n private callbacks: SwipeCallbacks;\n private enabled: boolean = false;\n private touchState: TouchState | null = null;\n\n // Track recent touch activity to prevent click-outside from unfocusing\n private recentTouchTimestamp: number = 0;\n private static readonly TOUCH_CLICK_DELAY = 300; // ms to ignore clicks after touch\n\n // Bound event handlers for proper cleanup\n private boundTouchStart: (e: TouchEvent) => void;\n private boundTouchMove: (e: TouchEvent) => void;\n private boundTouchEnd: (e: TouchEvent) => void;\n private boundTouchCancel: (e: TouchEvent) => void;\n\n constructor(container: HTMLElement, callbacks: SwipeCallbacks) {\n this.container = container;\n this.callbacks = callbacks;\n\n // Bind handlers\n this.boundTouchStart = this.handleTouchStart.bind(this);\n this.boundTouchMove = this.handleTouchMove.bind(this);\n this.boundTouchEnd = this.handleTouchEnd.bind(this);\n this.boundTouchCancel = this.handleTouchCancel.bind(this);\n }\n\n /**\n * Start listening for touch events\n */\n enable(): void {\n if (this.enabled) return;\n this.enabled = true;\n\n // Set touch-action to allow vertical scroll but let JS handle horizontal\n this.container.style.touchAction = 'pan-y';\n\n this.container.addEventListener('touchstart', this.boundTouchStart, { passive: false });\n this.container.addEventListener('touchmove', this.boundTouchMove, { passive: false });\n this.container.addEventListener('touchend', this.boundTouchEnd, { passive: true });\n this.container.addEventListener('touchcancel', this.boundTouchCancel, { passive: true });\n }\n\n /**\n * Stop listening for touch events\n */\n disable(): void {\n if (!this.enabled) return;\n this.enabled = false;\n\n // Restore default touch-action\n this.container.style.touchAction = '';\n\n this.container.removeEventListener('touchstart', this.boundTouchStart);\n this.container.removeEventListener('touchmove', this.boundTouchMove);\n this.container.removeEventListener('touchend', this.boundTouchEnd);\n this.container.removeEventListener('touchcancel', this.boundTouchCancel);\n\n // Reset any in-progress drag\n if (this.touchState?.isDragging) {\n this.callbacks.onDragEnd(false);\n }\n this.touchState = null;\n }\n\n /**\n * Clean up all event listeners\n */\n destroy(): void {\n this.disable();\n }\n\n /**\n * Check if a touch interaction happened recently\n * Used to prevent click-outside from unfocusing immediately after touch\n */\n hadRecentTouch(): boolean {\n return Date.now() - this.recentTouchTimestamp < SwipeEngine.TOUCH_CLICK_DELAY;\n }\n\n private handleTouchStart(e: TouchEvent): void {\n if (e.touches.length !== 1) return;\n\n // Mark recent touch to prevent click-outside unfocus\n this.recentTouchTimestamp = Date.now();\n\n const touch = e.touches[0];\n this.touchState = {\n startX: touch.clientX,\n startY: touch.clientY,\n startTime: performance.now(),\n currentX: touch.clientX,\n isDragging: false,\n isHorizontalSwipe: null\n };\n }\n\n private handleTouchMove(e: TouchEvent): void {\n if (!this.touchState || e.touches.length !== 1) return;\n\n const touch = e.touches[0];\n const deltaX = touch.clientX - this.touchState.startX;\n const deltaY = touch.clientY - this.touchState.startY;\n\n // Determine swipe direction on first significant movement\n if (this.touchState.isHorizontalSwipe === null) {\n const totalDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n if (totalDistance > 10) {\n // Calculate angle from horizontal\n const angleRad = Math.atan2(Math.abs(deltaY), Math.abs(deltaX));\n const angleDeg = angleRad * (180 / Math.PI);\n this.touchState.isHorizontalSwipe = angleDeg <= HORIZONTAL_ANGLE_THRESHOLD_DEG;\n }\n }\n\n // If determined to be vertical, don't capture\n if (this.touchState.isHorizontalSwipe === false) {\n return;\n }\n\n // If horizontal swipe, prevent default and track drag\n if (this.touchState.isHorizontalSwipe === true) {\n e.preventDefault();\n this.touchState.isDragging = true;\n this.touchState.currentX = touch.clientX;\n\n // Apply damped offset to focused image\n const dampedOffset = deltaX * DRAG_DAMPING;\n this.callbacks.onDragOffset(dampedOffset);\n }\n }\n\n private handleTouchEnd(_e: TouchEvent): void {\n if (!this.touchState) return;\n\n // Update timestamp to prevent click-outside unfocus\n this.recentTouchTimestamp = Date.now();\n\n const deltaX = this.touchState.currentX - this.touchState.startX;\n const deltaTime = performance.now() - this.touchState.startTime;\n const velocity = Math.abs(deltaX) / deltaTime;\n const absDistance = Math.abs(deltaX);\n\n let navigated = false;\n\n // Only process if it was a horizontal swipe\n if (this.touchState.isHorizontalSwipe === true && this.touchState.isDragging) {\n // Check if swipe threshold met (distance or velocity)\n const thresholdMet =\n absDistance >= SWIPE_THRESHOLD_PX ||\n (velocity >= SWIPE_VELOCITY_THRESHOLD && absDistance >= SWIPE_MIN_DISTANCE_FOR_VELOCITY);\n\n if (thresholdMet) {\n navigated = true;\n if (deltaX < 0) {\n // Swipe left -> next image\n this.callbacks.onNext();\n } else {\n // Swipe right -> previous image\n this.callbacks.onPrev();\n }\n }\n }\n\n // Notify drag end (handles snap-back if not navigated)\n if (this.touchState.isDragging) {\n this.callbacks.onDragEnd(navigated);\n }\n\n this.touchState = null;\n }\n\n private handleTouchCancel(_e: TouchEvent): void {\n if (this.touchState?.isDragging) {\n this.callbacks.onDragEnd(false);\n }\n this.touchState = null;\n }\n}\n\nexport { SNAP_BACK_DURATION_MS };\n","/**\n * GoogleDriveLoader.ts\n * Loads images from a public Google Drive folder\n *\n * Public API:\n * - prepare(filter) - Async discovery of images\n * - imagesLength() - Get count of discovered images\n * - imageURLs() - Get ordered list of image URLs\n * - isPrepared() - Check if loader has been prepared\n *\n * Helper methods (for advanced usage):\n * - extractFolderId(folderUrl)\n * - manualImageUrls(imageIds)\n */\n\nimport type { ImageLoader, IImageFilter, GoogleDriveResponse, GoogleDriveLoaderInnerConfig, GoogleDriveSource } from '../config/types';\n\nexport class GoogleDriveLoader implements ImageLoader {\n private apiKey: string;\n private apiEndpoint: string;\n private debugLogging: boolean;\n private sources: GoogleDriveSource[];\n\n // State for new interface\n private _prepared: boolean = false;\n private _discoveredUrls: string[] = [];\n\n constructor(config: GoogleDriveLoaderInnerConfig) {\n this.apiKey = config.apiKey ?? '';\n this.apiEndpoint = config.apiEndpoint ?? 'https://www.googleapis.com/drive/v3/files';\n this.debugLogging = config.debugLogging ?? false;\n this.sources = config.sources ?? [];\n\n // Validate that we have sources configured\n if (!this.sources || this.sources.length === 0) {\n throw new Error('GoogleDriveLoader requires at least one source to be configured');\n }\n }\n\n /**\n * Prepare the loader by discovering all images from configured sources\n * @param filter - Filter to apply to discovered images\n */\n async prepare(filter: IImageFilter): Promise<void> {\n this._discoveredUrls = [];\n\n for (const source of this.sources) {\n if ('folders' in source) {\n for (const folderUrl of source.folders) {\n const recursive = source.recursive !== undefined ? source.recursive : true;\n const urls = await this.loadFromFolder(folderUrl, filter, recursive);\n this._discoveredUrls.push(...urls);\n }\n } else if ('files' in source) {\n const urls = await this.loadFiles(source.files, filter);\n this._discoveredUrls.push(...urls);\n }\n }\n\n this._prepared = true;\n }\n\n /**\n * Get the number of discovered images\n * @throws Error if called before prepare()\n */\n imagesLength(): number {\n if (!this._prepared) {\n throw new Error('GoogleDriveLoader.imagesLength() called before prepare()');\n }\n return this._discoveredUrls.length;\n }\n\n /**\n * Get the ordered list of image URLs\n * @throws Error if called before prepare()\n */\n imageURLs(): string[] {\n if (!this._prepared) {\n throw new Error('GoogleDriveLoader.imageURLs() called before prepare()');\n }\n return [...this._discoveredUrls];\n }\n\n /**\n * Check if the loader has been prepared\n */\n isPrepared(): boolean {\n return this._prepared;\n }\n\n /**\n * Extract folder ID from various Google Drive URL formats\n * @param folderUrl - Google Drive folder URL\n * @returns Folder ID or null if invalid\n */\n extractFolderId(folderUrl: string): string | null {\n // Handle various URL formats:\n // https://drive.google.com/drive/folders/FOLDER_ID\n // https://drive.google.com/drive/folders/FOLDER_ID?usp=sharing\n\n const patterns = [\n /\\/folders\\/([a-zA-Z0-9_-]+)/, // Standard format\n /id=([a-zA-Z0-9_-]+)/ // Alternative format\n ];\n\n for (const pattern of patterns) {\n const match = folderUrl.match(pattern);\n if (match && match[1]) {\n return match[1];\n }\n }\n\n return null;\n }\n\n /**\n * Load images from a Google Drive folder\n * @param folderUrl - Google Drive folder URL\n * @param filter - Filter to apply to discovered images\n * @param recursive - Whether to include images from subfolders\n * @returns Promise resolving to array of image URLs\n */\n private async loadFromFolder(folderUrl: string, filter: IImageFilter, recursive: boolean = true): Promise<string[]> {\n const folderId = this.extractFolderId(folderUrl);\n\n if (!folderId) {\n throw new Error('Invalid Google Drive folder URL. Please check the URL format.');\n }\n\n // If no API key is configured, use direct link method\n if (!this.apiKey || this.apiKey === 'YOUR_API_KEY_HERE') {\n return this.loadImagesDirectly(folderId, filter);\n }\n\n try {\n if (recursive) {\n return await this.loadImagesRecursively(folderId, filter);\n } else {\n return await this.loadImagesFromSingleFolder(folderId, filter);\n }\n } catch (error) {\n console.error('Error loading from Google Drive API:', error);\n // Fallback to direct link method\n return this.loadImagesDirectly(folderId, filter);\n }\n }\n\n /**\n * Load images from a single folder (non-recursive)\n * @param folderId - Google Drive folder ID\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of image URLs\n */\n private async loadImagesFromSingleFolder(folderId: string, filter: IImageFilter): Promise<string[]> {\n const imageUrls: string[] = [];\n\n // Query for all files in this folder\n const query = `'${folderId}' in parents and trashed=false`;\n const fields = 'files(id,name,mimeType,thumbnailLink)';\n const url = `${this.apiEndpoint}?q=${encodeURIComponent(query)}&fields=${fields}&key=${this.apiKey}`;\n\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`API request failed: ${response.status} ${response.statusText}`);\n }\n\n const data: GoogleDriveResponse = await response.json();\n\n // Filter for valid image files only using the provided filter\n const validFiles = data.files.filter(file =>\n file.mimeType.startsWith('image/') && filter.isAllowed(file.name)\n );\n\n this.log(`Found ${validFiles.length} images in folder ${folderId} (non-recursive)`);\n\n // Add image URLs\n validFiles.forEach(file => {\n imageUrls.push(`https://lh3.googleusercontent.com/d/${file.id}=s1600`);\n this.log(`Added file: ${file.name}`);\n });\n\n return imageUrls;\n }\n\n /**\n * Load specific files by their URLs or IDs\n * @param fileUrls - Array of Google Drive file URLs or IDs\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of image URLs\n */\n private async loadFiles(fileUrls: string[], filter: IImageFilter): Promise<string[]> {\n const imageUrls: string[] = [];\n\n for (const fileUrl of fileUrls) {\n const fileId = this.extractFileId(fileUrl);\n\n if (!fileId) {\n this.log(`Skipping invalid file URL: ${fileUrl}`);\n continue;\n }\n\n // Validate it's an image file\n if (this.apiKey && this.apiKey !== 'YOUR_API_KEY_HERE') {\n try {\n // Get file metadata to verify it's an image\n const metadataUrl = `${this.apiEndpoint}/${fileId}?fields=name,mimeType&key=${this.apiKey}`;\n const response = await fetch(metadataUrl);\n\n if (response.ok) {\n const metadata = await response.json();\n if (metadata.mimeType.startsWith('image/') && filter.isAllowed(metadata.name)) {\n imageUrls.push(`https://lh3.googleusercontent.com/d/${fileId}=s1600`);\n this.log(`Added file: ${metadata.name}`);\n } else {\n this.log(`Skipping non-image file: ${metadata.name} (${metadata.mimeType})`);\n }\n } else {\n this.log(`Failed to fetch metadata for file ${fileId}: ${response.status}`);\n }\n } catch (error) {\n this.log(`Error fetching metadata for file ${fileId}:`, error);\n }\n } else {\n // Without API key, assume it's valid and add it\n imageUrls.push(`https://lh3.googleusercontent.com/d/${fileId}=s1600`);\n }\n }\n\n return imageUrls;\n }\n\n /**\n * Extract file ID from Google Drive file URL\n * @param fileUrl - Google Drive file URL or file ID\n * @returns File ID or null if invalid\n */\n private extractFileId(fileUrl: string): string | null {\n // Handle various URL formats:\n // https://drive.google.com/file/d/FILE_ID/view\n // https://drive.google.com/open?id=FILE_ID\n // FILE_ID (raw ID)\n\n // If it looks like a raw ID (no slashes or protocol), return it\n if (!/[/:.]/.test(fileUrl)) {\n return fileUrl;\n }\n\n const patterns = [\n /\\/file\\/d\\/([a-zA-Z0-9_-]+)/, // Standard file format\n /\\/open\\?id=([a-zA-Z0-9_-]+)/, // Alternative format\n /id=([a-zA-Z0-9_-]+)/ // Generic id parameter\n ];\n\n for (const pattern of patterns) {\n const match = fileUrl.match(pattern);\n if (match && match[1]) {\n return match[1];\n }\n }\n\n return null;\n }\n\n /**\n * Recursively load images from a folder and all its subfolders\n * @param folderId - Google Drive folder ID\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of image URLs\n */\n private async loadImagesRecursively(folderId: string, filter: IImageFilter): Promise<string[]> {\n const imageUrls: string[] = [];\n\n // Query for all files in this folder\n const query = `'${folderId}' in parents and trashed=false`;\n // Request thumbnailLink for PDFs\n const fields = 'files(id,name,mimeType,thumbnailLink)';\n const url = `${this.apiEndpoint}?q=${encodeURIComponent(query)}&fields=${fields}&key=${this.apiKey}`;\n\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`API request failed: ${response.status} ${response.statusText}`);\n }\n\n const data: GoogleDriveResponse = await response.json();\n\n // Separate images and folders using the provided filter\n const validFiles = data.files.filter(file =>\n file.mimeType.startsWith('image/') && filter.isAllowed(file.name)\n );\n\n const subfolders = data.files.filter(file =>\n file.mimeType === 'application/vnd.google-apps.folder'\n );\n\n this.log(`Found ${data.files.length} total items in folder ${folderId}`);\n // Log details of all files to see what we are missing\n data.files.forEach(f => this.log(` - File: ${f.name} (${f.mimeType})`));\n\n this.log(`- ${validFiles.length} valid files (images only)`);\n this.log(`- ${subfolders.length} subfolders`);\n\n // Add image URLs from this folder\n validFiles.forEach(file => {\n // Use the reliable thumbnail/preview endpoint for both Images and PDFs\n // This works for public folders and handles file format conversion automatically\n // 'sz=w1000' requests a high-quality preview (1000px width)\n // detailed explanation:\n // 1. \"drive.google.com\" is blocked by ad-blockers (net::ERR_BLOCKED_BY_CLIENT)\n // 2. The API's \"thumbnailLink\" is a signed URL that can expire or fail 403.\n // 3. \"lh3.googleusercontent.com/d/{ID}\" is the permanent CDN link structure.\n // It bypasses the domain block AND the signing issues.\n imageUrls.push(`https://lh3.googleusercontent.com/d/${file.id}=s1600`);\n\n this.log(`Added file: ${file.name}`);\n });\n\n // Recursively process subfolders\n for (const folder of subfolders) {\n this.log(`Loading images from subfolder: ${folder.name}`);\n const subfolderImages = await this.loadImagesRecursively(folder.id, filter);\n imageUrls.push(...subfolderImages);\n }\n\n return imageUrls;\n }\n\n /**\n * Direct loading method (no API key required, but less reliable)\n * Uses embedded folder view to scrape image IDs\n * @param folderId - Google Drive folder ID\n * @param filter - Filter to apply (not used in fallback mode)\n * @returns Promise resolving to array of image URLs\n */\n private async loadImagesDirectly(folderId: string, _filter: IImageFilter): Promise<string[]> {\n // For now, we'll return a method that requires the user to manually provide image IDs\n // or we construct URLs based on a known pattern\n\n // This is a fallback - in production, you'd want to use the API\n // For demo purposes, we can try to fetch the folder page and extract image IDs\n\n try {\n // Attempt to fetch folder page (CORS may block this)\n const folderUrl = `https://drive.google.com/embeddedfolderview?id=${folderId}`;\n const response = await fetch(folderUrl, { mode: 'cors' });\n\n if (!response.ok) {\n throw new Error('Cannot access folder directly (CORS or permissions issue)');\n }\n\n const html = await response.text();\n\n // Try to extract image IDs from HTML (this is fragile and may break)\n const imageIdPattern = /\\/file\\/d\\/([a-zA-Z0-9_-]+)/g;\n const matches = [...html.matchAll(imageIdPattern)];\n const imageIds = [...new Set(matches.map(m => m[1]))];\n\n const imageUrls = imageIds.map(id =>\n `https://drive.google.com/uc?export=view&id=${id}`\n );\n\n return imageUrls;\n\n } catch (error) {\n console.error('Direct loading failed:', error);\n throw new Error(\n 'Unable to load images. Please ensure:\\n' +\n '1. The folder is shared publicly (Anyone with the link can view)\\n' +\n '2. The folder contains image files\\n' +\n '3. Consider adding a Google Drive API key in config.js for better reliability'\n );\n }\n }\n\n /**\n * Manually add image URLs (for testing or when auto-loading fails)\n * @param imageIds - Array of Google Drive file IDs\n * @returns Array of direct image URLs\n */\n manualImageUrls(imageIds: string[]): string[] {\n return imageIds.map(id => `https://drive.google.com/uc?export=view&id=${id}`);\n }\n\n /**\n * Debug logging helper\n * @param args - Arguments to log\n */\n private log(...args: unknown[]): void {\n if (this.debugLogging && typeof console !== 'undefined') {\n console.log(...args);\n }\n }\n}\n","/**\n * StaticImageLoader.ts\n * Loads images from predefined URL sources and local paths\n * Compatible with ImageCloud's loader interface\n *\n * Public API:\n * - prepare(filter) - Async discovery of images\n * - imagesLength() - Get count of discovered images\n * - imageURLs() - Get ordered list of image URLs\n * - isPrepared() - Check if loader has been prepared\n */\n\nimport type { ImageLoader, IImageFilter, StaticSource, StaticLoaderInnerConfig } from '../config/types';\n\nexport class StaticImageLoader implements ImageLoader {\n private validateUrls: boolean;\n private validationTimeout: number;\n private validationMethod: 'head' | 'simple' | 'none';\n private sources: StaticSource[];\n private debugLogging: boolean;\n\n // State for new interface\n private _prepared: boolean = false;\n private _discoveredUrls: string[] = [];\n\n constructor(config: StaticLoaderInnerConfig) {\n this.validateUrls = config.validateUrls !== false;\n this.validationTimeout = config.validationTimeout ?? 5000;\n this.validationMethod = config.validationMethod ?? 'head';\n this.debugLogging = config.debugLogging ?? false;\n this.sources = config.sources ?? [];\n\n // Validate that we have sources configured\n if (!this.sources || this.sources.length === 0) {\n throw new Error('StaticImageLoader requires at least one source to be configured');\n }\n\n this.log('StaticImageLoader initialized with config:', config);\n }\n\n /**\n * Prepare the loader by discovering all images from configured sources\n * @param filter - Filter to apply to discovered images\n */\n async prepare(filter: IImageFilter): Promise<void> {\n this._discoveredUrls = [];\n\n this.log(`Processing ${this.sources.length} source(s)`);\n\n // Process sources sequentially to preserve order\n for (const source of this.sources) {\n try {\n const urls = await this.processSource(source, filter);\n this._discoveredUrls.push(...urls);\n } catch (error) {\n console.warn('Failed to process source:', source, error);\n // Continue processing other sources\n }\n }\n\n this._prepared = true;\n this.log(`Successfully loaded ${this._discoveredUrls.length} image(s)`);\n }\n\n /**\n * Get the number of discovered images\n * @throws Error if called before prepare()\n */\n imagesLength(): number {\n if (!this._prepared) {\n throw new Error('StaticImageLoader.imagesLength() called before prepare()');\n }\n return this._discoveredUrls.length;\n }\n\n /**\n * Get the ordered list of image URLs\n * @throws Error if called before prepare()\n */\n imageURLs(): string[] {\n if (!this._prepared) {\n throw new Error('StaticImageLoader.imageURLs() called before prepare()');\n }\n return [...this._discoveredUrls];\n }\n\n /**\n * Check if the loader has been prepared\n */\n isPrepared(): boolean {\n return this._prepared;\n }\n\n /**\n * Process a single source object using shape-based detection\n * @param source - Source configuration detected by key presence\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of valid URLs from this source\n */\n private async processSource(source: StaticSource, filter: IImageFilter): Promise<string[]> {\n if (!source) {\n console.warn('Invalid source object:', source);\n return [];\n }\n\n if ('urls' in source) {\n return await this.processUrls(source.urls, filter);\n } else if ('path' in source) {\n return await this.processPath(source.path, source.files, filter);\n } else if ('json' in source) {\n return await this.processJson(source.json, filter);\n } else {\n console.warn('Unknown source shape:', source);\n return [];\n }\n }\n\n /**\n * Process a list of direct URLs\n * @param urls - Array of image URLs\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of validated URLs\n */\n private async processUrls(urls: string[], filter: IImageFilter): Promise<string[]> {\n if (!Array.isArray(urls)) {\n console.warn('URLs must be an array:', urls);\n return [];\n }\n\n const validUrls: string[] = [];\n\n for (const url of urls) {\n // Apply filter based on URL filename\n const filename = url.split('/').pop() || url;\n if (!filter.isAllowed(filename)) {\n this.log(`Skipping filtered URL: ${url}`);\n continue;\n }\n\n if (this.validateUrls) {\n const isValid = await this.validateUrl(url);\n if (isValid) {\n validUrls.push(url);\n } else {\n console.warn(`Skipping invalid/missing URL: ${url}`);\n }\n } else {\n // No validation - add all URLs\n validUrls.push(url);\n }\n }\n\n return validUrls;\n }\n\n /**\n * Process a path-based source\n * @param basePath - Base path (relative or absolute)\n * @param files - Array of filenames\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of validated URLs\n */\n private async processPath(basePath: string, files: string[], filter: IImageFilter): Promise<string[]> {\n\n if (!Array.isArray(files)) {\n console.warn('files must be an array:', files);\n return [];\n }\n\n const validUrls: string[] = [];\n\n for (const file of files) {\n // Apply filter based on filename\n if (!filter.isAllowed(file)) {\n this.log(`Skipping filtered file: ${file}`);\n continue;\n }\n\n const url = this.constructUrl(basePath, file);\n\n if (this.validateUrls) {\n const isValid = await this.validateUrl(url);\n if (isValid) {\n validUrls.push(url);\n } else {\n console.warn(`Skipping invalid/missing file: ${url}`);\n }\n } else {\n // No validation - add all URLs\n validUrls.push(url);\n }\n }\n\n return validUrls;\n }\n\n /**\n * Process a JSON endpoint source\n * Fetches a JSON endpoint that returns { images: string[] }\n * @param url - JSON endpoint URL\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of validated URLs\n */\n private async processJson(url: string, filter: IImageFilter): Promise<string[]> {\n\n this.log(`Fetching JSON endpoint: ${url}`);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 10000);\n\n try {\n const response = await fetch(url, { signal: controller.signal });\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status} fetching ${url}`);\n }\n\n const data = await response.json();\n\n if (!data || !Array.isArray(data.images)) {\n throw new Error(`JSON source must return JSON with shape { \"images\": [\"url1\", \"url2\", ...] }`);\n }\n\n this.log(`JSON endpoint returned ${data.images.length} image(s)`);\n\n // Process the URLs through the standard URL processing pipeline\n return await this.processUrls(data.images, filter);\n } catch (error) {\n clearTimeout(timeoutId);\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Timeout fetching JSON endpoint: ${url}`);\n }\n throw error;\n }\n }\n\n /**\n * Validate a single URL using HEAD request\n * @param url - URL to validate\n * @returns Promise resolving to true if valid and accessible\n */\n private async validateUrl(url: string): Promise<boolean> {\n if (this.validationMethod === 'none') {\n return true;\n }\n\n if (this.validationMethod === 'simple') {\n // Basic URL format check\n try {\n if (typeof window !== 'undefined') {\n new URL(url, window.location.origin);\n } else {\n new URL(url);\n }\n return true;\n } catch {\n return false;\n }\n }\n\n // validationMethod === 'head' (default)\n // For cross-origin URLs, we can't validate due to CORS\n // So we only validate same-origin URLs\n if (typeof window === 'undefined') {\n return true; // In non-browser environment, assume valid\n }\n\n const isSameOrigin = url.startsWith(window.location.origin) ||\n url.startsWith('/');\n\n if (!isSameOrigin) {\n // Cross-origin URL - assume valid, can't validate due to CORS\n this.log(`Skipping validation for cross-origin URL: ${url}`);\n return true;\n }\n\n // Same-origin URL - validate with HEAD request\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.validationTimeout);\n\n const response = await fetch(url, {\n method: 'HEAD',\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return true;\n } else {\n this.log(`Validation failed for ${url}: HTTP ${response.status}`);\n return false;\n }\n\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n this.log(`Validation timeout for ${url}`);\n } else {\n this.log(`Validation failed for ${url}:`, error.message);\n }\n }\n return false;\n }\n }\n\n /**\n * Construct full URL from basePath and filename\n * @param basePath - Base path (relative or absolute)\n * @param filename - Filename to append\n * @returns Complete URL\n */\n private constructUrl(basePath: string, filename: string): string {\n // Remove trailing slash from basePath\n const cleanBase = basePath.replace(/\\/$/, '');\n\n // Check if basePath is absolute URL\n if (this.isAbsoluteUrl(basePath)) {\n return `${cleanBase}/${filename}`;\n }\n\n // Relative path - prepend current origin\n if (typeof window === 'undefined') {\n return `${cleanBase}/${filename}`; // In non-browser environment, return as-is\n }\n\n const origin = window.location.origin;\n // Ensure basePath starts with /\n const normalizedPath = basePath.startsWith('/') ? basePath : '/' + basePath;\n const cleanPath = normalizedPath.replace(/\\/$/, '');\n\n return `${origin}${cleanPath}/${filename}`;\n }\n\n /**\n * Check if URL is absolute (contains protocol)\n * @param url - URL to check\n * @returns True if absolute URL\n */\n private isAbsoluteUrl(url: string): boolean {\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Debug logging helper\n * @param args - Arguments to log\n */\n private log(...args: unknown[]): void {\n if (this.debugLogging && typeof console !== 'undefined') {\n console.log(...args);\n }\n }\n}\n","/**\n * CompositeLoader.ts\n * Combines multiple image loaders and loads them in parallel\n *\n * Public API:\n * - prepare(filter) - Async discovery of images from all loaders in parallel\n * - imagesLength() - Get combined count of discovered images\n * - imageURLs() - Get combined ordered list of image URLs\n * - isPrepared() - Check if loader has been prepared\n */\n\nimport type { ImageLoader, IImageFilter } from '../config/types';\n\nexport interface CompositeLoaderConfig {\n loaders: ImageLoader[];\n debugLogging?: boolean;\n}\n\nexport class CompositeLoader implements ImageLoader {\n private loaders: ImageLoader[];\n private debugLogging: boolean;\n\n // State for interface\n private _prepared: boolean = false;\n private _discoveredUrls: string[] = [];\n\n constructor(config: CompositeLoaderConfig) {\n this.loaders = config.loaders;\n this.debugLogging = config.debugLogging ?? false;\n\n // Validate that we have at least one loader\n if (!this.loaders || this.loaders.length === 0) {\n throw new Error('CompositeLoader requires at least one loader to be configured');\n }\n\n this.log(`CompositeLoader initialized with ${this.loaders.length} loader(s)`);\n }\n\n /**\n * Prepare all loaders in parallel and combine their results\n * @param filter - Filter to apply to discovered images\n */\n async prepare(filter: IImageFilter): Promise<void> {\n this._discoveredUrls = [];\n\n this.log(`Preparing ${this.loaders.length} loader(s) in parallel`);\n\n // Prepare all loaders in parallel\n const preparePromises = this.loaders.map((loader, index) => {\n return loader.prepare(filter).then(() => {\n this.log(`Loader ${index} prepared with ${loader.imagesLength()} images`);\n }).catch(error => {\n console.warn(`Loader ${index} failed to prepare:`, error);\n // Continue with other loaders even if one fails\n });\n });\n\n await Promise.all(preparePromises);\n\n // Combine URLs from all prepared loaders (preserves order of loaders array)\n for (const loader of this.loaders) {\n if (loader.isPrepared()) {\n const urls = loader.imageURLs();\n this._discoveredUrls.push(...urls);\n }\n }\n\n this._prepared = true;\n this.log(`CompositeLoader prepared with ${this._discoveredUrls.length} total images`);\n }\n\n /**\n * Get the combined number of discovered images\n * @throws Error if called before prepare()\n */\n imagesLength(): number {\n if (!this._prepared) {\n throw new Error('CompositeLoader.imagesLength() called before prepare()');\n }\n return this._discoveredUrls.length;\n }\n\n /**\n * Get the combined ordered list of image URLs\n * @throws Error if called before prepare()\n */\n imageURLs(): string[] {\n if (!this._prepared) {\n throw new Error('CompositeLoader.imageURLs() called before prepare()');\n }\n return [...this._discoveredUrls];\n }\n\n /**\n * Check if the loader has been prepared\n */\n isPrepared(): boolean {\n return this._prepared;\n }\n\n /**\n * Debug logging helper\n * @param args - Arguments to log\n */\n private log(...args: unknown[]): void {\n if (this.debugLogging && typeof console !== 'undefined') {\n console.log('[CompositeLoader]', ...args);\n }\n }\n}\n","/**\n * ImageFilter.ts\n * Filters images by extension, designed for future extensibility\n * (e.g., size filters, date filters, etc.)\n */\n\nexport class ImageFilter {\n private allowedExtensions: string[];\n\n /**\n * Create a new ImageFilter\n * @param extensions - Array of allowed file extensions (without dots)\n * Defaults to common image formats if not provided\n */\n constructor(extensions?: string[]) {\n this.allowedExtensions = extensions || [\n 'jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'\n ];\n }\n\n /**\n * Check if a filename has an allowed extension\n * @param filename - The filename to check (can include path or query string)\n * @returns True if the file extension is allowed\n */\n isAllowed(filename: string): boolean {\n // Remove query string if present (for URLs like image.jpg?w=800)\n const withoutQuery = filename.split('?')[0];\n const extension = withoutQuery.split('.').pop()?.toLowerCase();\n return extension ? this.allowedExtensions.includes(extension) : false;\n }\n\n /**\n * Get the list of allowed extensions\n * @returns Array of allowed extensions\n */\n getAllowedExtensions(): string[] {\n return [...this.allowedExtensions];\n }\n\n // Future expansion methods:\n // isAllowedSize(sizeBytes: number): boolean\n // isAllowedDate(date: Date): boolean\n // isAllowedDimensions(width: number, height: number): boolean\n}\n","/**\n * Minimal functional CSS required for the library to work.\n * Injected automatically - no external CSS file needed.\n */\nexport const FUNCTIONAL_CSS = `\n.fbn-ic-gallery {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n perspective: 1000px;\n}\n\n.fbn-ic-image {\n position: absolute;\n cursor: pointer;\n transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1),\n box-shadow 0.6s cubic-bezier(0.4, 0, 0.2, 1),\n filter 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n border 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n outline 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n z-index 0s 0.6s;\n will-change: transform;\n user-select: none;\n backface-visibility: hidden;\n -webkit-backface-visibility: hidden;\n}\n\n.fbn-ic-image.fbn-ic-focused {\n z-index: 1000;\n transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1),\n box-shadow 0.6s cubic-bezier(0.4, 0, 0.2, 1),\n filter 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n border 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n outline 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n z-index 0s 0s;\n will-change: auto;\n}\n\n.fbn-ic-counter {\n position: fixed;\n bottom: 24px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10001;\n pointer-events: none;\n}\n\n.fbn-ic-gallery:focus,\n.fbn-ic-gallery.fbn-ic-has-focus {\n outline: 2px solid rgba(147, 197, 253, 0.8);\n outline-offset: -4px;\n}\n.fbn-ic-gallery.fbn-ic-suppress-outline:focus {\n outline: none;\n}\n.fbn-ic-gallery.fbn-ic-suppress-outline.fbn-ic-has-focus {\n outline: 2px solid rgba(99, 102, 241, 0.6);\n outline-offset: -4px;\n}\n\n.fbn-ic-nav-btn {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n z-index: 10001;\n cursor: pointer;\n border: none;\n background: none;\n padding: 0;\n line-height: 1;\n}\n.fbn-ic-nav-btn-prev {\n left: 12px;\n}\n.fbn-ic-nav-btn-next {\n right: 12px;\n}\n\n.fbn-ic-hidden {\n display: none !important;\n}\n`;\n\n/**\n * Inject functional styles into document head.\n * Idempotent - safe to call multiple times.\n */\nexport function injectFunctionalStyles(): void {\n if (typeof document === 'undefined') return;\n const id = 'fbn-ic-functional-styles';\n if (document.getElementById(id)) return;\n const style = document.createElement('style');\n style.id = id;\n style.textContent = FUNCTIONAL_CSS;\n document.head.appendChild(style);\n}\n","/**\n * ImageCloud.ts\n * Main application class\n * Manages initialization and coordination of the interactive image cloud\n */\n\nimport type { ImageCloudOptions, ImageCloudConfig, ImageLayout, ContainerBounds, ImageLoader, EntryAnimationConfig, LoaderEntry, SharedLoaderConfig, StaticLoaderInnerConfig, GoogleDriveLoaderInnerConfig } from './config/types';\nimport { mergeConfig, DEFAULT_CONFIG } from './config/defaults';\nimport { AnimationEngine } from './engines/AnimationEngine';\nimport { EntryAnimationEngine } from './engines/EntryAnimationEngine';\nimport { IdleAnimationEngine } from './engines/IdleAnimationEngine';\nimport { LayoutEngine } from './engines/LayoutEngine';\nimport { ZoomEngine } from './engines/ZoomEngine';\nimport { SwipeEngine, SNAP_BACK_DURATION_MS } from './engines/SwipeEngine';\nimport { animatePath } from './engines/PathAnimator';\nimport { GoogleDriveLoader } from './loaders/GoogleDriveLoader';\nimport { StaticImageLoader } from './loaders/StaticImageLoader';\nimport { CompositeLoader } from './loaders/CompositeLoader';\nimport { ImageFilter } from './loaders/ImageFilter';\nimport { buildStyleProperties, applyStylesToElementWithState, applyClassNameToElement, removeClassNameFromElement, StyleProperties } from './utils/styleUtils';\nimport { injectFunctionalStyles } from './styles/functionalStyles';\n\nexport class ImageCloud {\n private containerId: string | null;\n private containerRef: HTMLElement | null;\n\n // Internal state\n private fullConfig: ImageCloudConfig;\n private imagesLoaded: boolean;\n private imageElements: HTMLImageElement[];\n private imageLayouts: ImageLayout[];\n private currentImageHeight: number;\n private currentFocusIndex: number | null;\n private hoveredImage: { element: HTMLImageElement; layout: ImageLayout } | null;\n private resizeTimeout: number | null;\n private displayQueue: HTMLImageElement[];\n private queueInterval: number | null;\n private loadGeneration: number;\n\n // Precomputed styling\n private defaultStyles: StyleProperties;\n private defaultClassName: string | string[] | undefined;\n private hoverClassName: string | string[] | undefined;\n\n // Modules\n private animationEngine: AnimationEngine;\n private entryAnimationEngine: EntryAnimationEngine;\n private idleAnimationEngine: IdleAnimationEngine | null;\n private layoutEngine: LayoutEngine;\n private zoomEngine: ZoomEngine;\n private swipeEngine: SwipeEngine | null;\n private imageLoader: ImageLoader;\n private imageFilter: ImageFilter;\n\n // DOM Elements\n private containerEl: HTMLElement | null;\n private loadingEl: HTMLElement | null;\n private errorEl: HTMLElement | null;\n private loadingElAutoCreated: boolean;\n private errorElAutoCreated: boolean;\n private counterEl: HTMLElement | null;\n private counterElAutoCreated: boolean;\n private prevButtonEl: HTMLElement | null;\n private nextButtonEl: HTMLElement | null;\n private prevButtonElAutoCreated: boolean;\n private nextButtonElAutoCreated: boolean;\n\n constructor(options: ImageCloudOptions = {}) {\n this.fullConfig = mergeConfig(options);\n\n // Container can be a string ID or an HTMLElement reference\n if (options.container instanceof HTMLElement) {\n this.containerRef = options.container;\n this.containerId = null;\n } else {\n this.containerRef = null;\n this.containerId = options.container || 'imageCloud';\n }\n\n // Internal state\n this.imagesLoaded = false;\n this.imageElements = [];\n this.imageLayouts = [];\n this.currentImageHeight = 225;\n this.currentFocusIndex = null;\n this.hoveredImage = null;\n this.resizeTimeout = null;\n this.displayQueue = [];\n this.queueInterval = null;\n this.loadGeneration = 0;\n this.loadingElAutoCreated = false;\n this.errorElAutoCreated = false;\n this.counterEl = null;\n this.counterElAutoCreated = false;\n this.prevButtonEl = null;\n this.nextButtonEl = null;\n this.prevButtonElAutoCreated = false;\n this.nextButtonElAutoCreated = false;\n\n // Initialize engines with new config structure\n this.animationEngine = new AnimationEngine(this.fullConfig.animation);\n this.layoutEngine = new LayoutEngine({\n layout: this.fullConfig.layout,\n image: this.fullConfig.image\n });\n this.zoomEngine = new ZoomEngine(this.fullConfig.interaction.focus, this.animationEngine, this.fullConfig.styling);\n\n // Precompute styling properties\n this.defaultStyles = buildStyleProperties(this.fullConfig.styling?.default);\n this.defaultClassName = this.fullConfig.styling?.default?.className;\n this.hoverClassName = this.fullConfig.styling?.hover?.className;\n\n // Initialize entry animation engine with layout-aware defaults\n const entryConfig = this.fullConfig.animation.entry || DEFAULT_CONFIG.animation.entry!;\n this.entryAnimationEngine = new EntryAnimationEngine(\n entryConfig as EntryAnimationConfig,\n this.fullConfig.layout.algorithm\n );\n\n // Initialize idle animation engine if configured\n const idleConfig = this.fullConfig.animation.idle;\n if (idleConfig && idleConfig.type !== 'none') {\n this.idleAnimationEngine = new IdleAnimationEngine(\n idleConfig,\n (entryConfig as EntryAnimationConfig).timing?.duration ?? 600\n );\n } else {\n this.idleAnimationEngine = null;\n }\n\n // Wire unfocus complete callback to resume idle animations and re-apply hover styles\n this.zoomEngine.setOnUnfocusCompleteCallback((el) => {\n this.idleAnimationEngine?.resumeForImage(el as HTMLImageElement);\n // If the cursor is still over this image, mouseenter won't re-fire — re-apply hover styles.\n // Defer to next frame so the browser updates :hover after the animation finishes.\n const img = el as HTMLImageElement;\n requestAnimationFrame(() => {\n if (img.matches(':hover') && this.fullConfig.styling?.hover) {\n const idx = this.imageElements.indexOf(img);\n if (idx !== -1) {\n const imageHeight = img.offsetHeight;\n const cachedWidth = (img as any).cachedRenderedWidth;\n applyStylesToElementWithState(img, this.fullConfig.styling.hover, imageHeight, cachedWidth);\n applyClassNameToElement(img, this.hoverClassName);\n this.hoveredImage = { element: img, layout: this.imageLayouts[idx] };\n }\n }\n });\n });\n\n // SwipeEngine will be initialized after container is available\n this.swipeEngine = null;\n\n // Initialize image filter with configured extensions\n this.imageFilter = this.createImageFilter();\n\n // Initialize image loader based on type\n this.imageLoader = this.createLoader();\n\n // DOM Elements (will be fetched on init)\n this.containerEl = null;\n this.loadingEl = null;\n this.errorEl = null;\n }\n\n /**\n * Create image filter based on shared loader config\n */\n private createImageFilter(): ImageFilter {\n const extensions = this.fullConfig.config.loaders?.allowedExtensions;\n return new ImageFilter(extensions);\n }\n\n /**\n * Create appropriate image loader based on config\n * Processes loaders array, merges shared config, wraps in CompositeLoader if needed\n */\n private createLoader(): ImageLoader {\n const entries = this.fullConfig.loaders;\n const shared = this.fullConfig.config.loaders ?? {};\n\n if (!entries || entries.length === 0) {\n throw new Error('No loaders configured. Provide `images`, `loaders`, or both.');\n }\n\n const childLoaders = entries.map(entry => this.createLoaderFromEntry(entry, shared));\n\n if (childLoaders.length === 1) {\n return childLoaders[0];\n }\n\n return new CompositeLoader({\n loaders: childLoaders,\n debugLogging: this.fullConfig.config.debug?.loaders\n });\n }\n\n /**\n * Create a single loader from a LoaderEntry, merging shared config\n */\n private createLoaderFromEntry(entry: LoaderEntry, shared: SharedLoaderConfig): ImageLoader {\n if ('static' in entry) {\n const inner = entry.static;\n const merged: StaticLoaderInnerConfig = {\n ...inner,\n validateUrls: inner.validateUrls ?? shared.validateUrls,\n validationTimeout: inner.validationTimeout ?? shared.validationTimeout,\n validationMethod: inner.validationMethod ?? shared.validationMethod,\n allowedExtensions: inner.allowedExtensions ?? shared.allowedExtensions,\n debugLogging: inner.debugLogging ?? this.fullConfig.config.debug?.loaders\n };\n return new StaticImageLoader(merged);\n } else if ('googleDrive' in entry) {\n const inner = entry.googleDrive;\n const merged: GoogleDriveLoaderInnerConfig = {\n ...inner,\n allowedExtensions: inner.allowedExtensions ?? shared.allowedExtensions,\n debugLogging: inner.debugLogging ?? this.fullConfig.config.debug?.loaders\n };\n return new GoogleDriveLoader(merged);\n } else {\n throw new Error(`Unknown loader entry: ${JSON.stringify(entry)}`);\n }\n }\n\n /**\n * Initialize the gallery\n */\n async init(): Promise<void> {\n try {\n // Inject functional styles (idempotent)\n injectFunctionalStyles();\n\n // 1. Setup DOM\n if (this.containerRef) {\n this.containerEl = this.containerRef;\n } else {\n this.containerEl = document.getElementById(this.containerId!);\n if (!this.containerEl) {\n throw new Error(`Container #${this.containerId} not found`);\n }\n }\n\n // Add gallery class for CSS scoping\n this.containerEl.classList.add('fbn-ic-gallery');\n this.containerEl.setAttribute('tabindex', '0');\n\n // Initialize swipe engine for touch navigation (guarded by config flag)\n if (this.fullConfig.interaction.navigation?.swipe !== false) {\n this.swipeEngine = new SwipeEngine(this.containerEl, {\n onNext: () => this.navigateToNextImage(),\n onPrev: () => this.navigateToPreviousImage(),\n onDragOffset: (offset) => this.zoomEngine.setDragOffset(offset),\n onDragEnd: (navigated) => {\n if (!navigated) {\n // Snap back to center with animation\n this.zoomEngine.clearDragOffset(true, SNAP_BACK_DURATION_MS);\n } else {\n // Clear offset immediately (navigation handles transition)\n this.zoomEngine.clearDragOffset(false);\n }\n }\n });\n }\n\n // Create or bind UI elements\n this.setupUI();\n\n // 2. Setup Event Listeners\n this.setupEventListeners();\n\n // 3. Load Images\n this.logDebug('ImageCloud initialized');\n await this.loadImages();\n\n } catch (error) {\n console.error('Gallery initialization failed:', error);\n if (this.errorEl && error instanceof Error) {\n this.showError('Gallery failed to initialize: ' + error.message);\n }\n }\n }\n\n private setupUI(): void {\n const uiConfig = this.fullConfig.ui;\n\n // Manage focus outline: suppress browser ring by default, restore when showFocusOutline: true\n if (!uiConfig.showFocusOutline) {\n this.containerEl?.classList.add('fbn-ic-suppress-outline');\n } else {\n this.containerEl?.classList.remove('fbn-ic-suppress-outline');\n }\n\n // Loading element\n if (uiConfig.showLoadingSpinner) {\n if (uiConfig.loadingElement) {\n this.loadingEl = this.resolveElement(uiConfig.loadingElement);\n this.loadingElAutoCreated = false;\n } else {\n this.loadingEl = this.createDefaultLoadingElement();\n this.loadingElAutoCreated = true;\n }\n }\n\n // Error element\n if (uiConfig.errorElement) {\n this.errorEl = this.resolveElement(uiConfig.errorElement);\n this.errorElAutoCreated = false;\n } else {\n this.errorEl = this.createDefaultErrorElement();\n this.errorElAutoCreated = true;\n }\n\n // Counter element\n if (uiConfig.showImageCounter) {\n if (uiConfig.counterElement) {\n this.counterEl = this.resolveElement(uiConfig.counterElement);\n this.counterElAutoCreated = false;\n } else {\n this.counterEl = this.createDefaultCounterElement();\n this.counterElAutoCreated = true;\n }\n }\n\n // Nav button elements\n if (uiConfig.showNavButtons) {\n if (uiConfig.prevButtonElement) {\n this.prevButtonEl = this.resolveElement(uiConfig.prevButtonElement);\n this.prevButtonElAutoCreated = false;\n } else {\n this.prevButtonEl = this.createDefaultPrevButtonElement();\n this.prevButtonElAutoCreated = true;\n }\n if (uiConfig.nextButtonElement) {\n this.nextButtonEl = this.resolveElement(uiConfig.nextButtonElement);\n this.nextButtonElAutoCreated = false;\n } else {\n this.nextButtonEl = this.createDefaultNextButtonElement();\n this.nextButtonElAutoCreated = true;\n }\n this.prevButtonEl?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.navigateToPreviousImage();\n });\n this.nextButtonEl?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.navigateToNextImage();\n });\n }\n }\n\n private resolveElement(ref: string | HTMLElement): HTMLElement | null {\n if (ref instanceof HTMLElement) return ref;\n return document.getElementById(ref);\n }\n\n private createDefaultLoadingElement(): HTMLElement {\n const el = document.createElement('div');\n el.className = 'fbn-ic-loading fbn-ic-hidden';\n const spinner = document.createElement('div');\n spinner.className = 'fbn-ic-spinner';\n el.appendChild(spinner);\n const text = document.createElement('p');\n text.textContent = 'Loading images...';\n el.appendChild(text);\n this.containerEl!.appendChild(el);\n return el;\n }\n\n private createDefaultErrorElement(): HTMLElement {\n const el = document.createElement('div');\n el.className = 'fbn-ic-error fbn-ic-hidden';\n this.containerEl!.appendChild(el);\n return el;\n }\n\n private createDefaultCounterElement(): HTMLElement {\n const el = document.createElement('div');\n el.className = 'fbn-ic-counter fbn-ic-hidden';\n this.containerEl!.appendChild(el);\n return el;\n }\n\n private createDefaultPrevButtonElement(): HTMLElement {\n const el = document.createElement('button');\n el.className = 'fbn-ic-nav-btn fbn-ic-nav-btn-prev fbn-ic-hidden';\n el.textContent = '‹';\n el.setAttribute('aria-label', 'Previous image');\n el.setAttribute('tabindex', '-1');\n this.containerEl!.appendChild(el);\n return el;\n }\n\n private createDefaultNextButtonElement(): HTMLElement {\n const el = document.createElement('button');\n el.className = 'fbn-ic-nav-btn fbn-ic-nav-btn-next fbn-ic-hidden';\n el.textContent = '›';\n el.setAttribute('aria-label', 'Next image');\n el.setAttribute('tabindex', '-1');\n this.containerEl!.appendChild(el);\n return el;\n }\n\n private setupEventListeners(): void {\n // Keyboard navigation — scoped to container, guarded by config flag\n if (this.fullConfig.interaction.navigation?.keyboard !== false) {\n this.containerEl!.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n this.zoomEngine.unfocusImage();\n this.currentFocusIndex = null;\n this.swipeEngine?.disable();\n this.hideCounter();\n this.hideNavButtons();\n this.hideFocusIndicator();\n } else if (e.key === 'ArrowRight') {\n this.navigateToNextImage();\n } else if (e.key === 'ArrowLeft') {\n this.navigateToPreviousImage();\n } else if ((e.key === 'Enter' || e.key === ' ') && this.hoveredImage) {\n this.handleImageClick(this.hoveredImage.element, this.hoveredImage.layout);\n e.preventDefault();\n }\n });\n }\n\n document.addEventListener('click', (e: MouseEvent) => {\n // Ignore clicks that follow touch events (prevents unfocus during swipe)\n if (this.swipeEngine?.hadRecentTouch()) {\n return;\n }\n if (!(e.target as HTMLElement).closest('.fbn-ic-image') &&\n !(e.target as HTMLElement).closest('.fbn-ic-nav-btn')) {\n this.zoomEngine.unfocusImage();\n this.currentFocusIndex = null;\n this.swipeEngine?.disable();\n this.hideCounter();\n this.hideNavButtons();\n this.hideFocusIndicator();\n }\n });\n\n // Resize handler\n window.addEventListener('resize', () => this.handleResize());\n }\n\n /**\n * Navigate to the next image (Right arrow)\n */\n private navigateToNextImage(): void {\n if (this.currentFocusIndex === null || this.imageElements.length === 0) return;\n\n const nextId = (this.currentFocusIndex + 1) % this.imageLayouts.length;\n const nextElement = this.imageElements.find(\n el => el.dataset.imageId === String(nextId)\n );\n if (!nextElement) return;\n\n const layout = this.imageLayouts[nextId];\n if (!layout) return;\n\n this.currentFocusIndex = nextId;\n this.handleImageClick(nextElement, layout);\n this.updateCounter(nextId);\n this.showNavButtons();\n this.showFocusIndicator();\n }\n\n /**\n * Navigate to the previous image (Left arrow)\n */\n private navigateToPreviousImage(): void {\n if (this.currentFocusIndex === null || this.imageElements.length === 0) return;\n\n const prevId = (this.currentFocusIndex - 1 + this.imageLayouts.length) % this.imageLayouts.length;\n const prevElement = this.imageElements.find(\n el => el.dataset.imageId === String(prevId)\n );\n if (!prevElement) return;\n\n const layout = this.imageLayouts[prevId];\n if (!layout) return;\n\n this.currentFocusIndex = prevId;\n this.handleImageClick(prevElement, layout);\n this.updateCounter(prevId);\n this.showNavButtons();\n this.showFocusIndicator();\n }\n\n /**\n * Navigate to a specific image by index\n */\n private handleResize(): void {\n if (!this.imagesLoaded) return;\n\n if (this.resizeTimeout !== null) {\n clearTimeout(this.resizeTimeout);\n }\n\n this.resizeTimeout = window.setTimeout(() => {\n const newHeight = this.getImageHeight();\n\n if (newHeight !== this.currentImageHeight) {\n this.logDebug(`Window resized to new breakpoint (height: ${newHeight}px). Reloading images...`);\n // Reload images with new breakpoint\n this.loadImages();\n } else {\n this.logDebug('Window resized (no breakpoint change)');\n }\n }, 500);\n }\n\n private getImageHeight(): number {\n const width = window.innerWidth;\n const responsive = this.fullConfig.layout.responsive;\n\n // Get sizing config for adaptive mode defaults\n const sizing = this.fullConfig.image.sizing;\n const maxSize = sizing?.maxSize ?? 400;\n\n // Use responsive breakpoints to determine max height\n // These serve as upper bounds for the adaptive sizing\n if (!responsive) {\n // Fallback defaults if responsive not configured\n if (width <= 767) return Math.min(100, maxSize);\n if (width <= 1199) return Math.min(180, maxSize);\n return Math.min(225, maxSize);\n }\n\n if (width <= responsive.mobile.maxWidth) {\n return Math.min(100, maxSize); // Mobile\n }\n if (width <= responsive.tablet.maxWidth) {\n return Math.min(180, maxSize); // Tablet\n }\n return Math.min(225, maxSize); // Screen (desktop)\n }\n\n /**\n * Get container bounds for layout calculations\n */\n private getContainerBounds(): { width: number; height: number } {\n if (!this.containerEl) {\n return { width: window.innerWidth, height: window.innerHeight * 0.7 };\n }\n return {\n width: this.containerEl.offsetWidth,\n height: this.containerEl.offsetHeight || window.innerHeight * 0.7\n };\n }\n\n /**\n * Load images using the unified loader interface\n */\n private async loadImages(): Promise<void> {\n try {\n this.showLoading(true);\n this.hideError();\n this.clearImageCloud();\n\n // Prepare the loader (show spinner during this)\n await this.imageLoader.prepare(this.imageFilter);\n\n // Get image count and URLs from loader\n const imageCount = this.imageLoader.imagesLength();\n let imageUrls = this.imageLoader.imageURLs();\n\n if (imageCount === 0) {\n this.showError('No images found.');\n this.showLoading(false);\n return;\n }\n\n // Calculate adaptive sizing based on container and image count\n const containerBounds = this.getContainerBounds();\n const responsiveHeight = this.getImageHeight();\n const viewportWidth = window.innerWidth;\n\n this.logDebug(`Adaptive sizing input: container=${containerBounds.width}x${containerBounds.height}px, images=${imageCount}, responsiveMax=${responsiveHeight}px`);\n\n const sizingResult = this.layoutEngine.calculateAdaptiveSize(\n containerBounds,\n imageCount,\n responsiveHeight,\n viewportWidth\n );\n\n this.logDebug(`Adaptive sizing result: height=${sizingResult.height}px`);\n\n await this.createImageCloud(imageUrls, sizingResult.height);\n\n this.showLoading(false);\n this.imagesLoaded = true;\n\n } catch (error) {\n console.error('Error loading images:', error);\n if (error instanceof Error) {\n this.showError(error.message || 'Failed to load images.');\n }\n this.showLoading(false);\n }\n }\n\n /**\n * Helper for debug logging\n */\n private logDebug(...args: unknown[]): void {\n if (this.fullConfig.config.debug?.enabled && typeof console !== 'undefined') {\n console.log(...args);\n }\n }\n\n private async createImageCloud(imageUrls: string[], imageHeight: number): Promise<void> {\n if (!this.containerEl) return;\n\n const containerBounds = this.getContainerBounds();\n this.currentImageHeight = imageHeight;\n\n // Capture current generation to detect stale callbacks\n const currentGeneration = this.loadGeneration;\n\n // Generate layout\n const layouts = this.layoutEngine.generateLayout(imageUrls.length, containerBounds, { fixedHeight: imageHeight } as any);\n this.imageLayouts = layouts;\n\n this.displayQueue = [];\n let processedCount = 0;\n\n // Helper to display a single image with animation\n const displayImage = (img: HTMLImageElement) => {\n if (!this.containerEl) return;\n\n this.containerEl.appendChild(img);\n this.imageElements.push(img);\n\n requestAnimationFrame(() => {\n void img.offsetWidth; // Force reflow\n // Use configured default opacity, or 1 if not specified\n img.style.opacity = this.defaultStyles.opacity ?? '1';\n\n // Check if we need JS animation for path type, rotation, or scale\n const needsJSAnimation = img.dataset.startX &&\n (this.entryAnimationEngine.requiresJSAnimation() ||\n this.entryAnimationEngine.requiresJSRotation() ||\n this.entryAnimationEngine.requiresJSScale() ||\n img.dataset.startRotation !== img.dataset.rotation ||\n img.dataset.startScale !== img.dataset.scale);\n\n if (needsJSAnimation) {\n // Use animatePath for bounce, elastic, wave paths or rotation/scale animation\n const startPosition = {\n x: parseFloat(img.dataset.startX!),\n y: parseFloat(img.dataset.startY!)\n };\n const endPosition = {\n x: parseFloat(img.dataset.endX!),\n y: parseFloat(img.dataset.endY!)\n };\n const imageWidth = parseFloat(img.dataset.imageWidth!);\n const imageHeight = parseFloat(img.dataset.imageHeight!);\n const rotation = parseFloat(img.dataset.rotation!);\n const scale = parseFloat(img.dataset.scale!);\n const startRotation = img.dataset.startRotation\n ? parseFloat(img.dataset.startRotation)\n : rotation;\n const startScale = img.dataset.startScale\n ? parseFloat(img.dataset.startScale)\n : scale;\n const timing = this.entryAnimationEngine.getTiming();\n\n animatePath({\n element: img,\n startPosition,\n endPosition,\n pathConfig: this.entryAnimationEngine.getPathConfig(),\n duration: timing.duration,\n imageWidth,\n imageHeight,\n rotation,\n scale,\n rotationConfig: this.entryAnimationEngine.getRotationConfig(),\n startRotation,\n scaleConfig: this.entryAnimationEngine.getScaleConfig(),\n startScale\n });\n } else {\n // Use CSS transition for linear/arc paths without rotation animation\n const finalTransform = img.dataset.finalTransform || '';\n img.style.transform = finalTransform;\n }\n\n // Debug: log final state for first few images\n const imgIndex = parseInt(img.dataset.imageId || '0');\n if (this.fullConfig.config.debug?.enabled && imgIndex < 3) {\n const finalTransform = img.dataset.finalTransform || '';\n console.log(`Image ${imgIndex} final state:`, {\n left: img.style.left,\n top: img.style.top,\n width: img.style.width,\n height: img.style.height,\n computedWidth: img.offsetWidth,\n computedHeight: img.offsetHeight,\n transform: finalTransform,\n pathType: this.entryAnimationEngine.getPathType()\n });\n }\n\n // Register with idle animation engine (starts after entry completes)\n if (this.idleAnimationEngine) {\n const entryDuration = this.entryAnimationEngine.getTiming().duration;\n this.idleAnimationEngine.register(img, imgIndex, this.imageElements.length, entryDuration);\n }\n });\n\n processedCount++;\n };\n\n const startQueueProcessing = () => {\n this.logDebug('Starting queue processing, enabled:', this.fullConfig.animation.queue.enabled);\n\n // If queue is disabled, display all images immediately\n if (!this.fullConfig.animation.queue.enabled) {\n while (this.displayQueue.length > 0) {\n const img = this.displayQueue.shift();\n if (img) {\n displayImage(img);\n }\n }\n return;\n }\n\n // Queue is enabled - stagger images with interval\n // Clear any existing interval before creating new one\n if (this.queueInterval !== null) {\n clearInterval(this.queueInterval);\n }\n this.queueInterval = window.setInterval(() => {\n // Check if this interval is still valid (generation hasn't changed)\n if (currentGeneration !== this.loadGeneration) {\n if (this.queueInterval !== null) {\n clearInterval(this.queueInterval);\n this.queueInterval = null;\n }\n return;\n }\n\n if (this.displayQueue.length > 0) {\n const img = this.displayQueue.shift();\n if (img) {\n displayImage(img);\n }\n }\n\n if (processedCount >= imageUrls.length && this.displayQueue.length === 0) {\n if (this.queueInterval !== null) {\n clearInterval(this.queueInterval);\n this.queueInterval = null;\n }\n }\n }, this.fullConfig.animation.queue.interval);\n };\n\n // Visibility Check\n if ('IntersectionObserver' in window && this.containerEl) {\n const observer = new IntersectionObserver((entries) => {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n startQueueProcessing();\n observer.disconnect();\n }\n });\n }, { threshold: 0.1, rootMargin: '50px' });\n observer.observe(this.containerEl);\n } else {\n startQueueProcessing();\n }\n\n // Debug: Draw center markers if debug.centers is enabled\n if (this.fullConfig.config.debug?.centers && this.containerEl) {\n // Remove any existing debug markers\n this.containerEl.querySelectorAll('.fbn-ic-debug-center').forEach(el => el.remove());\n\n layouts.forEach((layout, index) => {\n const marker = document.createElement('div');\n marker.className = 'fbn-ic-debug-center';\n marker.style.position = 'absolute';\n marker.style.width = '12px';\n marker.style.height = '12px';\n marker.style.borderRadius = '50%';\n marker.style.backgroundColor = 'red';\n marker.style.border = '2px solid yellow';\n marker.style.zIndex = '9999';\n marker.style.pointerEvents = 'none';\n // Center position: layout.x and layout.y now store the center position directly\n const centerX = layout.x;\n const centerY = layout.y;\n marker.style.left = `${centerX - 6}px`; // Offset by half marker size\n marker.style.top = `${centerY - 6}px`;\n marker.title = `Image ${index}: center (${Math.round(centerX)}, ${Math.round(centerY)})`;\n this.containerEl!.appendChild(marker);\n });\n }\n\n // Create elements\n imageUrls.forEach((url, index) => {\n const img = document.createElement('img');\n // NOTE: img.src is set AFTER onload handler to ensure handler catches cached images\n img.referrerPolicy = 'no-referrer';\n img.classList.add('fbn-ic-image');\n if (this.fullConfig.interaction.dragging === false) {\n img.draggable = false;\n }\n img.dataset.imageId = String(index);\n img.dataset.createdFlag = 'true'; // Debug flag\n\n const layout = layouts[index];\n img.style.position = 'absolute';\n img.style.width = 'auto';\n img.style.height = `${imageHeight}px`;\n img.style.left = `${layout.x}px`;\n img.style.top = `${layout.y}px`;\n // Transform will be applied in onload after we know the actual dimensions\n\n if (layout.zIndex) img.style.zIndex = String(layout.zIndex);\n\n // NOTE: Default styling will be applied in onload after image dimensions are known\n // This ensures height-relative clip-path is calculated correctly with proper width\n // Element starts with opacity 0 so it's not visible until onload completes\n applyClassNameToElement(img, this.defaultClassName);\n\n // Hover event handlers\n // Use isInvolved() to prevent hover styles on images that are focused or animating\n img.addEventListener('mouseenter', () => {\n this.hoveredImage = { element: img, layout };\n if (!this.zoomEngine.isInvolved(img)) {\n // Use cached rendered width for consistent clip-path centering (prevents shifting)\n const cachedWidth = (img as any).cachedRenderedWidth;\n applyStylesToElementWithState(img, this.fullConfig.styling?.hover, imageHeight, cachedWidth);\n applyClassNameToElement(img, this.hoverClassName);\n }\n });\n\n img.addEventListener('mouseleave', () => {\n this.hoveredImage = null;\n if (!this.zoomEngine.isInvolved(img)) {\n // Use cached rendered width for consistent clip-path centering (prevents shifting)\n const cachedWidth = (img as any).cachedRenderedWidth;\n applyStylesToElementWithState(img, this.fullConfig.styling?.default, imageHeight, cachedWidth);\n removeClassNameFromElement(img, this.hoverClassName);\n applyClassNameToElement(img, this.defaultClassName);\n }\n });\n\n img.addEventListener('click', (e: MouseEvent) => {\n e.stopPropagation();\n this.handleImageClick(img, layout);\n });\n\n img.style.opacity = '0';\n img.style.transition = this.entryAnimationEngine.getTransitionCSS();\n\n img.onload = () => {\n // Ignore if generation has changed (stale callback from previous load)\n if (currentGeneration !== this.loadGeneration) {\n return;\n }\n\n const aspectRatio = img.naturalWidth / img.naturalHeight;\n const renderedWidth = imageHeight * aspectRatio;\n\n // Debug: mark that onload was called\n img.dataset.onloadCalled = 'true';\n if ((window as any).DEBUG_CLIPPATH) {\n console.log(`[onload #${index}] Called with imageHeight=${imageHeight}, renderedWidth=${renderedWidth}`);\n }\n\n // Set explicit width so transform calculations are accurate\n img.style.width = `${renderedWidth}px`;\n\n // Store rendered width and aspect ratio on element for use in event handlers and focused state\n (img as any).cachedRenderedWidth = renderedWidth;\n (img as any).aspectRatio = aspectRatio;\n\n // Reapply default styling with correct width for height-relative clip-path centering\n // Now we know both height and the rendered width (from aspect ratio)\n applyStylesToElementWithState(img, this.fullConfig.styling?.default, imageHeight, renderedWidth);\n\n // Use EntryAnimationEngine for start position calculation\n const finalPosition = { x: layout.x, y: layout.y };\n const imageSize = { width: renderedWidth, height: imageHeight };\n\n const startPosition = this.entryAnimationEngine.calculateStartPosition(\n finalPosition,\n imageSize,\n containerBounds,\n index,\n imageUrls.length\n );\n\n // Calculate start rotation based on entry rotation config\n const startRotation = this.entryAnimationEngine.calculateStartRotation(layout.rotation);\n\n // Calculate start scale based on entry scale config\n const startScale = this.entryAnimationEngine.calculateStartScale(layout.scale);\n\n const finalTransform = this.entryAnimationEngine.buildFinalTransform(\n layout.rotation,\n layout.scale,\n renderedWidth,\n imageHeight\n );\n const startTransform = this.entryAnimationEngine.buildStartTransform(\n startPosition,\n finalPosition,\n layout.rotation,\n layout.scale,\n renderedWidth,\n imageHeight,\n startRotation,\n startScale\n );\n\n if (this.fullConfig.config.debug?.enabled && index < 3) {\n console.log(`Image ${index}:`, {\n finalPosition,\n imageSize,\n left: layout.x,\n top: layout.y,\n finalTransform,\n renderedWidth,\n renderedHeight: imageHeight\n });\n }\n\n img.style.transform = startTransform;\n img.dataset.finalTransform = finalTransform;\n\n // Store animation data for JS-animated paths (bounce, elastic, wave)\n // or when rotation/scale animation is needed\n const needsJSAnimation = this.entryAnimationEngine.requiresJSAnimation() ||\n this.entryAnimationEngine.requiresJSRotation() ||\n this.entryAnimationEngine.requiresJSScale() ||\n startRotation !== layout.rotation ||\n startScale !== layout.scale;\n\n if (needsJSAnimation) {\n img.dataset.startX = String(startPosition.x);\n img.dataset.startY = String(startPosition.y);\n img.dataset.endX = String(finalPosition.x);\n img.dataset.endY = String(finalPosition.y);\n img.dataset.imageWidth = String(renderedWidth);\n img.dataset.imageHeight = String(imageHeight);\n img.dataset.rotation = String(layout.rotation);\n img.dataset.scale = String(layout.scale);\n img.dataset.startRotation = String(startRotation);\n img.dataset.startScale = String(startScale);\n }\n\n this.displayQueue.push(img);\n };\n\n img.onerror = () => processedCount++;\n\n // Set src AFTER onload handler to ensure it catches cached images\n img.src = url;\n });\n }\n\n private async handleImageClick(imageElement: HTMLImageElement, originalLayout: ImageLayout): Promise<void> {\n if (!this.containerEl) return;\n\n const isFocused = this.zoomEngine.isFocused(imageElement);\n const bounds: ContainerBounds = {\n width: this.containerEl.offsetWidth,\n height: this.containerEl.offsetHeight\n };\n\n if (isFocused) {\n await this.zoomEngine.unfocusImage();\n this.currentFocusIndex = null;\n this.swipeEngine?.disable();\n this.hideCounter();\n this.hideNavButtons();\n this.hideFocusIndicator();\n } else {\n // Pause idle animation immediately before focus animation begins\n this.idleAnimationEngine?.pauseForImage(imageElement);\n\n // Track the focused image index for keyboard navigation\n const imageId = imageElement.dataset.imageId;\n this.currentFocusIndex = imageId !== undefined ? parseInt(imageId, 10) : null;\n this.swipeEngine?.enable();\n this.containerEl?.focus({ preventScroll: true });\n await this.zoomEngine.focusImage(imageElement, bounds, originalLayout);\n if (this.currentFocusIndex !== null) {\n this.updateCounter(this.currentFocusIndex);\n }\n this.showNavButtons();\n this.showFocusIndicator();\n }\n }\n\n /**\n * Clear the image cloud and reset state\n */\n clearImageCloud(): void {\n // Clear queue processing interval to prevent stale images from being added\n if (this.queueInterval !== null) {\n clearInterval(this.queueInterval);\n this.queueInterval = null;\n }\n // Increment generation to invalidate pending image onload handlers\n this.loadGeneration++;\n this.displayQueue = [];\n\n this.hideFocusIndicator();\n\n if (this.containerEl) {\n this.containerEl.querySelectorAll('.fbn-ic-image, .fbn-ic-debug-center').forEach(el => el.remove());\n }\n this.imageElements = [];\n this.imageLayouts = [];\n this.currentFocusIndex = null;\n this.hoveredImage = null;\n this.layoutEngine.reset();\n this.zoomEngine.reset();\n this.idleAnimationEngine?.stopAll();\n this.imagesLoaded = false;\n }\n\n private showLoading(show: boolean): void {\n if (!this.fullConfig.ui.showLoadingSpinner || !this.loadingEl) return;\n if (show) {\n this.loadingEl.classList.remove('fbn-ic-hidden');\n } else {\n this.loadingEl.classList.add('fbn-ic-hidden');\n }\n }\n\n private showError(message: string): void {\n if (!this.errorEl) return;\n this.errorEl.textContent = message;\n this.errorEl.classList.remove('fbn-ic-hidden');\n }\n\n private hideError(): void {\n if (this.errorEl) {\n this.errorEl.classList.add('fbn-ic-hidden');\n }\n }\n\n private updateCounter(index: number): void {\n if (!this.fullConfig.ui.showImageCounter || !this.counterEl) return;\n this.counterEl.textContent = `${index + 1} of ${this.imageElements.length}`;\n this.counterEl.classList.remove('fbn-ic-hidden');\n }\n\n private hideCounter(): void {\n if (this.counterEl) {\n this.counterEl.classList.add('fbn-ic-hidden');\n }\n }\n\n private showFocusIndicator(): void {\n this.containerEl?.classList.add('fbn-ic-has-focus');\n }\n\n private hideFocusIndicator(): void {\n this.containerEl?.classList.remove('fbn-ic-has-focus');\n }\n\n private showNavButtons(): void {\n this.prevButtonEl?.classList.remove('fbn-ic-hidden');\n this.nextButtonEl?.classList.remove('fbn-ic-hidden');\n }\n\n private hideNavButtons(): void {\n this.prevButtonEl?.classList.add('fbn-ic-hidden');\n this.nextButtonEl?.classList.add('fbn-ic-hidden');\n }\n\n /**\n * Destroy the gallery and clean up resources\n */\n destroy(): void {\n this.clearImageCloud();\n // Remove auto-created UI elements\n if (this.loadingElAutoCreated && this.loadingEl) {\n this.loadingEl.remove();\n this.loadingEl = null;\n }\n if (this.errorElAutoCreated && this.errorEl) {\n this.errorEl.remove();\n this.errorEl = null;\n }\n if (this.counterElAutoCreated && this.counterEl) {\n this.counterEl.remove();\n this.counterEl = null;\n }\n if (this.prevButtonElAutoCreated && this.prevButtonEl) {\n this.prevButtonEl.remove();\n this.prevButtonEl = null;\n }\n if (this.nextButtonElAutoCreated && this.nextButtonEl) {\n this.nextButtonEl.remove();\n this.nextButtonEl = null;\n }\n // Remove event listeners\n if (this.resizeTimeout !== null) {\n clearTimeout(this.resizeTimeout);\n }\n this.swipeEngine?.destroy();\n this.idleAnimationEngine?.stopAll();\n this.idleAnimationEngine = null;\n }\n}\n","import {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n type CSSProperties,\n} from 'react';\nimport { ImageCloud as ImageCloudCore } from '../ImageCloud';\nimport type { ImageCloudOptions } from '../config/types';\n\nexport type ImageCloudProps = Omit<ImageCloudOptions, 'container'> & {\n className?: string;\n style?: CSSProperties;\n};\n\nexport interface ImageCloudRef {\n instance: ImageCloudCore | null;\n}\n\nexport const ImageCloud = forwardRef<ImageCloudRef, ImageCloudProps>(\n function ImageCloud({ className, style, ...options }, ref) {\n const containerRef = useRef<HTMLDivElement>(null);\n const instanceRef = useRef<ImageCloudCore | null>(null);\n\n useImperativeHandle(ref, () => ({\n get instance() {\n return instanceRef.current;\n },\n }));\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n const cloud = new ImageCloudCore({\n container: containerRef.current,\n ...options,\n });\n instanceRef.current = cloud;\n\n cloud.init().catch((err) => {\n console.error('ImageCloud init failed:', err);\n });\n\n return () => {\n cloud.destroy();\n instanceRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(options)]);\n\n return <div ref={containerRef} className={className} style={style} />;\n }\n);\n\n// Re-export core types for convenience\nexport type {\n ImageCloudOptions,\n LayoutAlgorithm,\n LayoutConfig,\n AnimationConfig,\n ImageStylingConfig,\n} from '../config/types';\n"],"names":["SHADOW_PRESETS","BOUNCE_PRESETS","ELASTIC_PRESETS","WAVE_PATH_PRESETS","DEFAULT_PATH_CONFIG","DEFAULT_ENTRY_ROTATION","DEFAULT_ENTRY_SCALE","DEFAULT_STYLING","DEFAULT_RADIAL_CONFIG","DEFAULT_WAVE_CONFIG","DEFAULT_HONEYCOMB_CONFIG","DEFAULT_RESPONSIVE_BREAKPOINTS","DEFAULT_IMAGE_SIZING","DEFAULT_IMAGE_ROTATION","DEFAULT_IMAGE_CONFIG","DEFAULT_SHARED_LOADER_CONFIG","DEFAULT_DEBUG_CONFIG","DEFAULT_IDLE_WIGGLE","DEFAULT_IDLE_PULSE","DEFAULT_IDLE_BLINK","DEFAULT_IDLE_SPIN","DEFAULT_IDLE_CONFIG","DEFAULT_CONFIG","deepMergeStyleState","base","override","merged","deepMergeStyling","defaults","userStyling","mergedDefault","mergedHover","mergedFocused","deepMergeImageConfig","userImage","userVariance","validMin","validMax","userRange","convertLegacyRotationConfig","userConfig","legacyRotation","convertLegacyVarianceConfig","legacyVariance","mergeConfig","combinedImageConfig","loaders","mergedConfig","legacyUi","honeycombClip","resolveBounceConfig","preset","overrides","resolveElasticConfig","resolveWavePathConfig","AnimationEngine","config","params","transforms","x","y","element","from","to","duration","easing","animDuration","animEasing","fromTransform","toTransform","animation","handle","commitStyle","snapshot","currentTransform","allAnimations","anim","transformStr","matrix","scale","rotation","properties","resolve","originalState","ms","lerp","start","end","t","calculateBouncePosition","overshoot","bounces","decayRatio","dx","dy","keyframes","generateBounceKeyframes","progress","segmentStart","segmentEnd","segmentOvershoot","isOvershootPhase","i","segmentT","easeOutQuad","fromProgress","k","currentTime","currentOvershoot","bounceTime","calculateElasticPosition","stiffness","damping","mass","oscillations","omega","zeta","dampedFreq","envelope","oscillation","calculateWavePosition","amplitude","frequency","decay","decayRate","phase","length","perpX","perpY","wavePhase","decayFactor","waveOffset","progressT","easeOutCubic","calculateWobbleRotation","finalRotation","wobbleConfig","wobbleOffset","calculatePopScale","finalScale","popConfig","bounceDecay","undershoot","currentScale","prevTime","prevScale","segmentProgress","easedProgress","animatePath","options","startPosition","endPosition","pathConfig","imageWidth","imageHeight","onComplete","rotationConfig","startRotation","scaleConfig","startScale","pathType","animateRotation","isWobbleMode","needsRotationAnimation","animateScale","isPopMode","startTime","centerOffsetX","centerOffsetY","tick","elapsed","position","translateX","translateY","currentRotation","requiresJSAnimation","LAYOUT_ENTRY_DEFAULTS","EntryAnimationEngine","layoutAlgorithm","finalPosition","imageSize","containerBounds","imageIndex","totalImages","offset","centerX","centerY","distLeft","distRight","distTop","distBottom","minDist","startX","startY","edge","_finalPosition","_imageSize","edges","randomEdge","circularConfig","distribution","radius","radiusConfig","percentage","angle","_imageIndex","centerTranslate","offsetX","offsetY","startConfig","range","spinCount","direction","IdleAnimationEngine","entryDurationMs","index","entryDuration","delay","startDelay","entry","type","cfg","onOpacity","endDeg","fn","result","speed","RandomPlacementLayout","imageConfig","imageCount","layouts","width","height","padding","baseImageSize","rotationMode","minRotation","maxRotation","varianceMin","varianceMax","hasVariance","halfWidth","halfHeight","maxX","maxY","minX","minY","scaledImageSize","layout","min","max","RadialPlacementLayout","scaleDecay","radialConfig","cx","cy","estimatedMaxRings","maxRadius","varianceScale","centerSize","processedCount","currentRing","normalizedRing","ringScale","ringStep","radiusY","radiusX","circumference","estimatedItemWidth","itemsInRing","angleStep","ringOffset","combinedScale","DEFAULT_GRID_CONFIG","OVERFLOW_OFFSET_PATTERN","GridPlacementLayout","gridConfig","availableWidth","availableHeight","columns","rows","hasRowStagger","hasColumnStagger","effectiveColumns","effectiveRows","cellWidth","cellHeight","staggerOffsetX","staggerOffsetY","overlapMultiplier","cellBasedSize","totalGridWidth","totalGridHeight","gridOffsetX","gridOffsetY","cellCount","hasFixedGrid","isOverflowMode","cellStackCount","overflowOffsetPx","col","row","stackLayer","overflowIndex","targetCell","cellCenterX","cellCenterY","patternIndex","pattern","maxJitterX","maxJitterY","itemsInLastRow","lastRowWidth","alignmentOffset","zIndex","_baseImageSize","aspectRatio","GOLDEN_ANGLE","DEFAULT_SPIRAL_CONFIG","SpiralPlacementLayout","spiralConfig","directionMultiplier","theta","normalizedRadius","decayScale","clampedX","clampedY","baseRotation","rotationVariance","tangentAngle","b","psi","tightness","maxTheta","a","maxComputedRadius","DEFAULT_CLUSTER_CONFIG","ClusterPlacementLayout","clusterConfig","clusterCount","clusterCenters","imagesPerCluster","clusterIdx","cluster","imagesInThisCluster","distance","sizeMultiplier","normalizedDistance","configCount","clusterSpacing","countByImages","countBySpace","count","centers","bestCandidate","bestMinDistance","attempt","candidate","minDistance","existing","u","v","value","WavePlacementLayout","waveConfig","phaseShift","synchronization","imagesPerRow","halfImageWidth","endX","horizontalSpacing","minCenterY","maxCenterY","rowSpacing","rowIndex","baseY","imgInRow","waveY","containerWidth","normalizedX","derivative","HEXAGON_REF_HEIGHT","_R","HEXAGON_REF_POINTS","HEXAGON_COL_STEP_RATIO","HEXAGON_ROW_OFFSET_RATIO","getHexTilingParams","hexH","hexCubeToPixel","_cz","originX","originY","colStep","HEX_RING_DIRECTIONS","getHexRingCells","ring","cells","cz","dz","step","HoneycombPlacementLayout","_imageConfig","containerCX","containerCY","spacing","placed","px","py","LayoutEngine","imageId","newConfig","viewportWidth","breakpoints","sizing","responsiveHeight","breakpoint","maxHeight","userBaseHeight","minSize","maxSize","targetCoverage","densityFactor","areaPerImage","calculatedHeight","finalHeight","floor","maxRing","total","COL_STEP_RATIO","HALF_WIDTH_RATIO","maxV","maxH","ZoomState","CLIP_PATH_SHAPES","CLIP_PATH_SHAPES_HEIGHT_RELATIVE","getClipPath","shape","calculateHeightRelativeClipPath","shapeDef","xs","ys","shapeCenterX","shapeCenterY","scaledBBoxWidth","imageCenterX","imageCenterY","horizontalOffset","verticalOffset","scaledX","scaledY","isShadowPreset","resolveShadow","shadow","buildFilterString","filter","parts","ds","buildSingleBorder","style","color","buildStyleProperties","state","styles","baseRadius","baseBorder","topBorder","rightBorder","bottomBorder","leftBorder","filterStr","clipPathValue","isConfig","clipPathInput","applyStylesToElement","applyStylesToElementWithState","resolveClassName","className","applyClassNameToElement","resolved","cls","removeClassNameFromElement","Z_INDEX","ZoomEngine","animationEngine","styling","callback","normalizedPercent","targetHeight","focusHeight","focusWidth","maxWidth","targetX","targetY","fromWidth","fromHeight","toWidth","toHeight","fromTransformStr","toTransformStr","originalZIndex","isToFocused","styleConfig","updateClipPath","currentHeight","currentWidth","fromDimensions","originalWidth","originalHeight","focusDimensions","focusTransform","startTransform","startWidth","startHeight","toState","targetWidth","computed","midWidth","midHeight","pureX","pureY","imageElement","myGeneration","completedOutgoing","incomingFrom","incomingFromDimensions","completedFocusing","incomingUnfocus","outgoingElement","incomingUnfocusElement","newImageElement","animate","centeredTransform","SWIPE_THRESHOLD_PX","SWIPE_VELOCITY_THRESHOLD","SWIPE_MIN_DISTANCE_FOR_VELOCITY","DRAG_DAMPING","SNAP_BACK_DURATION_MS","HORIZONTAL_ANGLE_THRESHOLD_DEG","_SwipeEngine","container","callbacks","e","touch","deltaX","deltaY","angleDeg","dampedOffset","_e","deltaTime","velocity","absDistance","navigated","SwipeEngine","GoogleDriveLoader","source","folderUrl","recursive","urls","patterns","match","folderId","error","imageUrls","query","url","response","validFiles","file","fileUrls","fileUrl","fileId","metadataUrl","metadata","data","subfolders","f","folder","subfolderImages","_filter","html","imageIdPattern","matches","m","id","imageIds","args","StaticImageLoader","validUrls","filename","basePath","files","controller","timeoutId","cleanBase","origin","cleanPath","CompositeLoader","preparePromises","loader","ImageFilter","extensions","extension","FUNCTIONAL_CSS","injectFunctionalStyles","ImageCloud$1","entryConfig","idleConfig","el","img","idx","cachedWidth","entries","shared","childLoaders","inner","uiConfig","ref","spinner","text","nextId","nextElement","prevId","prevElement","newHeight","responsive","sizingResult","currentGeneration","displayImage","timing","finalTransform","imgIndex","startQueueProcessing","observer","marker","renderedWidth","originalLayout","isFocused","bounds","show","message","ImageCloud","forwardRef","containerRef","useRef","instanceRef","useImperativeHandle","useEffect","cloud","ImageCloudCore","err","jsx"],"mappings":";;AAUO,MAAMA,KAA+C,OAAO,OAAO;AAAA,EACxE,MAAQ;AAAA,EACR,IAAM;AAAA,EACN,IAAM;AAAA,EACN,IAAM;AAAA,EACN,MAAQ;AACV,CAAC,GAKYC,KAAyD,OAAO,OAAO;AAAA,EAClF,WAAW,OAAO,OAAO,EAAE,WAAW,MAAM,SAAS,GAAG,YAAY,KAAK;AAAA,EACzE,SAAS,OAAO,OAAO,EAAE,WAAW,MAAM,SAAS,GAAG,YAAY,KAAK;AAAA,EACvE,QAAQ,OAAO,OAAO,EAAE,WAAW,MAAM,SAAS,GAAG,YAAY,IAAA,CAAK;AACxE,CAAC,GAKYC,KAA4D,OAAO,OAAO;AAAA,EACrF,QAAQ,OAAO,OAAO,EAAE,WAAW,KAAK,SAAS,IAAI,MAAM,GAAG,cAAc,EAAA,CAAG;AAAA,EAC/E,QAAQ,OAAO,OAAO,EAAE,WAAW,KAAK,SAAS,IAAI,MAAM,GAAG,cAAc,EAAA,CAAG;AAAA,EAC/E,QAAQ,OAAO,OAAO,EAAE,WAAW,KAAK,SAAS,IAAI,MAAM,KAAK,cAAc,EAAA,CAAG;AAAA,EACjF,QAAQ,OAAO,OAAO,EAAE,WAAW,KAAK,SAAS,IAAI,MAAM,KAAK,cAAc,EAAA,CAAG;AACnF,CAAC,GAKYC,KAA4D,OAAO,OAAO;AAAA,EACrF,QAAQ,OAAO,OAAO,EAAE,WAAW,IAAI,WAAW,KAAK,OAAO,IAAM,WAAW,KAAK,OAAO,GAAG;AAAA,EAC9F,SAAS,OAAO,OAAO,EAAE,WAAW,IAAI,WAAW,KAAK,OAAO,IAAM,WAAW,KAAK,OAAO,GAAG;AAAA,EAC/F,YAAY,OAAO,OAAO,EAAE,WAAW,IAAI,WAAW,GAAG,OAAO,IAAO,WAAW,GAAG,OAAO,GAAG;AAAA,EAC/F,SAAS,OAAO,OAAO,EAAE,WAAW,IAAI,WAAW,GAAG,OAAO,IAAM,WAAW,KAAK,OAAO,GAAG;AAC/F,CAAC,GAKYC,KAAuC,OAAO,OAAO;AAAA,EAChE,MAAM;AACR,CAAC,GAKYC,KAA8C,OAAO,OAAO;AAAA,EACvE,MAAM;AACR,CAAC,GAKYC,KAAwC,OAAO,OAAO;AAAA,EACjE,MAAM;AACR,CAAC,GAMYC,KAAsC,OAAO,OAAO;AAAA,EAC/D,SAAS,OAAO,OAAO;AAAA,IACrB,QAAQ,OAAO,OAAO;AAAA,MACpB,OAAO;AAAA,MACP,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,OAAO;AAAA,IAAA,CACR;AAAA,IACD,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,QAAQ,OAAO,OAAO,EAAE;AAAA,IACxB,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,SAAS,OAAO,OAAO;AAAA,MACrB,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,QAAQ;AAAA,IAAA,CACT;AAAA,EAAA,CACF;AAAA,EACD,OAAO,OAAO,OAAO;AAAA,IACnB,QAAQ;AAAA,EAAA,CACT;AAAA,EACD,SAAS,OAAO,OAAO;AAAA,IACrB,QAAQ;AAAA,EAAA,CACT;AACH,CAAC,GAKYC,KAA+C,OAAO,OAAO;AAAA,EACxE,WAAW;AACb,CAAC,GAKYC,KAA2C,OAAO,OAAO;AAAA,EACpE,MAAM;AAAA,EACN,WAAW;AAAA,EACX,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,iBAAiB;AAAA;AAEnB,CAAC,GAEYC,KAAqD,OAAO,OAAO;AAAA,EAC9E,SAAS;AACX,CAAC,GAKYC,KAAwD,OAAO,OAAO;AAAA,EACjF,QAAQ,OAAO,OAAO,EAAE,UAAU,KAAK;AAAA,EACvC,QAAQ,OAAO,OAAO,EAAE,UAAU,MAAM;AAC1C,CAAC,GAKYC,KAA0C,OAAO,OAAO;AAAA,EACnE,MAAM;AAAA;AAAA,EACN,SAAS;AAAA;AAAA,EACT,SAAS;AAAA;AAAA,EACT,UAAU,OAAO,OAAO;AAAA,IACtB,KAAK;AAAA;AAAA,IACL,KAAK;AAAA,EAAA,CACN;AACH,CAAC,GAKYC,KAA8C,OAAO,OAAO;AAAA,EACvE,MAAM;AAAA,EACN,OAAO,OAAO,OAAO;AAAA,IACnB,KAAK;AAAA,IACL,KAAK;AAAA,EAAA,CACN;AACH,CAAC,GAKYC,KAAoC,OAAO,OAAO;AAAA,EAC7D,QAAQF;AAAA,EACR,UAAUC;AACZ,CAAC,GASYE,KAAmD,OAAO,OAAO;AAAA,EAC5E,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,mBAAmB,CAAC,OAAO,QAAQ,OAAO,OAAO,QAAQ,KAAK;AAChE,CAAC,GAKYC,KAAoC,OAAO,OAAO;AAAA,EAC7D,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AACX,CAAC,GAEYC,KAAwC,OAAO,OAAO,EAAE,UAAU,GAAG,OAAO,KAAM,MAAM,UAAmB,GAC3GC,KAAsC,OAAO,OAAO,EAAE,UAAU,MAAM,UAAU,MAAM,OAAO,MAAM,MAAM,SAAA,CAAmB,GAC5HC,KAAsC,OAAO,OAAO,EAAE,SAAS,KAAK,OAAO,KAAM,OAAO,QAAiB,GACzGC,KAAoC,OAAO,OAAO,EAAE,OAAO,KAAM,WAAW,aAAsB,GAClGC,KAA2C,OAAO,OAAO,EAAE,MAAM,QAAiB,GAElFC,IAAmC,OAAO,OAAO;AAAA;AAAA,EAE5D,SAAS,CAAA;AAAA;AAAA,EAGT,QAAQ,OAAO,OAAO;AAAA,IACpB,SAASP;AAAA,IACT,OAAOC;AAAA,EAAA,CACR;AAAA;AAAA,EAGD,OAAOF;AAAA;AAAA,EAGP,QAAQ,OAAO,OAAO;AAAA,IACpB,WAAW;AAAA,IACX,YAAY;AAAA;AAAA,IACZ,YAAYH;AAAA,IACZ,gBAAgB;AAAA;AAAA,IAChB,eAAe;AAAA;AAAA,IACf,SAAS,OAAO,OAAO;AAAA,MACrB,SAAS;AAAA;AAAA,IAAA,CACV;AAAA,EAAA,CACF;AAAA;AAAA,EAGD,WAAW,OAAO,OAAO;AAAA,IACvB,UAAU;AAAA;AAAA,IACV,QAAQ,OAAO,OAAO;AAAA,MACpB,SAAS;AAAA;AAAA,MACT,QAAQ;AAAA;AAAA,MACR,OAAO;AAAA;AAAA,IAAA,CACR;AAAA,IACD,OAAO,OAAO,OAAO;AAAA,MACnB,SAAS;AAAA;AAAA,MACT,UAAU;AAAA;AAAA,IAAA,CACX;AAAA,IACD,OAAO,OAAO,OAAO;AAAA,MACnB,OAAO,OAAO,OAAO;AAAA,QACnB,UAAU;AAAA;AAAA,QACV,QAAQ;AAAA;AAAA,QACR,UAAU,OAAO,OAAO;AAAA,UACtB,QAAQ;AAAA;AAAA,UACR,cAAc;AAAA,QAAA,CACf;AAAA,MAAA,CACF;AAAA,MACD,QAAQ,OAAO,OAAO;AAAA,QACpB,UAAU;AAAA;AAAA,MAAA,CACX;AAAA,MACD,QAAQ;AAAA;AAAA,MACR,MAAMP;AAAA,MACN,UAAUC;AAAA,MACV,OAAOC;AAAA,IAAA,CACR;AAAA,IACD,MAAMe;AAAA,EAAA,CACP;AAAA;AAAA,EAGD,aAAa,OAAO,OAAO;AAAA,IACzB,OAAO,OAAO,OAAO;AAAA,MACnB,cAAc;AAAA;AAAA,MACd,QAAQ;AAAA,MACR,mBAAmB;AAAA;AAAA,IAAA,CACpB;AAAA,IACD,YAAY,OAAO,OAAO;AAAA,MACxB,UAAU;AAAA,MACV,OAAO;AAAA,MACP,YAAY;AAAA;AAAA,IAAA,CACb;AAAA,IACD,UAAU;AAAA,EAAA,CACX;AAAA;AAAA,EAGD,IAAI,OAAO,OAAO;AAAA,IAChB,oBAAoB;AAAA,IACpB,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,IAChB,kBAAkB;AAAA,EAAA,CACnB;AAAA;AAAA,EAGD,SAASd;AACX,CAAC;AAKD,SAASgB,EACPC,GACAC,GACiB;AACjB,MAAI,CAACD,EAAM,QAAOC,KAA+B,CAAA;AACjD,MAAI,CAACA,EAAU,QAAO,EAAE,GAAGD,EAAA;AAE3B,QAAME,IAA0B,EAAE,GAAGF,EAAA;AAGrC,SAAIC,EAAS,WAAW,WACtBC,EAAO,SAAS,EAAE,GAAGF,EAAK,QAAQ,GAAGC,EAAS,OAAA,IAI5CA,EAAS,cAAc,WACzBC,EAAO,YAAY,EAAE,GAAGF,EAAK,WAAW,GAAGC,EAAS,UAAA,IAElDA,EAAS,gBAAgB,WAC3BC,EAAO,cAAc,EAAE,GAAGF,EAAK,aAAa,GAAGC,EAAS,YAAA,IAEtDA,EAAS,iBAAiB,WAC5BC,EAAO,eAAe,EAAE,GAAGF,EAAK,cAAc,GAAGC,EAAS,aAAA,IAExDA,EAAS,eAAe,WAC1BC,EAAO,aAAa,EAAE,GAAGF,EAAK,YAAY,GAAGC,EAAS,WAAA,IAIpDA,EAAS,WAAW,WACtBC,EAAO,SAAS,EAAE,GAAGF,EAAK,QAAQ,GAAGC,EAAS,OAAA,IAI5CA,EAAS,YAAY,WACvBC,EAAO,UAAU,EAAE,GAAGF,EAAK,SAAS,GAAGC,EAAS,QAAA,IAI9CA,EAAS,WAAW,WAAWC,EAAO,SAASD,EAAS,SACxDA,EAAS,aAAa,WAAWC,EAAO,WAAWD,EAAS,WAC5DA,EAAS,YAAY,WAAWC,EAAO,UAAUD,EAAS,UAC1DA,EAAS,WAAW,WAAWC,EAAO,SAASD,EAAS,SACxDA,EAAS,cAAc,WAAWC,EAAO,YAAYD,EAAS,YAC9DA,EAAS,cAAc,WAAWC,EAAO,YAAYD,EAAS,YAC9DA,EAAS,gBAAgB,WAAWC,EAAO,cAAcD,EAAS,cAGlEA,EAAS,wBAAwB,WAAWC,EAAO,sBAAsBD,EAAS,sBAClFA,EAAS,yBAAyB,WAAWC,EAAO,uBAAuBD,EAAS,uBACpFA,EAAS,4BAA4B,WAAWC,EAAO,0BAA0BD,EAAS,0BAC1FA,EAAS,2BAA2B,WAAWC,EAAO,yBAAyBD,EAAS,yBAErFC;AACT;AAOA,SAASC,GACPC,GACAC,GACoB;AACpB,MAAI,CAACA,EAAa,QAAO,EAAE,GAAGD,EAAA;AAG9B,QAAME,IAAgBP,EAAoBK,EAAS,SAASC,EAAY,OAAO,GAGzEE,IAAcR;AAAA,IAClBA,EAAoBO,GAAeF,EAAS,KAAK;AAAA,IACjDC,EAAY;AAAA,EAAA,GAIRG,IAAgBT;AAAA,IACpBA,EAAoBO,GAAeF,EAAS,OAAO;AAAA,IACnDC,EAAY;AAAA,EAAA;AAGd,SAAO;AAAA,IACL,SAASC;AAAA,IACT,OAAOC;AAAA,IACP,SAASC;AAAA,EAAA;AAEb;AASA,SAASC,GACPL,GACAM,GACa;AACb,MAAI,CAACA,EAAW,QAAO,EAAE,GAAGN,EAAA;AAE5B,QAAMF,IAAsB,EAAE,GAAGE,EAAA;AAGjC,MAAIM,EAAU,WAAW,WACvBR,EAAO,SAAS;AAAA,IACd,GAAGE,EAAS;AAAA,IACZ,GAAGM,EAAU;AAAA,EAAA,GAIXA,EAAU,OAAO,WAAU;AAC7B,UAAMC,IAAeD,EAAU,OAAO,UAChCE,IAAWD,EAAa,QAAQ,UAAaA,EAAa,OAAO,QAAQA,EAAa,OAAO,IAC/FA,EAAa,MACbP,EAAS,QAAQ,UAAU,OAAO,GAChCS,IAAWF,EAAa,QAAQ,UAAaA,EAAa,OAAO,KAAKA,EAAa,OAAO,OAC5FA,EAAa,MACbP,EAAS,QAAQ,UAAU,OAAO;AACtC,IAAAF,EAAO,OAAQ,WAAW,EAAE,KAAKU,GAAU,KAAKC,EAAA;AAAA,EAClD;AAIF,MAAIH,EAAU,aAAa,WACzBR,EAAO,WAAW;AAAA,IAChB,GAAGE,EAAS;AAAA,IACZ,GAAGM,EAAU;AAAA,EAAA,GAIXA,EAAU,SAAS,QAAO;AAC5B,UAAMI,IAAYJ,EAAU,SAAS,OAC/BE,IAAWE,EAAU,QAAQ,UAAaA,EAAU,OAAO,QAAQA,EAAU,OAAO,IACtFA,EAAU,MACVV,EAAS,UAAU,OAAO,OAAO,KAC/BS,IAAWC,EAAU,QAAQ,UAAaA,EAAU,OAAO,KAAKA,EAAU,OAAO,MACnFA,EAAU,MACVV,EAAS,UAAU,OAAO,OAAO;AACrC,IAAAF,EAAO,SAAU,QAAQ,EAAE,KAAKU,GAAU,KAAKC,EAAA;AAAA,EACjD;AAGF,SAAOX;AACT;AAMA,SAASa,GAA4BC,GAA6E;AAChH,QAAMC,IAAkBD,EAAW,QAAgB;AACnD,MAAKC,KAGD,aAAaA;AACf,WAAO;AAAA,MACL,UAAU;AAAA,QACR,MAAMA,EAAe,UAAU,WAAW;AAAA,QAC1C,OAAOA,EAAe;AAAA,MAAA;AAAA,IACxB;AAKN;AAKA,SAASC,GAA4BF,GAA6E;AAChH,QAAMG,IAAkBH,EAAW,QAAgB,QAAQ;AAC3D,MAAKG;AAEL,WAAO;AAAA,MACL,QAAQ;AAAA,QACN,MAAM;AAAA;AAAA,QACN,UAAUA;AAAA,MAAA;AAAA,IACZ;AAEJ;AAEO,SAASC,GACdJ,IAAgC,IACd;AAElB,QAAMC,IAAiBF,GAA4BC,CAAiB,GAC9DG,IAAiBD,GAA4BF,CAAiB;AAIpE,MAAIK,IAAwDL,EAAW;AACvE,GAAIC,KAAkBE,OACpBE,IAAsB;AAAA,IACpB,GAAIF,KAAkB,CAAA;AAAA,IACtB,GAAIF,KAAkB,CAAA;AAAA,IACtB,GAAGI;AAAA,EAAA,GAGDA,EAAoB,YAAYJ,GAAgB,YAAYD,EAAW,OAAO,aAChFK,EAAoB,WAAW;AAAA,IAC7B,GAAGJ,EAAe;AAAA,IAClB,GAAID,EAAW,MAAc;AAAA,EAAA;AAMnC,QAAMM,IAAU,CAAC,GAAIN,EAAW,WAAW,CAAA,CAAG;AAC9C,EAAIA,EAAW,UAAUA,EAAW,OAAO,SAAS,KAClDM,EAAQ,QAAQ;AAAA,IACd,QAAQ;AAAA,MACN,SAAS,CAAC,EAAE,MAAMN,EAAW,QAAQ;AAAA,IAAA;AAAA,EACvC,CACD;AASH,QAAMO,IAA8B;AAAA,IAClC,SAN6C;AAAA,MAC7C,GAAGhC;AAAA,MACH,GAAIyB,EAAW,QAAQ,WAAW,CAAA;AAAA,IAAC;AAAA,EAI1B,GAGLd,IAA2B;AAAA,IAC/B,SAAAoB;AAAA,IACA,QAAQC;AAAA,IACR,OAAOd,GAAqBnB,IAAsB+B,CAAmB;AAAA,IACrE,QAAQ,EAAE,GAAGvB,EAAe,OAAA;AAAA,IAC5B,WAAW,EAAE,GAAGA,EAAe,UAAA;AAAA,IAC/B,aAAa,EAAE,GAAGA,EAAe,YAAA;AAAA,IACjC,IAAI,EAAE,GAAGA,EAAe,GAAA;AAAA,IACxB,SAASK,GAAiBpB,IAAiBiC,EAAW,OAAkD;AAAA,EAAA;AAI1G,EAAIA,EAAW,WACbd,EAAO,SAAS;AAAA,IACd,GAAGJ,EAAe;AAAA,IAClB,GAAGkB,EAAW;AAAA,EAAA,GAIZA,EAAW,OAAO,eACpBd,EAAO,OAAO,aAAa;AAAA,IACzB,GAAGJ,EAAe,OAAO;AAAA,IACzB,QAAQkB,EAAW,OAAO,WAAW,SACjC,EAAE,GAAGlB,EAAe,OAAO,WAAY,QAAQ,GAAGkB,EAAW,OAAO,WAAW,WAC/ElB,EAAe,OAAO,WAAY;AAAA,IACtC,QAAQkB,EAAW,OAAO,WAAW,SACjC,EAAE,GAAGlB,EAAe,OAAO,WAAY,QAAQ,GAAGkB,EAAW,OAAO,WAAW,WAC/ElB,EAAe,OAAO,WAAY;AAAA,EAAA,IAKtCkB,EAAW,OAAO,YACpBd,EAAO,OAAO,UAAU;AAAA,IACtB,GAAGJ,EAAe,OAAO;AAAA,IACzB,GAAGkB,EAAW,OAAO;AAAA,EAAA,KAMvBA,EAAW,cACbd,EAAO,YAAY;AAAA,IACjB,GAAGJ,EAAe;AAAA,IAClB,GAAGkB,EAAW;AAAA,EAAA,GAIZA,EAAW,UAAU,WACvBd,EAAO,UAAU,SAAS;AAAA,IACxB,GAAGJ,EAAe,UAAU;AAAA,IAC5B,GAAGkB,EAAW,UAAU;AAAA,EAAA,IAKxBA,EAAW,UAAU,UACvBd,EAAO,UAAU,QAAQ;AAAA,IACvB,GAAGJ,EAAe,UAAU;AAAA,IAC5B,GAAGkB,EAAW,UAAU;AAAA,EAAA,IAKxBA,EAAW,UAAU,UACvBd,EAAO,UAAU,QAAQ;AAAA,IACvB,GAAGJ,EAAe,UAAU;AAAA,IAC5B,GAAGkB,EAAW,UAAU;AAAA,IACxB,OAAOA,EAAW,UAAU,MAAM,QAC9B;AAAA,MACE,GAAGlB,EAAe,UAAU,MAAO;AAAA,MACnC,GAAGkB,EAAW,UAAU,MAAM;AAAA,MAC9B,UAAUA,EAAW,UAAU,MAAM,MAAM,WACvC,EAAE,GAAGlB,EAAe,UAAU,MAAO,MAAM,UAAU,GAAGkB,EAAW,UAAU,MAAM,MAAM,aACzFlB,EAAe,UAAU,MAAO,MAAM;AAAA,IAAA,IAE5CA,EAAe,UAAU,MAAO;AAAA,IACpC,QAAQkB,EAAW,UAAU,MAAM,SAC/B,EAAE,GAAGlB,EAAe,UAAU,MAAO,QAAQ,GAAGkB,EAAW,UAAU,MAAM,WAC3ElB,EAAe,UAAU,MAAO;AAAA,IACpC,MAAMkB,EAAW,UAAU,MAAM,OAC7B,EAAE,GAAGpC,IAAqB,GAAGoC,EAAW,UAAU,MAAM,KAAA,IACxDlB,EAAe,UAAU,MAAO;AAAA,IACpC,UAAUkB,EAAW,UAAU,MAAM,WACjC,EAAE,GAAGnC,IAAwB,GAAGmC,EAAW,UAAU,MAAM,SAAA,IAC3DlB,EAAe,UAAU,MAAO;AAAA,IACpC,OAAOkB,EAAW,UAAU,MAAM,QAC9B,EAAE,GAAGlC,IAAqB,GAAGkC,EAAW,UAAU,MAAM,MAAA,IACxDlB,EAAe,UAAU,MAAO;AAAA,EAAA,IAKpCkB,EAAW,UAAU,SACvBd,EAAO,UAAU,OAAO;AAAA,IACtB,GAAGL;AAAA,IACH,GAAGmB,EAAW,UAAU;AAAA,EAAA,KAM1BA,EAAW,gBACbd,EAAO,cAAc;AAAA,IACnB,GAAGJ,EAAe;AAAA,IAClB,GAAGkB,EAAW;AAAA,EAAA,GAIZA,EAAW,YAAY,UACzBd,EAAO,YAAY,QAAQ;AAAA,IACzB,GAAGJ,EAAe,YAAY;AAAA,IAC9B,GAAGkB,EAAW,YAAY;AAAA,EAAA,IAK1BA,EAAW,YAAY,eACzBd,EAAO,YAAY,aAAa;AAAA,IAC9B,GAAGJ,EAAe,YAAY;AAAA,IAC9B,GAAGkB,EAAW,YAAY;AAAA,EAAA;AAOhC,QAAMQ,IAAYR,EAAmB,WAAW;AAiBhD,MAhBIQ,KACF,QAAQ,KAAK,oEAAoE,GAEnFtB,EAAO,KAAK;AAAA,IACV,GAAGJ,EAAe;AAAA,IAClB,GAAG0B;AAAA,IACH,GAAGR,EAAW;AAAA,EAAA,GAIhBd,EAAO,OAAO,QAAQ;AAAA,IACpB,GAAGV;AAAA,IACH,GAAIwB,EAAW,QAAQ,SAAS,CAAA;AAAA,EAAC,GAI/Bd,EAAO,OAAO,cAAc,eAAeA,EAAO,SAAS;AAC7D,UAAMuB,IAAgB,EAAE,OAAO,WAAoB,MAAM,kBAAA;AACzD,IAAAvB,EAAO,UAAU;AAAA,MACf,GAAGA,EAAO;AAAA,MACV,SAAS,EAAE,GAAGA,EAAO,QAAQ,SAAS,UAAUuB,EAAA;AAAA,MAChD,OAAS,EAAE,GAAGvB,EAAO,QAAQ,OAAS,UAAUuB,EAAA;AAAA;AAAA,IAAc;AAAA,EAGlE;AAEA,SAAOvB;AACT;AAKO,SAASwB,GACdC,GACAC,GACkB;AAElB,SAAO,EAAE,GADID,IAASlD,GAAekD,CAAM,IAAIlD,GAAe,SAC5C,GAAGmD,EAAA;AACvB;AAKO,SAASC,GACdF,GACAC,GACmB;AAEnB,SAAO,EAAE,GADID,IAASjD,GAAgBiD,CAAM,IAAIjD,GAAgB,QAC9C,GAAGkD,EAAA;AACvB;AAKO,SAASE,GACdH,GACAC,GACgB;AAEhB,SAAO,EAAE,GADID,IAAShD,GAAkBgD,CAAM,IAAIhD,GAAkB,QAClD,GAAGiD,EAAA;AACvB;ACtqBO,MAAMG,GAAgB;AAAA,EAK3B,YAAYC,GAAyB;AAHrC,SAAQ,uCAA0D,IAAA,GAClE,KAAQ,qBAAqB,GAG3B,KAAK,SAASA;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,qBAAqBC,GAAiC;AAC5D,UAAMC,IAAuB,CAAC,uBAAuB;AAErD,QAAID,EAAO,MAAM,UAAaA,EAAO,MAAM,QAAW;AACpD,YAAME,IAAIF,EAAO,KAAK,GAChBG,IAAIH,EAAO,KAAK;AACtB,MAAAC,EAAW,KAAK,aAAaC,CAAC,OAAOC,CAAC,KAAK;AAAA,IAC7C;AAEA,WAAIH,EAAO,aAAa,UACtBC,EAAW,KAAK,UAAUD,EAAO,QAAQ,MAAM,GAG7CA,EAAO,UAAU,UACnBC,EAAW,KAAK,SAASD,EAAO,KAAK,GAAG,GAGnCC,EAAW,KAAK,GAAG;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,4BACEG,GACAC,GACAC,GACAC,IAA0B,MAC1BC,IAAwB,MACP;AAEjB,SAAK,oBAAoBJ,CAAO;AAEhC,UAAMK,IAAeF,KAAY,KAAK,OAAO,UACvCG,IAAaF,KAAU,KAAK,OAAO,OAAO,SAE1CG,IAAgB,KAAK,qBAAqBN,CAAI,GAC9CO,IAAc,KAAK,qBAAqBN,CAAE;AAGhD,IAAAF,EAAQ,MAAM,aAAa;AAG3B,UAAMS,IAAYT,EAAQ;AAAA,MACxB;AAAA,QACE,EAAE,WAAWO,EAAA;AAAA,QACb,EAAE,WAAWC,EAAA;AAAA,MAAY;AAAA,MAE3B;AAAA,QACE,UAAUH;AAAA,QACV,QAAQC;AAAA,QACR,MAAM;AAAA;AAAA,MAAA;AAAA,IACR,GAGII,IAA0B;AAAA,MAC9B,IAAI,QAAQ,EAAE,KAAK,kBAAkB;AAAA,MACrC,SAAAV;AAAA,MACA,WAAAS;AAAA,MACA,WAAWR;AAAA,MACX,SAASC;AAAA,MACT,WAAW,YAAY,IAAA;AAAA,MACvB,UAAUG;AAAA,IAAA;AAGZ,gBAAK,iBAAiB,IAAIL,GAASU,CAAM,GAGzCD,EAAU,SACP,KAAK,MAAM;AAEV,MAAAT,EAAQ,MAAM,YAAYQ,GAC1B,KAAK,iBAAiB,OAAOR,CAAO;AAAA,IACtC,CAAC,EACA,MAAM,MAAM;AAEX,WAAK,iBAAiB,OAAOA,CAAO;AAAA,IACtC,CAAC,GAEIU;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,gBAAgBA,GAAyBC,IAAuB,IAAyB;AACvF,UAAMC,IAAW,KAAK,oBAAoBF,EAAO,OAAO;AAKxD,QAFAA,EAAO,UAAU,OAAA,GAEbC,GAAa;AAEf,YAAME,IAAmB,KAAK,qBAAqB;AAAA,QACjD,GAAGD,EAAS;AAAA,QACZ,GAAGA,EAAS;AAAA,QACZ,UAAUA,EAAS;AAAA,QACnB,OAAOA,EAAS;AAAA,MAAA,CACjB;AACD,MAAAF,EAAO,QAAQ,MAAM,YAAYG;AAAA,IACnC;AAEA,gBAAK,iBAAiB,OAAOH,EAAO,OAAO,GAEpCE;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,oBAAoBZ,GAA4B;AAE9C,UAAMU,IAAS,KAAK,iBAAiB,IAAIV,CAAO;AAChD,IAAIU,KACF,KAAK,gBAAgBA,GAAQ,EAAK;AAIpC,UAAMI,IAAgBd,EAAQ,cAAA;AAC9B,eAAWe,KAAQD;AACjB,MAAAC,EAAK,OAAA;AAAA,EAET;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,oBAAoBf,GAAyC;AAE3D,UAAMgB,IADW,iBAAiBhB,CAAO,EACX;AAE9B,QAAIgB,MAAiB,UAAU,CAACA;AAC9B,aAAO,EAAE,GAAG,GAAG,GAAG,GAAG,UAAU,GAAG,OAAO,EAAA;AAG3C,UAAMC,IAAS,IAAI,UAAUD,CAAY,GAGnCE,IAAQ,KAAK,KAAKD,EAAO,IAAIA,EAAO,IAAIA,EAAO,IAAIA,EAAO,CAAC,GAG3DE,IAAW,KAAK,MAAMF,EAAO,GAAGA,EAAO,CAAC,KAAK,MAAM,KAAK,KASxDnB,IAAImB,EAAO,GACXlB,IAAIkB,EAAO;AAEjB,WAAO,EAAE,GAAAnB,GAAG,GAAAC,GAAG,UAAAoB,GAAU,OAAAD,EAAA;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,mBAAmBlB,GAA+B;AAChD,WAAO,KAAK,iBAAiB,IAAIA,CAAO;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,mBAAmBA,GAAmD;AACpE,WAAO,KAAK,iBAAiB,IAAIA,CAAO;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,iBACEA,GACAoB,GACAjB,IAA0B,MAC1BC,IAAwB,MACT;AACf,WAAO,IAAI,QAAQ,CAACiB,MAAY;AAC9B,YAAMhB,IAAeF,KAAY,KAAK,OAAO,UACvCG,IAAaF,KAAU,KAAK,OAAO,OAAO;AAGhD,MAAAJ,EAAQ,MAAM,aAAa,aAAaK,CAAY,MAAMC,CAAU,gBAAgBD,CAAY,MAAMC,CAAU,IAGhHN,EAAQ,MAAM,YAAY,KAAK,qBAAqBoB,CAAU,GAG9D,WAAW,MAAM;AACf,QAAAC,EAAA;AAAA,MACF,GAAGhB,CAAY;AAAA,IACjB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eAAeL,GAAsBsB,GAA6D;AAChG,WAAO,KAAK,iBAAiBtB,GAASsB,CAAa;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAgBtB,GAA4B;AAC1C,IAAAA,EAAQ,MAAM,aAAa;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KAAKuB,GAA2B;AAC9B,WAAO,IAAI,QAAQ,CAAAF,MAAW,WAAWA,GAASE,CAAE,CAAC;AAAA,EACvD;AACF;AC3NA,SAASC,EAAKC,GAAeC,GAAaC,GAAmB;AAC3D,SAAOF,KAASC,IAAMD,KAASE;AACjC;AAMO,SAASC,GACdD,GACAF,GACAC,GACA/B,GACO;AACP,QAAM,EAAE,WAAAkC,GAAW,SAAAC,GAAS,YAAAC,EAAA,IAAepC,GAGrCqC,IAAKN,EAAI,IAAID,EAAM,GACnBQ,IAAKP,EAAI,IAAID,EAAM,GAGnBS,IAAYC,GAAwBL,GAASC,CAAU;AAG7D,MAAIK,IAAW,GACXC,IAAe,GACfC,IAAa,GACbC,IAAmBV,GACnBW,IAAmB;AAEvB,WAASC,IAAI,GAAGA,IAAIP,EAAU,QAAQO;AACpC,QAAId,KAAKO,EAAUO,CAAC,EAAE,MAAM;AAC1B,MAAAJ,IAAeI,MAAM,IAAI,IAAIP,EAAUO,IAAI,CAAC,EAAE,MAC9CH,IAAaJ,EAAUO,CAAC,EAAE,MAC1BF,IAAmBL,EAAUO,CAAC,EAAE,WAChCD,IAAmBN,EAAUO,CAAC,EAAE;AAChC;AAAA,IACF;AAIF,QAAMC,KAAYf,IAAIU,MAAiBC,IAAaD;AAEpD,MAAIG;AAEF,IAAAJ,IAAW,IAAIG,IAAmBI,GAAYD,CAAQ;AAAA,WAC7CL,MAAiB;AAE1B,IAAAD,IAAWO,GAAYD,CAAQ;AAAA,OAC1B;AAKL,UAAME,IAAe,KAHCV,EAAU;AAAA,MAAK,CAACW,GAAGJ,MACvCI,EAAE,OAAOR,KAAgBI,IAAI,KAAKP,EAAUO,IAAI,CAAC,EAAE;AAAA,IAAA,GAEZ,aAAaF;AACtD,IAAAH,IAAWZ,EAAKoB,GAAc,GAAGD,GAAYD,CAAQ,CAAC;AAAA,EACxD;AAEA,SAAO;AAAA,IACL,GAAGjB,EAAM,IAAIO,IAAKI;AAAA,IAClB,GAAGX,EAAM,IAAIQ,IAAKG;AAAA,EAAA;AAEtB;AAKA,SAASD,GACPL,GACAC,GACkE;AAClE,QAAMG,IAA8E,CAAA;AAGpF,MAAIY,IAAc;AAClB,EAAAZ,EAAU,KAAK,EAAE,MAAMY,GAAa,WAAW,GAAG,aAAa,IAAO;AAEtE,MAAIC,IAAmB;AAEvB,QAAMC,IADgB,OACclB,IAAU;AAE9C,WAASW,IAAI,GAAGA,IAAIX,GAASW;AAE3B,IAAAK,KAAeE,GACfd,EAAU,KAAK,EAAE,MAAMY,GAAa,WAAWC,GAAkB,aAAa,IAAM,GAGpFD,KAAeE,GACfd,EAAU,KAAK,EAAE,MAAMY,GAAa,WAAWC,IAAmBhB,GAAY,aAAa,IAAO,GAElGgB,KAAoBhB;AAItB,SAAAG,EAAU,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,IAAO,GAErDA;AACT;AAMO,SAASe,GACdtB,GACAF,GACAC,GACA/B,GACO;AACP,QAAM,EAAE,WAAAuD,GAAW,SAAAC,GAAS,MAAAC,GAAM,cAAAC,MAAiB1D,GAG7CqC,IAAKN,EAAI,IAAID,EAAM,GACnBQ,IAAKP,EAAI,IAAID,EAAM,GAInB6B,IAAQ,KAAK,KAAKJ,IAAYE,CAAI,GAGlCG,IAAOJ,KAAW,IAAI,KAAK,KAAKD,IAAYE,CAAI;AAGtD,MAAIhB;AAEJ,MAAImB,IAAO,GAAG;AAEZ,UAAMC,IAAaF,IAAQ,KAAK,KAAK,IAAIC,IAAOA,CAAI,GAC9CE,IAAW,KAAK,IAAI,CAACF,IAAOD,IAAQ3B,IAAI,CAAC,GACzC+B,IAAc,KAAK,IAAIF,IAAa7B,IAAI0B,IAAe,KAAK,EAAE;AACpE,IAAAjB,IAAW,IAAIqB,IAAWC;AAAA,EAC5B;AAEE,IAAAtB,IAAW,IAAI,KAAK,IAAI,CAACkB,IAAQ3B,IAAI,CAAC;AAIxC,SAAAS,IAAW,KAAK,IAAI,GAAG,KAAK,IAAIA,GAAU,GAAG,CAAC,GAEvC;AAAA,IACL,GAAGX,EAAM,IAAIO,IAAKI;AAAA,IAClB,GAAGX,EAAM,IAAIQ,IAAKG;AAAA,EAAA;AAEtB;AAMO,SAASuB,GACdhC,GACAF,GACAC,GACA/B,GACO;AACP,QAAM,EAAE,WAAAiE,GAAW,WAAAC,GAAW,OAAAC,GAAO,WAAAC,GAAW,OAAAC,MAAUrE,GAGpDqC,IAAKN,EAAI,IAAID,EAAM,GACnBQ,IAAKP,EAAI,IAAID,EAAM,GACnBwC,IAAS,KAAK,KAAKjC,IAAKA,IAAKC,IAAKA,CAAE,GAGpCiC,IAAQD,IAAS,IAAI,CAAChC,IAAKgC,IAAS,GACpCE,IAAQF,IAAS,IAAIjC,IAAKiC,IAAS,GAGnCG,IAAYP,IAAY,KAAK,KAAK,IAAIlC,IAAIqC,GAC1CK,IAAcP,IAAQ,KAAK,IAAI,IAAInC,GAAGoC,CAAS,IAAI,GACnDO,IAAaV,IAAY,KAAK,IAAIQ,CAAS,IAAIC,GAG/CE,IAAYC,GAAa7C,CAAC;AAGhC,SAAO;AAAA,IACL,GAAGH,EAAKC,EAAM,GAAGC,EAAI,GAAG6C,CAAS,IAAID,IAAaJ;AAAA,IAClD,GAAG1C,EAAKC,EAAM,GAAGC,EAAI,GAAG6C,CAAS,IAAID,IAAaH;AAAA,EAAA;AAEtD;AAKA,SAASxB,GAAYhB,GAAmB;AACtC,SAAO,KAAK,IAAIA,MAAM,IAAIA;AAC5B;AAEA,SAAS6C,GAAa7C,GAAmB;AACvC,SAAO,IAAI,KAAK,IAAI,IAAIA,GAAG,CAAC;AAC9B;AAKA,SAAS8C,GACPrC,GACAsC,GACAC,GACQ;AACR,QAAM,EAAE,WAAAf,GAAW,WAAAC,GAAW,OAAAC,EAAA,IAAUa,GAGlCjB,IAAc,KAAK,IAAItB,IAAWyB,IAAY,KAAK,KAAK,CAAC,GAGzDQ,IAAcP,IAAQ,KAAK,IAAI,IAAI1B,GAAU,CAAC,IAAI,GAGlDwC,IAAehB,IAAYF,IAAcW;AAE/C,SAAOK,IAAgBE;AACzB;AAKA,SAASC,GACPzC,GACA0C,GACAC,GACQ;AACR,QAAM,EAAE,WAAAlD,GAAW,SAAAC,EAAA,IAAYiD,GAGzB7C,IAAoD,CAAA;AAG1D,EAAAA,EAAU,KAAK,EAAE,MAAM,KAAK,OAAOL,GAAW;AAG9C,MAAIkB,IAAmBlB;AACvB,QAAMmD,IAAc,KAEdhC,IADgB,OACclB,IAAU;AAE9C,MAAIgB,IAAc;AAClB,WAASL,IAAI,GAAGA,IAAIX,GAASW,KAAK;AAChC,UAAMwC,IAAa,KAAKlC,IAAmB,KAAKiC;AAChD,IAAAlC,KAAeE,GACfd,EAAU,KAAK,EAAE,MAAMY,GAAa,OAAOmC,GAAY,GAEvDlC,IAAmB,KAAKA,IAAmB,KAAKiC,IAAcA,GAC9DlC,KAAeE,GACXP,IAAIX,IAAU,KAChBI,EAAU,KAAK,EAAE,MAAMY,GAAa,OAAOC,GAAkB;AAAA,EAEjE;AAEA,EAAAb,EAAU,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG;AAGpC,MAAIgD,IAAe;AACnB,WAASzC,IAAI,GAAGA,IAAIP,EAAU,QAAQO;AACpC,QAAIL,KAAYF,EAAUO,CAAC,EAAE,MAAM;AACjC,YAAM0C,IAAW1C,MAAM,IAAI,IAAIP,EAAUO,IAAI,CAAC,EAAE,MAC1C2C,IAAY3C,MAAM,IAAI,IAAIP,EAAUO,IAAI,CAAC,EAAE,OAC3C4C,KAAmBjD,IAAW+C,MAAajD,EAAUO,CAAC,EAAE,OAAO0C,IAC/DG,IAAgB3C,GAAY0C,CAAe;AACjD,MAAAH,IAAeE,KAAalD,EAAUO,CAAC,EAAE,QAAQ2C,KAAaE;AAC9D;AAAA,IACF;AAGF,SAAOJ,IAAeJ;AACxB;AAKO,SAASS,GAAYC,GAAqC;AAC/D,QAAM;AAAA,IACJ,SAAAxF;AAAA,IACA,eAAAyF;AAAA,IACA,aAAAC;AAAA,IACA,YAAAC;AAAA,IACA,UAAAxF;AAAA,IACA,YAAAyF;AAAA,IACA,aAAAC;AAAA,IACA,UAAUnB;AAAA,IACV,OAAOI;AAAA,IACP,YAAAgB;AAAA,IACA,gBAAAC;AAAA,IACA,eAAAC;AAAA,IACA,aAAAC;AAAA,IACA,YAAAC;AAAA,EAAA,IACEV,GAEEW,IAAWR,EAAW,MAGtBS,IAAkBJ,MAAkB,UAAaA,MAAkBtB,GACnE2B,IAAeN,GAAgB,SAAS,UACxCpB,IAAeoB,GAAgB,UAAU,EAAE,WAAW,IAAI,WAAW,GAAG,OAAO,GAAA,GAC/EO,IAAyBF,KAAmBC,GAG5CE,IAAeL,MAAe,UAAaA,MAAepB,GAC1D0B,IAAYP,GAAa,SAAS,OAClClB,IAAYkB,GAAa,OAAO,EAAE,WAAW,KAAK,SAAS,EAAA;AAIjE,OAAKE,MAAa,YAAYA,MAAa,UAAU,CAACG,KAA0B,EAHpDC,KAAgBC,IAG0D;AACpG,IAAIV,KAAYA,EAAA;AAChB;AAAA,EACF;AAEA,QAAMW,IAAY,YAAY,IAAA,GAGxBC,IAAgB,CAACd,IAAa,GAC9Be,IAAgB,CAACd,IAAc;AAErC,WAASe,EAAK9D,GAA2B;AACvC,UAAM+D,IAAU/D,IAAc2D,GACxB9E,IAAI,KAAK,IAAIkF,IAAU1G,GAAU,CAAC;AAGxC,QAAI2G;AAEJ,YAAQX,GAAA;AAAA,MACN,KAAK,UAAU;AACb,cAAMxG,IAASN;AAAA,UACbsG,EAAW;AAAA,UACXA,EAAW;AAAA,QAAA;AAEb,QAAAmB,IAAWlF,GAAwBD,GAAG8D,GAAeC,GAAa/F,CAAM;AACxE;AAAA,MACF;AAAA,MACA,KAAK,WAAW;AACd,cAAMA,IAASH;AAAA,UACbmG,EAAW;AAAA,UACXA,EAAW;AAAA,QAAA;AAEb,QAAAmB,IAAW7D,GAAyBtB,GAAG8D,GAAeC,GAAa/F,CAAM;AACzE;AAAA,MACF;AAAA,MACA,KAAK,QAAQ;AACX,cAAMA,IAASF;AAAA,UACbkG,EAAW;AAAA,UACXA,EAAW;AAAA,QAAA;AAEb,QAAAmB,IAAWnD,GAAsBhC,GAAG8D,GAAeC,GAAa/F,CAAM;AACtE;AAAA,MACF;AAAA,MACA;AACE,QAAAmH,IAAW;AAAA,UACT,GAAGtF,EAAKiE,EAAc,GAAGC,EAAY,GAAG/D,CAAC;AAAA,UACzC,GAAGH,EAAKiE,EAAc,GAAGC,EAAY,GAAG/D,CAAC;AAAA,QAAA;AAAA,IAC3C;AAIJ,UAAMoF,IAAaD,EAAS,IAAIpB,EAAY,GACtCsB,IAAaF,EAAS,IAAIpB,EAAY;AAG5C,QAAIuB;AACJ,IAAIZ,IACFY,IAAkBxC,GAAwB9C,GAAG+C,GAAeC,CAAY,IAC/DyB,IACTa,IAAkBzF,EAAKwE,GAAgBtB,GAAe/C,CAAC,IAEvDsF,IAAkBvC;AAIpB,QAAIQ;AACJ,IAAIsB,IACFtB,IAAeL,GAAkBlD,GAAGmD,GAAYC,CAAS,IAChDwB,IACTrB,IAAe1D,EAAK0E,GAAapB,GAAYnD,CAAC,IAE9CuD,IAAeJ,GAIjB9E,EAAQ,MAAM,YACZ,aAAa0G,CAAa,OAAOC,CAAa,iBACjCI,CAAU,OAAOC,CAAU,cAC9BC,CAAe,cAAc/B,CAAY,KAEjDvD,IAAI,IACN,sBAAsBiF,CAAI,KAG1B5G,EAAQ,MAAM,YACZ,aAAa0G,CAAa,OAAOC,CAAa,cACpCjC,CAAa,cAAcI,CAAU,KAC7CgB,KAAYA,EAAA;AAAA,EAEpB;AAEA,wBAAsBc,CAAI;AAC5B;AAKO,SAASM,GAAoBf,GAAkC;AACpE,SAAOA,MAAa,YAAYA,MAAa,aAAaA,MAAa;AACzE;ACjbA,MAAMgB,KAAqE;AAAA,EACzE,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,WAAW;AACb;AAeO,MAAMC,GAAqB;AAAA,EAQhC,YAAYzH,GAA8B0H,GAAkC;AAC1E,SAAK,SAAS1H,GACd,KAAK,kBAAkB0H,GAGvB,KAAK,wBAAwB,KAAK,qBAAA,GAGlC,KAAK,aAAa1H,EAAO,QAAQpD,IAGjC,KAAK,iBAAiBoD,EAAO,YAAYnD,IAGzC,KAAK,cAAcmD,EAAO,SAASlD;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKQ,uBAA2C;AAEjD,WAAI,KAAK,OAAO,MAAM,WACb,KAAK,OAAO,MAAM,WAGpB0K,GAAsB,KAAK,eAAe,KAAK;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKA,uBACEG,GACAC,GACAC,GACAC,GACAC,GACe;AACf,UAAMZ,IAAW,KAAK,uBAChBa,IAAS,KAAK,OAAO,MAAM,UAAU;AAE3C,YAAQb,GAAA;AAAA,MACN,KAAK;AACH,eAAO,KAAK,qBAAqBQ,GAAeC,GAAWC,GAAiBG,CAAM;AAAA,MAEpF,KAAK;AACH,eAAO,KAAK,sBAAsB,OAAOL,GAAeC,GAAWC,GAAiBG,CAAM;AAAA,MAE5F,KAAK;AACH,eAAO,KAAK,sBAAsB,UAAUL,GAAeC,GAAWC,GAAiBG,CAAM;AAAA,MAE/F,KAAK;AACH,eAAO,KAAK,sBAAsB,QAAQL,GAAeC,GAAWC,GAAiBG,CAAM;AAAA,MAE7F,KAAK;AACH,eAAO,KAAK,sBAAsB,SAASL,GAAeC,GAAWC,GAAiBG,CAAM;AAAA,MAE9F,KAAK;AACH,eAAO,KAAK,wBAAwBH,GAAiBF,GAAeC,CAAS;AAAA,MAE/E,KAAK;AACH,eAAO,KAAK,oBAAoBD,GAAeC,GAAWC,GAAiBG,CAAM;AAAA,MAEnF,KAAK;AACH,eAAO,KAAK;AAAA,UACVL;AAAA,UACAC;AAAA,UACAC;AAAA,UACAC;AAAA,UACAC;AAAA,QAAA;AAAA,MAGJ;AACE,eAAO,KAAK,qBAAqBJ,GAAeC,GAAWC,GAAiBG,CAAM;AAAA,IAAA;AAAA,EAExF;AAAA;AAAA;AAAA;AAAA,EAKQ,qBACNL,GACAC,GACAC,GACAG,GACe;AAEf,UAAMC,IAAUN,EAAc,GACxBO,IAAUP,EAAc,GAExBQ,IAAWF,GACXG,IAAYP,EAAgB,QAAQI,GACpCI,IAAUH,GACVI,IAAaT,EAAgB,SAASK,GAEtCK,IAAU,KAAK,IAAIJ,GAAUC,GAAWC,GAASC,CAAU;AAEjE,QAAIE,IAASb,EAAc,GACvBc,IAASd,EAAc;AAE3B,WAAIY,MAAYJ,IAEdK,IAAS,EAAEZ,EAAU,QAAQI,KACpBO,MAAYH,IAErBI,IAASX,EAAgB,QAAQG,IACxBO,MAAYF,IAErBI,IAAS,EAAEb,EAAU,SAASI,KAG9BS,IAASZ,EAAgB,SAASG,GAG7B,EAAE,GAAGQ,GAAQ,GAAGC,EAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKQ,sBACNC,GACAf,GACAC,GACAC,GACAG,GACe;AACf,QAAIQ,IAASb,EAAc,GACvBc,IAASd,EAAc;AAE3B,YAAQe,GAAA;AAAA,MACN,KAAK;AACH,QAAAD,IAAS,EAAEb,EAAU,SAASI;AAC9B;AAAA,MACF,KAAK;AACH,QAAAS,IAASZ,EAAgB,SAASG;AAClC;AAAA,MACF,KAAK;AACH,QAAAQ,IAAS,EAAEZ,EAAU,QAAQI;AAC7B;AAAA,MACF,KAAK;AACH,QAAAQ,IAASX,EAAgB,QAAQG;AACjC;AAAA,IAAA;AAGJ,WAAO,EAAE,GAAGQ,GAAQ,GAAGC,EAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKQ,wBACNZ,GACAc,GACAC,GACe;AAEf,UAAMX,IAAUJ,EAAgB,QAAQ,GAClCK,IAAUL,EAAgB,SAAS;AAEzC,WAAO;AAAA,MACL,GAAGI;AAAA,MACH,GAAGC;AAAA,MACH,UAAU;AAAA;AAAA,IAAA;AAAA,EAEd;AAAA;AAAA;AAAA;AAAA,EAKQ,oBACNP,GACAC,GACAC,GACAG,GACe;AACf,UAAMa,IAAiD,CAAC,OAAO,UAAU,QAAQ,OAAO,GAClFC,IAAaD,EAAM,KAAK,MAAM,KAAK,OAAA,IAAWA,EAAM,MAAM,CAAC;AACjE,WAAO,KAAK,sBAAsBC,GAAYnB,GAAeC,GAAWC,GAAiBG,CAAM;AAAA,EACjG;AAAA;AAAA;AAAA;AAAA,EAKQ,0BACNW,GACAC,GACAf,GACAC,GACAC,GACe;AACf,UAAMgB,IAAiB,KAAK,OAAO,MAAM,YAAY,CAAA,GAC/CC,IAAeD,EAAe,gBAAgB;AAGpD,QAAIE;AACJ,UAAMC,IAAeH,EAAe,UAAU;AAE9C,QAAI,OAAOG,KAAiB,YAAYA,EAAa,SAAS,GAAG,GAAG;AAElE,YAAMC,IAAa,WAAWD,CAAY,IAAI;AAI9C,MAAAD,IAHiB,KAAK;AAAA,QACpBpB,EAAgB,SAAS,IAAIA,EAAgB,UAAU;AAAA,MAAA,IAErCsB,IAAa;AAAA,IACnC;AACE,MAAAF,IAAS,OAAOC,KAAiB,WAAWA,IAAe;AAI7D,QAAIE;AACJ,IAAIJ,MAAiB,SACnBI,IAAStB,IAAaC,IAAe,IAAI,KAAK,KAE9CqB,IAAQ,KAAK,OAAA,IAAW,IAAI,KAAK;AAInC,UAAMnB,IAAUJ,EAAgB,QAAQ,GAClCK,IAAUL,EAAgB,SAAS,GAGnCW,IAASP,IAAU,KAAK,IAAImB,CAAK,IAAIH,GACrCR,IAASP,IAAU,KAAK,IAAIkB,CAAK,IAAIH;AAE3C,WAAO,EAAE,GAAGT,GAAQ,GAAGC,EAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmBY,GAAsC;AACvD,UAAM7I,IAAW,KAAK,OAAO,OAAO,UAC9BC,IAAS,KAAK,OAAO;AAE3B,WAAO;AAAA,MACL,gBAAgB;AAAA;AAAA,MAChB,UAAAD;AAAA,MACA,OAAO;AAAA,MACP,QAAAC;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,oBACEqF,GACA6B,GACA5C,GACAI,GACAc,GACAC,GACAG,GACAE,GACQ;AAER,UAAMa,IAAatB,EAAc,IAAI6B,EAAc,GAC7CN,IAAavB,EAAc,IAAI6B,EAAc,GAG7CnG,IAAW6E,MAAkB,SAAYA,IAAgBtB,GAGzDxD,IAAQgF,MAAe,SAAYA,IAAapB,GAGhD4B,IAAgBd,MAAe,SAAY,CAACA,IAAa,IAAI,GAC7De,IAAgBd,MAAgB,SAAY,CAACA,IAAc,IAAI,GAC/DoD,IAAkBrD,MAAe,SACnC,aAAac,CAAa,OAAOC,CAAa,QAC9C;AAEJ,WAAIlB,EAAc,WAET,GAAGwD,CAAe,cAAclC,CAAU,OAAOC,CAAU,cAAc7F,CAAQ,kBAInF,GAAG8H,CAAe,cAAclC,CAAU,OAAOC,CAAU,cAAc7F,CAAQ,cAAcD,CAAK;AAAA,EAC7G;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,oBAAoBC,GAAkBD,GAAe0E,GAAqBC,GAA8B;AAEtG,QAAID,MAAe,UAAaC,MAAgB,QAAW;AACzD,YAAMqD,IAAU,CAACtD,IAAa,GACxBuD,IAAU,CAACtD,IAAc;AAC/B,aAAO,aAAaqD,CAAO,OAAOC,CAAO,cAAchI,CAAQ,cAAcD,CAAK;AAAA,IACpF;AACA,WAAO,gCAAgCC,CAAQ,cAAcD,CAAK;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAA2B;AACzB,UAAMf,IAAW,KAAK,OAAO,OAAO,UAC9BC,IAAS,KAAK,OAAO;AAG3B,WAAI,KAAK,wBACA,WAAWD,CAAQ,gBAGrB,WAAWA,CAAQ,0BAA0BA,CAAQ,MAAMC,CAAM;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA,EAKA,sBAA+B;AAC7B,WAAO8G,GAAoB,KAAK,WAAW,IAAI;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAiC;AAC/B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,cAA6B;AAC3B,WAAO,KAAK,WAAW;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,YAAkC;AAChC,WAAO;AAAA,MACL,UAAU,KAAK,OAAO,OAAO;AAAA,IAAA;AAAA,EAEjC;AAAA;AAAA;AAAA;AAAA,EAKA,oBAAyC;AACvC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAqC;AACnC,WAAO,KAAK,eAAe;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,uBAAuBxC,GAA+B;AAGpD,YAFa,KAAK,eAAe,MAEzB;AAAA,MACN,KAAK;AAEH,eAAOA;AAAA,MAET,KAAK,UAAU;AAEb,cAAM0E,IAAc,KAAK,eAAe;AACxC,YAAIA,MAAgB;AAElB,iBAAO1E,KAAiB,KAAK,OAAA,IAAW,OAAO;AAEjD,YAAI,OAAO0E,KAAgB;AACzB,iBAAOA;AAGT,cAAMC,IAAQD,EAAY,MAAMA,EAAY;AAC5C,eAAOA,EAAY,MAAM,KAAK,OAAA,IAAWC;AAAA,MAC3C;AAAA,MAEA,KAAK,QAAQ;AAEX,cAAMC,IAAY,KAAK,eAAe,aAAa,GAC7CC,IAAY,KAAK,qBAAqB7E,CAAa;AACzD,eAAOA,IAAiB4E,IAAY,MAAMC;AAAA,MAC5C;AAAA,MAEA,KAAK;AAEH,eAAO7E,KAAiB,KAAK,OAAA,IAAW,OAAO;AAAA,MAEjD,KAAK;AAEH,eAAOA;AAAA,MAET;AACE,eAAOA;AAAA,IAAA;AAAA,EAEb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,qBAAqBA,GAA+B;AAG1D,YAFkB,KAAK,eAAe,aAAa,QAE3C;AAAA,MACN,KAAK;AACH,eAAO;AAAA;AAAA,MACT,KAAK;AACH,eAAO;AAAA;AAAA,MACT,KAAK;AACH,eAAO,KAAK,OAAA,IAAW,MAAM,IAAI;AAAA,MAEnC;AAGE,eAAOA,KAAiB,IAAI,IAAI;AAAA,IAAA;AAAA,EAEtC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,qBAA8B;AAC5B,WAAO,KAAK,eAAe,SAAS;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,wBAAwBtC,GAAkBsC,GAA+B;AACvE,QAAI,KAAK,eAAe,SAAS;AAC/B,aAAOA;AAGT,UAAMC,IAAe,KAAK,eAAe,UAAU;AAAA,MACjD,WAAW;AAAA,MACX,WAAW;AAAA,MACX,OAAO;AAAA,IAAA,GAGH,EAAE,WAAAf,GAAW,WAAAC,GAAW,OAAAC,EAAA,IAAUa,GAGlCjB,IAAc,KAAK,IAAItB,IAAWyB,IAAY,KAAK,KAAK,CAAC,GAGzDQ,IAAcP,IAAQ,KAAK,IAAI,IAAI1B,GAAU,CAAC,IAAI,GAGlDwC,IAAehB,IAAYF,IAAcW;AAE/C,WAAOK,IAAgBE;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAmC;AACjC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,eAA+B;AAC7B,WAAO,KAAK,YAAY;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,oBAAoBE,GAA4B;AAG9C,YAFa,KAAK,YAAY,MAEtB;AAAA,MACN,KAAK;AAEH,eAAOA;AAAA,MAET,KAAK;AAGH,gBADmB,KAAK,YAAY,cAAc,OAC9BA;AAAA,MAGtB,KAAK;AAGH,gBADmB,KAAK,YAAY,cAAc,OAC9BA;AAAA,MAGtB,KAAK;AAGH,eAAOA;AAAA,MAET,KAAK,UAAU;AAEb,cAAMuE,IAAQ,KAAK,YAAY,SAAS,EAAE,KAAK,KAAK,KAAK,EAAA;AAEzD,gBADqBA,EAAM,MAAM,KAAK,YAAYA,EAAM,MAAMA,EAAM,QAC9CvE;AAAA,MACxB;AAAA,MAEA;AACE,eAAOA;AAAA,IAAA;AAAA,EAEb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,kBAA2B;AACzB,WAAO,KAAK,YAAY,SAAS;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,kBAAkB1C,GAAkB0C,GAA4B;AAC9D,QAAI,KAAK,YAAY,SAAS;AAC5B,aAAOA;AAGT,UAAMC,IAAY,KAAK,YAAY,OAAO;AAAA,MACxC,WAAW;AAAA,MACX,SAAS;AAAA,IAAA,GAGL,EAAE,WAAAlD,GAAW,SAAAC,EAAA,IAAYiD,GAIzB7C,IAAY,KAAK,6BAA6BJ,GAASD,CAAS;AAGtE,QAAIqD,IAAeJ;AACnB,aAASrC,IAAI,GAAGA,IAAIP,EAAU,QAAQO;AACpC,UAAIL,KAAYF,EAAUO,CAAC,EAAE,MAAM;AACjC,cAAM0C,IAAW1C,MAAM,IAAI,IAAIP,EAAUO,IAAI,CAAC,EAAE,MAC1C2C,IAAY3C,MAAM,IAAIqC,IAAa5C,EAAUO,IAAI,CAAC,EAAE,OACpD4C,KAAmBjD,IAAW+C,MAAajD,EAAUO,CAAC,EAAE,OAAO0C,IAE/DG,IAAgB,KAAK,YAAYD,CAAe;AACtD,QAAAH,IAAeE,KAAalD,EAAUO,CAAC,EAAE,QAAQ2C,KAAaE;AAC9D;AAAA,MACF;AAGF,WAAOJ,IAAeJ;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKQ,6BACNhD,GACAD,GACwC;AACxC,UAAMK,IAAoD,CAAA;AAG1D,IAAAA,EAAU,KAAK,EAAE,MAAM,KAAK,OAAOL,GAAW;AAG9C,QAAIkB,IAAmBlB;AACvB,UAAMmD,IAAc,KAEdhC,IADgB,OACclB,IAAU;AAE9C,QAAIgB,IAAc;AAClB,aAASL,IAAI,GAAGA,IAAIX,GAASW,KAAK;AAEhC,YAAMwC,IAAa,KAAKlC,IAAmB,KAAKiC;AAChD,MAAAlC,KAAeE,GACfd,EAAU,KAAK,EAAE,MAAMY,GAAa,OAAOmC,GAAY,GAGvDlC,IAAmB,KAAKA,IAAmB,KAAKiC,IAAcA,GAC9DlC,KAAeE,GACXP,IAAIX,IAAU,KAChBI,EAAU,KAAK,EAAE,MAAMY,GAAa,OAAOC,GAAkB;AAAA,IAEjE;AAGA,WAAAb,EAAU,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,GAE7BA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,GAAmB;AACrC,WAAO,KAAK,IAAI,MAAM,IAAI;AAAA,EAC5B;AACF;AC5nBO,MAAMsH,GAAoB;AAAA,EAU/B,YAAY7J,GAA6B8J,IAAkB,KAAK;AARhE,SAAQ,8BAA2C,IAAA,GAKnD,KAAQ,gBAA+B,MACvC,KAAQ,gBAAwB,GAG9B,KAAK,SAAS9J,GACd,KAAK,kBAAkB8J;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAASzJ,GAAsB0J,GAAehC,GAAqBiC,GAA8B;AAC/F,QAAI,KAAK,QAAQ,IAAI3J,CAAO,EAAG;AAE/B,UAAM4J,IAAQD,KAAiB,KAAK,iBAC9BE,IAAa,KAAK,OAAO,cAAcD,GAEvCE,IAAmB;AAAA,MACvB,SAAA9J;AAAA,MACA,OAAA0J;AAAA,MACA,aAAAhC;AAAA,MACA,WAAW;AAAA,MACX,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,MAChB,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,YAAY;AAAA,IAAA;AAGd,SAAK,QAAQ,IAAI1H,GAAS8J,CAAK,GAE/BA,EAAM,aAAa,WAAW,MAAM;AAClC,MAAAA,EAAM,aAAa,MACf,CAACA,EAAM,WAAW,CAACA,EAAM,UAC3B,KAAK,gBAAgBA,CAAK;AAAA,IAE9B,GAAGD,CAAU;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc7J,GAA4B;AACxC,UAAM8J,IAAQ,KAAK,QAAQ,IAAI9J,CAAO;AACtC,IAAK8J,MACLA,EAAM,SAAS,IAEXA,EAAM,eAAe,SACvB,aAAaA,EAAM,UAAU,GAC7BA,EAAM,aAAa,OAErB,KAAK,YAAYA,CAAK;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,eAAe9J,GAA4B;AACzC,UAAM8J,IAAQ,KAAK,QAAQ,IAAI9J,CAAO;AACtC,IAAI,CAAC8J,KAASA,EAAM,YACpBA,EAAM,SAAS,IACf,KAAK,gBAAgBA,CAAK;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa9J,GAA4B;AACvC,UAAM8J,IAAQ,KAAK,QAAQ,IAAI9J,CAAO;AACtC,IAAK8J,MACLA,EAAM,UAAU,IACZA,EAAM,eAAe,SACvB,aAAaA,EAAM,UAAU,GAC7BA,EAAM,aAAa,OAErB,KAAK,aAAaA,CAAK,GACvB,KAAK,QAAQ,OAAO9J,CAAO;AAAA,EAC7B;AAAA,EAEA,WAAiB;AACf,eAAW8J,KAAS,KAAK,QAAQ,OAAA;AAC/B,MAAAA,EAAM,SAAS,IACXA,EAAM,eAAe,SACvB,aAAaA,EAAM,UAAU,GAC7BA,EAAM,aAAa,OAErB,KAAK,YAAYA,CAAK;AAAA,EAE1B;AAAA,EAEA,YAAkB;AAChB,eAAWA,KAAS,KAAK,QAAQ,OAAA;AAC/B,MAAKA,EAAM,YACTA,EAAM,SAAS,IACf,KAAK,gBAAgBA,CAAK;AAAA,EAGhC;AAAA,EAEA,UAAgB;AACd,eAAWA,KAAS,KAAK,QAAQ,OAAA;AAC/B,MAAAA,EAAM,UAAU,IACZA,EAAM,eAAe,SACvB,aAAaA,EAAM,UAAU,GAC7BA,EAAM,aAAa,OAErB,KAAK,aAAaA,CAAK;AAEzB,SAAK,QAAQ,MAAA,GACb,KAAK,kBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAMQ,gBAAgBA,GAAwB;AAC9C,UAAM,EAAE,MAAAC,MAAS,KAAK;AACtB,YAAQA,GAAA;AAAA,MACN,KAAK;AAAW,aAAK,aAAaD,CAAK;AAAG;AAAA,MAC1C,KAAK;AAAW,aAAK,YAAYA,CAAK;AAAI;AAAA,MAC1C,KAAK;AAAW,aAAK,YAAYA,CAAK;AAAI;AAAA,MAC1C,KAAK;AAAW,aAAK,WAAWA,CAAK;AAAK;AAAA,MAC1C,KAAK;AAAW,aAAK,aAAaA,CAAK;AAAG;AAAA,IACjC;AAAA,EAEb;AAAA,EAEQ,aAAaA,GAAwB;AAC3C,UAAME,IAAwB,EAAE,GAAG5M,IAAqB,GAAG,KAAK,OAAO,OAAA,GAEjE8E,IAAwB;AAAA,MAC5B,EAAE,WAAW,gBAAiC,QAAQ,EAAA;AAAA,MACtD,EAAE,WAAW,UAAU8H,EAAI,QAAQ,QAAW,QAAQ,KAAA;AAAA,MACtD,EAAE,WAAW,gBAAiC,QAAQ,IAAA;AAAA,MACtD,EAAE,WAAW,UAAU,CAACA,EAAI,QAAQ,QAAU,QAAQ,KAAA;AAAA,MACtD,EAAE,WAAW,gBAAiC,QAAQ,EAAA;AAAA,IAAK;AAG7D,IAAIA,EAAI,SAAS,cACfF,EAAM,YAAYA,EAAM,QAAQ,QAAQ5H,GAAW;AAAA,MACjD,UAAU8H,EAAI;AAAA,MAAO,YAAY;AAAA,MAAU,WAAW;AAAA,MAAO,MAAM;AAAA,IAAA,CACpE,GACDF,EAAM,UAAU,MAAA,GAChB,KAAK,mBAAmBE,EAAI,KAAK,KAEjCF,EAAM,YAAYA,EAAM,QAAQ,QAAQ5H,GAAW;AAAA,MACjD,UAAU8H,EAAI;AAAA,MACd,OAAO,EAAE,KAAK,OAAA,IAAWA,EAAI;AAAA,MAC7B,YAAY;AAAA,MACZ,WAAW;AAAA,IAAA,CACZ;AAAA,EAEL;AAAA,EAEQ,YAAYF,GAAwB;AAC1C,UAAME,IAAuB,EAAE,GAAG3M,IAAoB,GAAG,KAAK,OAAO,MAAA,GAE/D6E,IAAwB;AAAA,MAC5B,EAAE,WAAW,YAA0B,QAAQ,EAAA;AAAA,MAC/C,EAAE,WAAW,SAAS8H,EAAI,QAAQ,KAAK,QAAQ,KAAA;AAAA,MAC/C,EAAE,WAAW,YAA0B,QAAQ,IAAA;AAAA,MAC/C,EAAE,WAAW,SAASA,EAAI,QAAQ,KAAK,QAAQ,KAAA;AAAA,MAC/C,EAAE,WAAW,YAA0B,QAAQ,EAAA;AAAA,IAAK;AAGtD,IAAIA,EAAI,SAAS,cACfF,EAAM,YAAYA,EAAM,QAAQ,QAAQ5H,GAAW;AAAA,MACjD,UAAU8H,EAAI;AAAA,MAAO,YAAY;AAAA,MAAU,WAAW;AAAA,MAAO,MAAM;AAAA,IAAA,CACpE,GACDF,EAAM,UAAU,MAAA,GAChB,KAAK,mBAAmBE,EAAI,KAAK,KAEjCF,EAAM,YAAYA,EAAM,QAAQ,QAAQ5H,GAAW;AAAA,MACjD,UAAU8H,EAAI;AAAA,MACd,OAAO,EAAE,KAAK,OAAA,IAAWA,EAAI;AAAA,MAC7B,YAAY;AAAA,MACZ,WAAW;AAAA,IAAA,CACZ;AAAA,EAEL;AAAA,EAEQ,YAAYF,GAAwB;AAC1C,UAAME,IAAuB,EAAE,GAAG1M,IAAoB,GAAG,KAAK,OAAO,MAAA,GAC/DsM,IAAQ,EAAE,KAAK,OAAA,IAAWI,EAAI,QAI9BC,IAAY,WAAW,iBAAiBH,EAAM,OAAO,EAAE,OAAO,KAAK;AAEzE,QAAI5H,GACAsD;AAEJ,IAAIwE,EAAI,UAAU,UAChB9H,IAAY;AAAA,MACV,EAAE,SAAS+H,GAAW,QAAQ,EAAA;AAAA,MAC9B,EAAE,SAAS,GAAW,QAAQ,IAAA;AAAA,MAC9B,EAAE,SAASA,GAAW,QAAQ,EAAA;AAAA,IAAI,GAEpCzE,IAAU;AAAA,MACR,UAAUwE,EAAI;AAAA,MACd,OAAAJ;AAAA,MACA,YAAY;AAAA,MACZ,QAAQ;AAAA,IAAA,MAIV1H,IAAY;AAAA,MACV,EAAE,SAAS+H,GAAW,QAAQ,EAAA;AAAA,MAC9B,EAAE,SAASA,GAAW,QAAQD,EAAI,QAAA;AAAA,MAClC,EAAE,SAAS,GAAW,QAAQ,KAAK,IAAIA,EAAI,UAAU,MAAM,IAAI,EAAA;AAAA,MAC/D,EAAE,SAAS,GAAW,QAAQ,KAAA;AAAA,MAC9B,EAAE,SAASC,GAAW,QAAQ,EAAA;AAAA,IAAe,GAE/CzE,IAAU;AAAA,MACR,UAAUwE,EAAI;AAAA,MACd,OAAAJ;AAAA,MACA,YAAY;AAAA,IAAA,IAIhBE,EAAM,iBAAiBA,EAAM,QAAQ,QAAQ5H,GAAWsD,CAAO;AAAA,EACjE;AAAA,EAEQ,WAAWsE,GAAwB;AACzC,UAAME,IAAsB,EAAE,GAAGzM,IAAmB,GAAG,KAAK,OAAO,KAAA,GAC7D2M,IAASF,EAAI,cAAc,cAAc,MAAM;AAErD,IAAAF,EAAM,YAAYA,EAAM,QAAQ;AAAA,MAC9B,CAAC,EAAE,WAAW,eAAA,GAAkB,EAAE,WAAW,UAAUI,CAAM,QAAQ;AAAA,MACrE;AAAA,QACE,UAAUF,EAAI;AAAA,QACd,YAAY;AAAA,QACZ,QAAQ;AAAA,QACR,WAAW;AAAA,MAAA;AAAA,IACb;AAAA,EAEJ;AAAA,EAEQ,aAAaF,GAAwB;AAC3C,UAAMK,IAAK,KAAK,OAAO;AACvB,QAAI,CAACA,EAAI;AAET,UAAMC,IAASD,EAAG,EAAE,SAASL,EAAM,SAAS,OAAOA,EAAM,OAAO,aAAaA,EAAM,YAAA,CAAa;AAEhG,IAAI,OAAOM,KAAW,aACpBN,EAAM,iBAAiBM,IACdA,KAAU,OAAQA,EAAqB,QAAS,eACzDN,EAAM,YAAYM;AAAA,EAEtB;AAAA,EAEQ,mBAAmBC,GAAqB;AAE9C,QADA,KAAK,gBAAgBA,GACjB,KAAK,kBAAkB,KAAM;AACjC,UAAMzD,IAAO,MAAM;AACjB,YAAMjF,IAAI,YAAY,IAAA,IAAQ,KAAK;AACnC,iBAAWmI,KAAS,KAAK,QAAQ,OAAA;AAC/B,QAAI,CAACA,EAAM,WAAW,CAACA,EAAM,UAAUA,EAAM,cAC3CA,EAAM,UAAU,cAAcnI;AAGlC,WAAK,gBAAgB,sBAAsBiF,CAAI;AAAA,IACjD;AACA,SAAK,gBAAgB,sBAAsBA,CAAI;AAAA,EACjD;AAAA,EAEQ,oBAA0B;AAChC,IAAI,KAAK,kBAAkB,SACzB,qBAAqB,KAAK,aAAa,GACvC,KAAK,gBAAgB;AAAA,EAEzB;AAAA,EAEQ,YAAYkD,GAAwB;AAG1C,IAAIA,EAAM,cACRA,EAAM,UAAU,OAAA,GAChBA,EAAM,YAAY,OAEhBA,EAAM,mBACRA,EAAM,eAAe,OAAA,GACrBA,EAAM,iBAAiB;AAAA,EAE3B;AAAA,EAEQ,aAAaA,GAAwB;AAC3C,IAAIA,EAAM,cACRA,EAAM,UAAU,OAAA,GAChBA,EAAM,YAAY,OAEhBA,EAAM,mBACRA,EAAM,eAAe,OAAA,GACrBA,EAAM,iBAAiB,OAErBA,EAAM,mBACRA,EAAM,eAAA,GACNA,EAAM,iBAAiB;AAAA,EAE3B;AACF;AC/UO,MAAMQ,GAAiD;AAAA,EAI5D,YAAY3K,GAAsB4K,IAA2B,IAAI;AAC/D,SAAK,SAAS5K,GACd,KAAK,cAAc4K;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,SAASC,GAAoBhD,GAAkChC,IAA+B,CAAA,GAAmB;AAC/G,UAAMiF,IAAyB,CAAA,GACzB,EAAE,OAAAC,GAAO,QAAAC,EAAA,IAAWnD,GAEpBoD,IAAU,KAAK,OAAO,QAAQ,SAE9BC,IAAgBrF,EAAQ,eAAe,KAGvCsF,IAAe,KAAK,YAAY,UAAU,QAAQ,QAClDC,IAAc,KAAK,YAAY,UAAU,OAAO,OAAO,KACvDC,IAAc,KAAK,YAAY,UAAU,OAAO,OAAO,IAGvDC,IAAc,KAAK,YAAY,QAAQ,UAAU,OAAO,GACxDC,IAAc,KAAK,YAAY,QAAQ,UAAU,OAAO,GACxDC,IAAcF,MAAgB,KAAOC,MAAgB,GAKrDE,IAAaP,IADI,MAC8B,GAC/CQ,IAAaR,IAAgB,GAE7BS,IAAOZ,IAAQE,IAAUQ,GACzBG,IAAOZ,IAASC,IAAUS,GAC1BG,IAAOZ,IAAUQ,GACjBK,IAAOb,IAAUS;AAEvB,aAAS5I,IAAI,GAAGA,IAAI+H,GAAY/H,KAAK;AAEnC,YAAM3C,IAAI,KAAK,OAAO0L,GAAMF,CAAI,GAC1BvL,IAAI,KAAK,OAAO0L,GAAMF,CAAI,GAG1BpK,IAAW2J,MAAiB,WAAW,KAAK,OAAOC,GAAaC,CAAW,IAAI,GAG/E9J,IAAQiK,IAAc,KAAK,OAAOF,GAAaC,CAAW,IAAI,GAC9DQ,IAAkBb,IAAgB3J,GAElCyK,IAAsB;AAAA,QAC1B,IAAIlJ;AAAA,QACJ,GAAA3C;AAAA,QACA,GAAAC;AAAA,QACA,UAAAoB;AAAA,QACA,OAAAD;AAAA,QACA,UAAUwK;AAAA,MAAA;AAGZ,MAAAjB,EAAQ,KAAKkB,CAAM;AAAA,IACrB;AAEA,WAAOlB;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,OAAOmB,GAAaC,GAAqB;AAC/C,WAAO,KAAK,OAAA,KAAYA,IAAMD,KAAOA;AAAA,EACvC;AACF;AChFO,MAAME,GAAiD;AAAA,EAI5D,YAAYnM,GAAsB4K,IAA2B,IAAI;AAC/D,SAAK,SAAS5K,GACd,KAAK,cAAc4K;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,SACEC,GACAhD,GACAhC,IAA+B,CAAA,GAChB;AACf,UAAMiF,IAAyB,CAAA,GACzB,EAAE,OAAAC,GAAO,QAAAC,EAAA,IAAWnD,GAEpBqD,IAAgBrF,EAAQ,eAAe,KAGvCsF,IAAe,KAAK,YAAY,UAAU,QAAQ,QAClDC,IAAc,KAAK,YAAY,UAAU,OAAO,OAAO,KACvDC,IAAc,KAAK,YAAY,UAAU,OAAO,OAAO,IAGvDC,IAAc,KAAK,YAAY,QAAQ,UAAU,OAAO,GACxDC,IAAc,KAAK,YAAY,QAAQ,UAAU,OAAO,GACxDC,IAAcF,MAAgB,KAAOC,MAAgB,GAGrDa,IAAa,KAAK,OAAO,cAAc,GAEvCC,IAAsC;AAAA,MAC1C,GAAGrP;AAAA,MACH,GAAG,KAAK,OAAO;AAAA,IAAA,GAIX4K,IAAY/B,EAAQ,eAAeqF,GACnCoB,IAAKvB,IAAQ,GACbwB,IAAKvB,IAAS,GAGdwB,IAAoB,KAAK,KAAK,KAAK,KAAK3B,CAAU,CAAC,GAEnDI,IAAU,KAAK,OAAO,QAAQ,WAAW,IACzCwB,IAAY,KAAK,IAAI7E,IAAY,KAAK,KAAK;AAAA,MAC/C0E,IAAKrB,IAAUrD,IAAY;AAAA,MAC3B2E,IAAKtB,IAAUrD,IAAY;AAAA,IAAA,CAC5B;AAGD,QAAIiD,IAAa,GAAG;AAElB,YAAM6B,IAAgBlB,IAAc,KAAK,OAAOF,GAAaC,CAAW,IAAI,GACtEoB,IAAa/E,IAAY8E;AAE/B,MAAA5B,EAAQ,KAAK;AAAA,QACX,IAAI;AAAA,QACJ,GAAGwB;AAAA,QACHC;AAAA,QACA,UAAUpB,MAAiB,WAAW,KAAK,OAAOC,IAAc,MAAMC,IAAc,IAAI,IAAI;AAAA;AAAA,QAC5F,OAAOqB;AAAA,QACP,UAAUC;AAAA,QACV,QAAQ;AAAA;AAAA,MAAA,CACT;AAAA,IACH;AAEA,QAAIC,IAAiB,GACjBC,IAAc;AAElB,WAAOD,IAAiB/B,KAAY;AAElC,YAAMiC,IAAiBD,IAAcL,GAC/BO,IAAYX,IAAa,IAC3B,IAAKU,IAAiBV,IAAa,MACnC,GAIEY,IAAW,KAAK,IAAIpF,IAAY,KAAM6E,IAAYD,IAAqB,MAAMH,EAAa,SAAS,GACnGY,IAAUJ,IAAcG,GACxBE,IAAUD,IAAU,KAEpBE,IAAgB,KAAK,MAAM,KAAKD,IAAUD,KAAW,KAAK,MAAM,IAAIC,IAAUD,MAAYC,IAAU,IAAID,EAAQ,IAEhHG,IAAqB,KAAK,cAAcxF,CAAS,GAEjDyF,IAAc,KAAK,MAAMF,KAAiBC,IAAqB,IAAI;AAEzE,UAAIC,MAAgB,GAAG;AACrB,QAAAR;AACA;AAAA,MACF;AAEA,YAAMS,IAAa,IAAI,KAAK,KAAMD,GAG5BE,IAAaV,KAAe,KAAK,KAAK,KAAK;AAEjD,eAAS/J,IAAI,GAAGA,IAAIuK,KAAeT,IAAiB/B,GAAY/H,KAAK;AACnE,cAAMsG,IAAStG,IAAIwK,IAAaC,GAG1Bb,IAAgBlB,IAAc,KAAK,OAAOF,GAAaC,CAAW,IAAI,GACtEiC,IAAgBT,IAAYL,GAC5BX,IAAkBnE,IAAY4F;AAGpC,YAAIrN,IAAImM,IAAK,KAAK,IAAIlD,CAAK,IAAI8D,GAC3B9M,IAAImM,IAAK,KAAK,IAAInD,CAAK,IAAI6D;AAK/B,cAAMxB,IAAaM,IADI,MACgC,GACjDL,IAAaK,IAAkB;AAGrC,QAAI5L,IAAIsL,IAAYR,IAClB9K,IAAI8K,IAAUQ,IACLtL,IAAIsL,IAAYV,IAAQE,MACjC9K,IAAI4K,IAAQE,IAAUQ,IAIpBrL,IAAIsL,IAAaT,IACnB7K,IAAI6K,IAAUS,IACLtL,IAAIsL,IAAaV,IAASC,MACnC7K,IAAI4K,IAASC,IAAUS;AAGzB,cAAMlK,IAAW2J,MAAiB,WAAW,KAAK,OAAOC,GAAaC,CAAW,IAAI;AAErF,QAAAP,EAAQ,KAAK;AAAA,UACX,IAAI8B;AAAA,UACJ,GAAAzM;AAAA,UACA,GAAAC;AAAA,UACA,UAAAoB;AAAA,UACA,OAAOgM;AAAA,UACP,UAAUzB;AAAA,UACV,QAAQ,KAAK,IAAI,GAAG,MAAMc,CAAW;AAAA;AAAA,QAAA,CACtC,GAEDD;AAAA,MACF;AAEA,MAAAC;AAAA,IACF;AAEA,WAAO/B;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,cAAcE,GAAwB;AAG5C,WAAOA,IAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,OAAOiB,GAAaC,GAAqB;AAC/C,WAAO,KAAK,OAAA,KAAYA,IAAMD,KAAOA;AAAA,EACvC;AACF;ACtLA,MAAMwB,KAA2C;AAAA,EAC/C,SAAS;AAAA,EACT,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,eAAe;AAAA,EACf,WAAW;AAAA,EACX,KAAK;AAAA,EACL,gBAAgB;AAClB,GAKMC,KAA0B;AAAA,EAC9B,EAAE,GAAG,GAAG,GAAG,EAAA;AAAA;AAAA,EACX,EAAE,GAAG,IAAI,GAAG,GAAA;AAAA;AAAA,EACZ,EAAE,GAAG,GAAG,GAAG,GAAA;AAAA;AAAA,EACX,EAAE,GAAG,IAAI,GAAG,EAAA;AAAA;AAAA,EACZ,EAAE,GAAG,IAAI,GAAG,EAAA;AAAA;AAAA,EACZ,EAAE,GAAG,GAAG,GAAG,EAAA;AAAA;AAAA,EACX,EAAE,GAAG,GAAG,GAAG,GAAA;AAAA;AAAA,EACX,EAAE,GAAG,GAAG,GAAG,EAAA;AAAA;AACb;AAEO,MAAMC,GAA+C;AAAA,EAI1D,YAAY3N,GAAsB4K,IAA2B,IAAI;AAC/D,SAAK,SAAS5K,GACd,KAAK,cAAc4K;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,SACEC,GACAhD,GACAhC,IAA6B,CAAA,GACd;AACf,UAAMiF,IAAyB,CAAA,GACzB,EAAE,OAAAC,GAAO,QAAAC,EAAA,IAAWnD,GAEpB+F,IAAa,EAAE,GAAGH,IAAqB,GAAG,KAAK,OAAO,KAAA,GACtDxC,IAAU,KAAK,OAAO,QAAQ,SAE9BC,IAAgBrF,EAAQ,eAAe,KAGvCsF,IAAe,KAAK,YAAY,UAAU,QAAQ,QAGlDG,IAAc,KAAK,YAAY,QAAQ,UAAU,OAAO,GACxDC,IAAc,KAAK,YAAY,QAAQ,UAAU,OAAO,GACxDC,IAAcF,MAAgB,KAAOC,MAAgB,GAGrDsC,IAAiB9C,IAAS,IAAIE,GAC9B6C,IAAkB9C,IAAU,IAAIC,GAGhC,EAAE,SAAA8C,GAAS,MAAAC,EAAA,IAAS,KAAK;AAAA,MAC7BnD;AAAA,MACAgD;AAAA,MACAC;AAAA,MACA5C;AAAA,MACA0C;AAAA,IAAA,GAKIK,IAAgBL,EAAW,YAAY,OACvCM,IAAmBN,EAAW,YAAY,UAG1CO,IAAmBF,IAAgBF,IAAU,MAAMA,GACnDK,IAAgBF,IAAmBF,IAAO,MAAMA,GAGhDK,KAAaR,IAAkBD,EAAW,OAAOG,IAAU,MAAOI,GAClEG,KAAcR,IAAmBF,EAAW,OAAOI,IAAO,MAAOI,GAGjEG,IAAiBN,IAAgBI,IAAY,IAAI,GACjDG,IAAiBN,IAAmBI,IAAa,IAAI,GAIrDG,IAAoB,IAAIb,EAAW,SACnCc,IAAgB,KAAK,IAAIL,GAAWC,CAAU,IAAIG,GAIlD7G,IAAY/B,EAAQ,cACtB,KAAK,IAAIA,EAAQ,aAAa6I,CAAa,IAC3CA,GAGEC,IAAiBZ,IAAUM,KAAaN,IAAU,KAAKH,EAAW,MAAMW,GACxEK,IAAkBZ,IAAOM,KAAcN,IAAO,KAAKJ,EAAW,MAAMY,GAGpEK,IAAc5D,KAAW4C,IAAiBc,KAAkB,GAC5DG,IAAc7D,KAAW6C,IAAkBc,KAAmB,GAG9DG,IAAYhB,IAAUC,GACtBgB,IAAepB,EAAW,YAAY,UAAUA,EAAW,SAAS,QACpEqB,IAAiBD,KAAgBnE,IAAakE;AAEpD,IAAI,OAAO,SAAW,QACnB,OAAe,sBAAsB;AAAA,MACpC,mBAAmBnB,EAAW;AAAA,MAC9B,gBAAgBA,EAAW;AAAA,MAC3B,SAAAG;AAAA,MAAS,MAAAC;AAAA,MAAM,WAAAe;AAAA,MACf,cAAAC;AAAA,MAAc,YAAAnE;AAAA,MAAY,gBAAAoE;AAAA,IAAA;AAK9B,UAAMC,IAA2BD,IAAiB,IAAI,MAAMF,CAAS,EAAE,KAAK,CAAC,IAAI,CAAA,GAG3EI,IAAmB,KAAK,IAAId,GAAWC,CAAU,IAAIV,EAAW;AAEtE,aAAS9K,IAAI,GAAGA,IAAI+H,GAAY/H,KAAK;AACnC,UAAIsM,GACAC,GACAC,IAAa;AAEjB,UAAIL,KAAkBnM,KAAKiM,GAAW;AAEpC,cAAMQ,IAAgBzM,IAAIiM,GACpBS,IAAaD,IAAgBR;AACnC,QAAAO,IAAa,KAAK,MAAMC,IAAgBR,CAAS,IAAI,GACrDG,EAAeM,CAAU,KAGrB5B,EAAW,kBAAkB,SAC/BwB,IAAMI,IAAazB,GACnBsB,IAAM,KAAK,MAAMG,IAAazB,CAAO,MAErCsB,IAAMG,IAAaxB,GACnBoB,IAAM,KAAK,MAAMI,IAAaxB,CAAI;AAAA,MAEtC;AAEE,QAAIJ,EAAW,kBAAkB,SAC/BwB,IAAMtM,IAAIiL,GACVsB,IAAM,KAAK,MAAMvM,IAAIiL,CAAO,MAE5BsB,IAAMvM,IAAIkL,GACVoB,IAAM,KAAK,MAAMtM,IAAIkL,CAAI;AAK7B,UAAIyB,IAAcZ,IAAcO,KAAOf,IAAYT,EAAW,OAAOS,IAAY,GAC7EqB,IAAcZ,IAAcO,KAAOf,IAAaV,EAAW,OAAOU,IAAa;AAUnF,UAPIV,EAAW,YAAY,SAASyB,IAAM,MAAM,IAC9CI,KAAepB,IAAY,IAClBT,EAAW,YAAY,YAAYwB,IAAM,MAAM,MACxDM,KAAepB,IAAa,IAI1BgB,IAAa,GAAG;AAClB,cAAMK,KAAgBL,IAAa,KAAK5B,GAAwB,QAC1DkC,IAAUlC,GAAwBiC,CAAY;AACpD,QAAAF,KAAeG,EAAQ,IAAIT,GAC3BO,KAAeE,EAAQ,IAAIT;AAAA,MAC7B;AAGA,UAAIvB,EAAW,SAAS,GAAG;AACzB,cAAMiC,IAAcxB,IAAY,IAAKT,EAAW,QAC1CkC,IAAcxB,IAAa,IAAKV,EAAW;AACjD,QAAA6B,KAAe,KAAK,OAAO,CAACI,GAAYA,CAAU,GAClDH,KAAe,KAAK,OAAO,CAACI,GAAYA,CAAU;AAAA,MACpD;AAGA,UAAI3P,IAAIsP,GACJrP,IAAIsP;AAGR,UAAI,CAACT,KAAkBrB,EAAW,kBAAkB,OAAO;AACzD,cAAMmC,IAAiBlF,IAAakD,KAAWA;AAG/C,YAFkBsB,MAAQ,KAAK,OAAOxE,IAAa,KAAKkD,CAAO,KAE9CgC,IAAiBhC,GAAS;AACzC,gBAAMiC,KAAeD,IAAiB1B,KAAa0B,IAAiB,KAAKnC,EAAW;AACpF,cAAIqC,KAAkB;AAEtB,UAAIrC,EAAW,cAAc,WAC3BqC,MAAmBtB,IAAiBqB,MAAgB,IAC3CpC,EAAW,cAAc,UAClCqC,KAAkBtB,IAAiBqB,KAGrC7P,KAAK8P;AAAA,QACP;AAAA,MACF;AAGA,YAAMvD,KAAgBlB,IAAc,KAAK,OAAOF,GAAaC,CAAW,IAAI,GACtEQ,IAAkBnE,IAAY8E,IAK9BjB,KAAaM,IADI,MACgC,GACjDL,KAAaK,IAAkB,GAC/BF,KAAOZ,IAAUQ,IACjBE,KAAOZ,IAAQE,IAAUQ,IACzBK,KAAOb,IAAUS,IACjBE,KAAOZ,IAASC,IAAUS;AAEhC,MAAAvL,IAAI,KAAK,IAAI0L,IAAM,KAAK,IAAI1L,GAAGwL,EAAI,CAAC,GACpCvL,IAAI,KAAK,IAAI0L,IAAM,KAAK,IAAI1L,GAAGwL,EAAI,CAAC;AAIpC,UAAIpK,KAAW;AACf,UAAI2J,MAAiB,UAAU;AAC7B,cAAMC,IAAc,KAAK,YAAY,UAAU,OAAO,OAAO,KACvDC,IAAc,KAAK,YAAY,UAAU,OAAO,OAAO;AAC7D,QAAIuC,EAAW,SAAS,IAEtBpM,KAAW,KAAK,OAAO4J,IAAcwC,EAAW,QAAQvC,IAAcuC,EAAW,MAAM,IAGvFpM,KAAW,KAAK,OAAO4J,GAAaC,CAAW;AAAA,MAEnD;AAKA,UAAI6E;AACJ,MAAIjB,KAAkBK,IAAa,IAGjCY,KAAS,KAAKZ,IAGdY,KAASjB,IAAiB,MAAMnM,IAAIA,IAAI,GAG1CgI,EAAQ,KAAK;AAAA,QACX,IAAIhI;AAAA,QACJ,GAAA3C;AAAA,QACA,GAAAC;AAAA,QACA,UAAAoB;AAAA,QACA,OAAOkL;AAAA,QACP,UAAUX;AAAA,QACV,QAAAmE;AAAA,MAAA,CACD;AAAA,IACH;AAEA,WAAOpF;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,wBACND,GACAgD,GACAC,GACAqC,GACAnQ,GACmC;AACnC,QAAI+N,GACAC;AAEJ,QAAIhO,EAAO,YAAY,UAAUA,EAAO,SAAS;AAC/C,MAAA+N,IAAU/N,EAAO,SACjBgO,IAAOhO,EAAO;AAAA,aACLA,EAAO,YAAY;AAC5B,MAAA+N,IAAU/N,EAAO,SACjBgO,IAAO,KAAK,KAAKnD,IAAakD,CAAO;AAAA,aAC5B/N,EAAO,SAAS;AACzB,MAAAgO,IAAOhO,EAAO,MACd+N,IAAU,KAAK,KAAKlD,IAAamD,CAAI;AAAA,SAChC;AAEL,YAAMoC,IAAcvC,IAAiBC;AAQrC,WAJAC,IAAU,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,KAAKlD,IAAauF,IAH/B,GAG6D,CAAC,CAAC,GACxFpC,IAAO,KAAK,KAAKnD,IAAakD,CAAO,GAG9BA,IAAU,MAAMA,IAAU,KAAKC,KAAQnD;AAC5C,QAAAkD;AAAA,IAEJ;AAEA,WAAO,EAAE,SAAS,KAAK,IAAI,GAAGA,CAAO,GAAG,MAAM,KAAK,IAAI,GAAGC,CAAI,EAAA;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA,EAKQ,OAAO/B,GAAaC,GAAqB;AAC/C,WAAO,KAAK,OAAA,KAAYA,IAAMD,KAAOA;AAAA,EACvC;AACF;AC7TA,MAAMoE,KAAe,KAAK,MAAM,IAAI,KAAK,KAAK,CAAC,IAEzCC,KAA+C;AAAA,EACnD,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,YAAY;AACd;AAEO,MAAMC,GAAiD;AAAA,EAI5D,YAAYvQ,GAAsB4K,IAA2B,IAAI;AAC/D,SAAK,SAAS5K,GACd,KAAK,cAAc4K;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,SACEC,GACAhD,GACAhC,IAA+B,CAAA,GAChB;AACf,UAAMiF,IAAyB,CAAA,GACzB,EAAE,OAAAC,GAAO,QAAAC,EAAA,IAAWnD,GAEpB2I,IAAe,EAAE,GAAGF,IAAuB,GAAG,KAAK,OAAO,OAAA,GAC1DrF,IAAU,KAAK,OAAO,QAAQ,SAE9BC,IAAgBrF,EAAQ,eAAe,KAGvCsF,IAAe,KAAK,YAAY,UAAU,QAAQ,QAClDC,IAAc,KAAK,YAAY,UAAU,OAAO,OAAO,KACvDC,IAAc,KAAK,YAAY,UAAU,OAAO,OAAO,IAGvDC,IAAc,KAAK,YAAY,QAAQ,UAAU,OAAO,GACxDC,IAAc,KAAK,YAAY,QAAQ,UAAU,OAAO,GACxDC,IAAcF,MAAgB,KAAOC,MAAgB,GAGrDa,IAAa,KAAK,OAAO,cAAcoE,EAAa,YAGpDlE,IAAKvB,IAAQ,GACbwB,IAAKvB,IAAS,GAGdyB,IAAY,KAAK;AAAA,MACrBH,IAAKrB,IAAUC,IAAgB;AAAA,MAC/BqB,IAAKtB,IAAUC,IAAgB;AAAA,IAAA,GAI3BuF,IAAsBD,EAAa,cAAc,cAAc,KAAK;AAE1E,aAAS1N,IAAI,GAAGA,IAAI+H,GAAY/H,KAAK;AAEnC,UAAIsG,GACAH;AAEJ,UAAIuH,EAAa,eAAe;AAG9B,QAAApH,IAAQtG,IAAIuN,KAAeI,IAAsBD,EAAa,YAE9DvH,IAAS,KAAK,sBAAsBnG,GAAG+H,GAAY4B,GAAW+D,EAAa,SAAS;AAAA,eAC3EA,EAAa,eAAe,eAAe;AAEpD,cAAME,IAAQ5N,IAAI,MAAM0N,EAAa;AACrC,QAAApH,IAAQsH,IAAQD,IAAsBD,EAAa,YACnDvH,IAAS,KAAK,2BAA2ByH,GAAO7F,GAAY4B,GAAW+D,EAAa,SAAS;AAAA,MAC/F,OAAO;AAEL,cAAME,IAAQ5N,IAAI,MAAM0N,EAAa;AACrC,QAAApH,IAAQsH,IAAQD,IAAsBD,EAAa,YACnDvH,IAAS,KAAK,2BAA2ByH,GAAO7F,GAAY4B,GAAW+D,EAAa,SAAS;AAAA,MAC/F;AAGA,YAAMrQ,IAAImM,IAAK,KAAK,IAAIlD,CAAK,IAAIH,GAC3B7I,IAAImM,IAAK,KAAK,IAAInD,CAAK,IAAIH,GAG3B0H,IAAmB1H,IAASwD,GAC5BmE,IAAaxE,IAAa,IAC5B,IAAKuE,IAAmBvE,IAAa,MACrC,GAGEM,IAAgBlB,IAAc,KAAK,OAAOF,GAAaC,CAAW,IAAI,GACtEiC,IAAgBoD,IAAalE,GAG7BX,IAAkBb,IAAgBsC,GAKlC/B,IAAaM,IADI,MACgC,GACjDL,IAAaK,IAAkB,GAC/BF,IAAOZ,IAAUQ,GACjBE,IAAOZ,IAAQE,IAAUQ,GACzBK,IAAOb,IAAUS,GACjBE,IAAOZ,IAASC,IAAUS,GAE1BmF,IAAW,KAAK,IAAIhF,GAAM,KAAK,IAAI1L,GAAGwL,CAAI,CAAC,GAC3CmF,IAAW,KAAK,IAAIhF,GAAM,KAAK,IAAI1L,GAAGwL,CAAI,CAAC;AAGjD,UAAIpK,IAAW;AACf,UAAI2J,MAAiB,UAAU;AAC7B,cAAM4F,IAAgB3H,IAAQ,MAAM,KAAK,KAAM,KACzC4H,IAAmB,KAAK,OAAO5F,GAAaC,CAAW;AAC7D,QAAA7J,IAAWgP,EAAa,eAAe,WACnCQ,IACCD,IAAe,MAAMC,IAAmB;AAAA,MAC/C,MAAA,CAAW7F,MAAiB,cAE1B3J,IAAW,KAAK,uBAAuB4H,GAAOH,GAAQuH,CAAY;AAIpE,YAAMN,IAASrF,IAAa/H;AAE5B,MAAAgI,EAAQ,KAAK;AAAA,QACX,IAAIhI;AAAA,QACJ,GAAG+N;AAAA,QACH,GAAGC;AAAA,QACH,UAAAtP;AAAA,QACA,OAAOgM;AAAA,QACP,UAAUzB;AAAA,QACV,QAAAmE;AAAA,MAAA,CACD;AAAA,IACH;AAEA,WAAOpF;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,uBACN1B,GACAH,GACAuH,GACQ;AAIR,QAAIS;AAEJ,QAAIT,EAAa,eAAe;AAG9B,MAAAS,IAAe7H,IAAQ,KAAK,KAAK;AAAA,aACxBoH,EAAa,eAAe,eAAe;AAIpD,YAAMU,IAAI,IAAIV,EAAa,WACrBW,IAAM,KAAK,KAAKlI,IAASiI,CAAC;AAChC,MAAAD,IAAe7H,IAAQ+H;AAAA,IACzB,OAAO;AAGL,YAAMD,IAAI,OAAOV,EAAa,WACxBW,IAAM,KAAK,KAAK,IAAID,CAAC;AAC3B,MAAAD,IAAe7H,IAAQ+H;AAAA,IACzB;AAMA,WAHiBF,IAAe,MAAM,KAAK,KAAM,MAGhC;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,sBACNlH,GACAhC,GACA0E,GACA2E,GACQ;AAIR,UAAMnI,IADgBwD,IAAY,KAAK,KAAK1E,CAAW,IACxB,KAAK,KAAKgC,CAAK,IAAIqH;AAClD,WAAO,KAAK,IAAInI,GAAQwD,CAAS;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,2BACNiE,GACA3I,GACA0E,GACA2E,GACQ;AAER,UAAMC,IAAWtJ,IAAc,MAAMqJ;AAErC,WADwBV,IAAQW,IACP5E;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,2BACNiE,GACA3I,GACA0E,GACA2E,GACQ;AAER,UAAME,IAAI7E,IAAY,MAChByE,IAAI,OAAOE,GAEXnI,IAASqI,IAAI,KAAK,IAAIJ,IAAIR,CAAK,GAG/BW,IAAWtJ,IAAc,MAAMqJ,GAC/BG,IAAoBD,IAAI,KAAK,IAAIJ,IAAIG,CAAQ;AAEnD,WAAQpI,IAASsI,IAAqB9E;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKQ,OAAOR,GAAaC,GAAqB;AAC/C,WAAO,KAAK,OAAA,KAAYA,IAAMD,KAAOA;AAAA,EACvC;AACF;ACrPA,MAAMuF,KAAiD;AAAA,EACrD,cAAc;AAAA,EACd,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,SAAS;AAAA,EACT,cAAc;AAChB;AAEO,MAAMC,GAAkD;AAAA,EAI7D,YAAYzR,GAAsB4K,IAA2B,IAAI;AAC/D,SAAK,SAAS5K,GACd,KAAK,cAAc4K;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,SACEC,GACAhD,GACAhC,IAAgC,CAAA,GACjB;AACf,UAAMiF,IAAyB,CAAA,GACzB,EAAE,OAAAC,GAAO,QAAAC,EAAA,IAAWnD,GAEpB6J,IAAgB,EAAE,GAAGF,IAAwB,GAAG,KAAK,OAAO,QAAA,GAC5DvG,IAAU,KAAK,OAAO,QAAQ,SAE9BC,IAAgBrF,EAAQ,eAAe,KAGvCsF,IAAe,KAAK,YAAY,UAAU,QAAQ,QAClDC,IAAc,KAAK,YAAY,UAAU,OAAO,OAAO,KACvDC,IAAc,KAAK,YAAY,UAAU,OAAO,OAAO,IAGvDC,IAAc,KAAK,YAAY,QAAQ,UAAU,OAAO,GACxDC,IAAc,KAAK,YAAY,QAAQ,UAAU,OAAO,GACxDC,IAAcF,MAAgB,KAAOC,MAAgB,GAGrDoG,IAAe,KAAK;AAAA,MACxB9G;AAAA,MACA6G,EAAc;AAAA,MACd3G;AAAA,MACAC;AAAA,MACA0G,EAAc;AAAA,IAAA,GAIVE,IAAiB,KAAK;AAAA,MAC1BD;AAAA,MACA5G;AAAA,MACAC;AAAA,MACAC;AAAA,MACAyG;AAAA,IAAA,GAIIG,IAAmB,IAAI,MAAMF,CAAY,EAAE,KAAK,CAAC;AACvD,aAAS7O,IAAI,GAAGA,IAAI+H,GAAY/H;AAC9B,MAAA+O,EAAiB/O,IAAI6O,CAAY;AAGnC,QAAI7J,IAAa;AAGjB,aAASgK,IAAa,GAAGA,IAAaH,GAAcG,KAAc;AAChE,YAAMC,IAAUH,EAAeE,CAAU,GACnCE,IAAsBH,EAAiBC,CAAU;AAEvD,eAAShP,IAAI,GAAGA,IAAIkP,GAAqBlP,KAAK;AAE5C,YAAIyG,GACAC;AAEJ,YAAIkI,EAAc,iBAAiB;AAEjC,UAAAnI,IAAU,KAAK,eAAA,IAAmBwI,EAAQ,QAC1CvI,IAAU,KAAK,eAAA,IAAmBuI,EAAQ;AAAA,aACrC;AAEL,gBAAM3I,IAAQ,KAAK,OAAO,GAAG,KAAK,KAAK,CAAC,GAClC6I,IAAW,KAAK,OAAO,GAAGF,EAAQ,MAAM;AAC9C,UAAAxI,IAAU,KAAK,IAAIH,CAAK,IAAI6I,GAC5BzI,IAAU,KAAK,IAAIJ,CAAK,IAAI6I;AAAA,QAC9B;AAGA,cAAMxD,IAAoB,IAAIiD,EAAc,UAAU,KAChDQ,IAAiB,IAAIR,EAAc,UAAU;AAGnD,QAAAnI,KAAWkF,GACXjF,KAAWiF;AAGX,cAAM/B,IAAgBlB,IAAc,KAAK,OAAOF,GAAaC,CAAW,IAAI,GACtEiC,IAAgB0E,IAAiBxF,GAGjC9E,IAAYsD,IAAgBsC;AAGlC,YAAIrN,IAAI4R,EAAQ,IAAIxI,GAChBnJ,IAAI2R,EAAQ,IAAIvI;AAKpB,cAAMiC,IAAa7D,IADI,MAC0B,GAC3C8D,IAAa9D,IAAY;AAC/B,QAAAzH,IAAI,KAAK,IAAI8K,IAAUQ,GAAW,KAAK,IAAItL,GAAG4K,IAAQE,IAAUQ,CAAS,CAAC,GAC1ErL,IAAI,KAAK,IAAI6K,IAAUS,GAAY,KAAK,IAAItL,GAAG4K,IAASC,IAAUS,CAAU,CAAC;AAG7E,cAAMlK,IAAW2J,MAAiB,WAAW,KAAK,OAAOC,GAAaC,CAAW,IAAI,GAI/E8G,IADqB,KAAK,KAAK5I,IAAUA,IAAUC,IAAUA,CAAO,IAC1BuI,EAAQ,QAClD7B,IAAS,KAAK,OAAO,IAAIiC,KAAsB,EAAE,IAAI;AAE3D,QAAArH,EAAQ,KAAK;AAAA,UACX,IAAIhD;AAAA,UACJ,GAAA3H;AAAA,UACA,GAAAC;AAAA,UACA,UAAAoB;AAAA,UACA,OAAOgM;AAAA,UACP,UAAU5F;AAAA,UACV,QAAAsI;AAAA,QAAA,CACD,GAEDpI;AAAA,MACF;AAAA,IACF;AAEA,WAAOgD;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,sBACND,GACAuH,GACArH,GACAC,GACAqH,GACQ;AACR,QAAID,MAAgB;AAClB,aAAO,KAAK,IAAI,GAAG,KAAK,IAAIA,GAAavH,CAAU,CAAC;AAMtD,UAAMyH,IAAgB,KAAK,IAAI,GAAG,KAAK,KAAKzH,IADd,CACgD,CAAC,GAGzE0H,IAAe,KAAK;AAAA,MACvBxH,IAAQsH,KAAmBrH,IAASqH,KAAkB;AAAA,IAAA;AAGzD,WAAO,KAAK,IAAI,GAAG,KAAK,IAAIC,GAAeC,GAAc,EAAE,CAAC;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA,EAKQ,uBACNC,GACAzH,GACAC,GACAC,GACAjL,GACiB;AACjB,UAAMyS,IAA2B,CAAA,GAI3B5G,IAAOZ,IAAUjL,EAAO,eACxB2L,IAAOZ,IAAQE,IAAUjL,EAAO,eAChC8L,IAAOb,IAAUjL,EAAO,eACxB4L,IAAOZ,IAASC,IAAUjL,EAAO;AAEvC,aAAS8C,IAAI,GAAGA,IAAI0P,GAAO1P,KAAK;AAC9B,UAAI4P,IAAsC,MACtCC,IAAkB;AAGtB,eAASC,IAAU,GAAGA,IAAU,KAAaA,KAAW;AACtD,cAAMC,IAAY;AAAA,UAChB,GAAG,KAAK,OAAOhH,GAAMF,CAAI;AAAA,UACzB,GAAG,KAAK,OAAOG,GAAMF,CAAI;AAAA,UACzB,QAAQ,KAAK,uBAAuB5L,CAAM;AAAA,QAAA;AAI5C,YAAI8S,IAAc;AAClB,mBAAWC,KAAYN,GAAS;AAC9B,gBAAMpQ,IAAKwQ,EAAU,IAAIE,EAAS,GAC5BzQ,IAAKuQ,EAAU,IAAIE,EAAS,GAC5Bd,IAAW,KAAK,KAAK5P,IAAKA,IAAKC,IAAKA,CAAE;AAC5C,UAAAwQ,IAAc,KAAK,IAAIA,GAAab,CAAQ;AAAA,QAC9C;AASA,aANIQ,EAAQ,WAAW,KAAKK,IAAcH,OACxCD,IAAgBG,GAChBF,IAAkBG,IAIhBA,KAAe9S,EAAO;AACxB;AAAA,MAEJ;AAEA,MAAI0S,KACFD,EAAQ,KAAKC,CAAa;AAAA,IAE9B;AAEA,WAAOD;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,uBAAuBzS,GAAwC;AACrE,WAAIA,EAAO,YAAY,YACdA,EAAO,gBAITA,EAAO,gBAAgB,KAAK,OAAO,KAAK,GAAG;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,iBAAyB;AAC/B,QAAIgT,IAAI,GAAGC,IAAI;AACf,WAAOD,MAAM,IAAG,CAAAA,IAAI,KAAK,OAAA;AACzB,WAAOC,MAAM,IAAG,CAAAA,IAAI,KAAK,OAAA;AAEzB,UAAMC,IAAQ,KAAK,KAAK,KAAO,KAAK,IAAIF,CAAC,CAAC,IAAI,KAAK,IAAI,IAAM,KAAK,KAAKC,CAAC;AAGxE,WAAO,KAAK,IAAI,IAAI,KAAK,IAAI,GAAGC,CAAK,CAAC,IAAI;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKQ,OAAOjH,GAAaC,GAAqB;AAC/C,WAAO,KAAK,OAAA,KAAYA,IAAMD,KAAOA;AAAA,EACvC;AACF;ACjRO,MAAMkH,GAA+C;AAAA,EAI1D,YAAYnT,GAAsB4K,IAA2B,IAAI;AAC/D,SAAK,SAAS5K,GACd,KAAK,cAAc4K;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,SACEC,GACAhD,GACAhC,IAA6B,CAAA,GACd;AACf,UAAMiF,IAAyB,CAAA,GACzB,EAAE,OAAAC,GAAO,QAAAC,EAAA,IAAWnD,GAEpBqD,IAAgBrF,EAAQ,eAAe,KACvCoF,IAAU,KAAK,OAAO,QAAQ,WAAW,IAGzCE,IAAe,KAAK,YAAY,UAAU,QAAQ,QAClDC,IAAc,KAAK,YAAY,UAAU,OAAO,OAAO,KACvDC,IAAc,KAAK,YAAY,UAAU,OAAO,OAAO,IAGvDC,IAAc,KAAK,YAAY,QAAQ,UAAU,OAAO,GACxDC,IAAc,KAAK,YAAY,QAAQ,UAAU,OAAO,GACxDC,IAAcF,MAAgB,KAAOC,MAAgB,GAGrD3D,IAAY/B,EAAQ,eAAeqF,GAGnCkI,IAAa;AAAA,MACjB,GAAGnW;AAAA,MACH,GAAG,KAAK,OAAO;AAAA,IAAA,GAGX,EAAE,MAAA+Q,GAAM,WAAA/J,GAAW,WAAAC,GAAW,YAAAmP,GAAY,iBAAAC,MAAoBF,GAG9DG,IAAe,KAAK,KAAK1I,IAAamD,CAAI,GAK1CwF,IADgB5L,IADC,MAEgB,GAIjCY,IAASyC,IAAUuI,GACnBC,IAAO1I,IAAQE,IAAUuI,GACzB3F,IAAiB4F,IAAOjL,GAGxBkL,IAAoBH,IAAe,IAAI1F,KAAkB0F,IAAe,KAAK,GAI7EI,IAAa1I,IAAUhH,IAAa2D,IAAY,GAChDgM,IAAa5I,IAASC,IAAUhH,IAAa2D,IAAY,GACzDkG,IAAkB8F,IAAaD,GAG/BE,IAAa7F,IAAO,IAAIF,KAAmBE,IAAO,KAAK;AAE7D,QAAIlG,IAAa;AAEjB,aAASgM,IAAW,GAAGA,IAAW9F,KAAQlG,IAAa+C,GAAYiJ,KAAY;AAE7E,YAAMC,IAAQ/F,MAAS,KAClB2F,IAAaC,KAAc,IAC5BD,IAAcG,IAAWD;AAG7B,UAAIxP,IAAQ;AACZ,MAAIiP,MAAoB,WACtBjP,IAAQyP,IAAWT,IACVC,MAAoB,kBAC7BjP,IAAQyP,IAAW,KAAK;AAK1B,eAASE,IAAW,GAAGA,IAAWT,KAAgBzL,IAAa+C,GAAYmJ,KAAY;AAGrF,cAAM/L,IAAUsL,MAAiB,KAC5B/K,IAASiL,KAAQ,IAClBjL,IAAUwL,IAAWN,GAGnBO,IAAQ,KAAK,eAAehM,GAAS8C,GAAO9G,GAAWC,GAAWG,CAAK,GAGvElE,IAAI8H,GACJ7H,IAAI2T,IAAQE,GAGZvH,IAAgBlB,IAAc,KAAK,OAAOF,GAAaC,CAAW,IAAI,GACtEQ,IAAkBnE,IAAY8E;AAGpC,YAAIlL,IAAW;AACf,QAAI2J,MAAiB,YAEnB3J,IAAW,KAAK,kBAAkByG,GAAS8C,GAAO9G,GAAWC,GAAWG,CAAK,IACpE8G,MAAiB,aAE1B3J,IAAW,KAAK,OAAO4J,GAAaC,CAAW;AAOjD,cAAMI,IAAaM,IADI,MACgC,GACjDL,KAAaK,IAAkB,GAC/BF,KAAOZ,IAAUQ,GACjBE,KAAOZ,IAAQE,IAAUQ,GACzBK,KAAOb,IAAUS,IACjBE,KAAOZ,IAASC,IAAUS;AAEhC,QAAAZ,EAAQ,KAAK;AAAA,UACX,IAAIhD;AAAA,UACJ,GAAG,KAAK,IAAI+D,IAAM,KAAK,IAAI1L,GAAGwL,EAAI,CAAC;AAAA,UACnC,GAAG,KAAK,IAAIG,IAAM,KAAK,IAAI1L,GAAGwL,EAAI,CAAC;AAAA,UACnC,UAAApK;AAAA,UACA,OAAOkL;AAAA,UACP,UAAUX;AAAA,UACV,QAAQjE,IAAa;AAAA,QAAA,CACtB,GAEDA;AAAA,MACF;AAAA,IACF;AAEA,WAAOgD;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,eACN3K,GACA+T,GACAjQ,GACAC,GACAG,GACQ;AAER,UAAM8P,IAAchU,IAAI+T;AAGxB,WAAOjQ,IAAY,KAAK,IAAIC,IAAYiQ,IAAc,IAAI,KAAK,KAAK9P,CAAK;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,kBACNlE,GACA+T,GACAjQ,GACAC,GACAG,GACQ;AAER,UAAM8P,IAAchU,IAAI+T,GAGlBE,IAAanQ,IAAYC,IAAY,IAAI,KAAK,KAAK,KAAK,IAAIA,IAAYiQ,IAAc,IAAI,KAAK,KAAK9P,CAAK,IAAI6P;AAGnH,WAAO,KAAK,KAAKE,CAAU,KAAK,MAAM,KAAK;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,OAAOnI,GAAaC,GAAqB;AAC/C,WAAO,KAAK,OAAA,KAAYA,IAAMD,KAAOA;AAAA,EACvC;AACF;ACrNO,MAAMoI,KAAqB,KAW5BC,IAAK,MAAM,KAAK,KAAK,CAAC,GACfC,KAA8C;AAAA,EACzD,CAACD,IAAK,GAAO,CAAG;AAAA;AAAA,EAChB,CAAC,IAAIA,IAAK,GAAG,CAAG;AAAA;AAAA,EAChB,CAAC,IAAIA,GAAQ,EAAG;AAAA;AAAA,EAChB,CAAC,IAAIA,IAAK,GAAG,GAAG;AAAA;AAAA,EAChB,CAACA,IAAK,GAAO,GAAG;AAAA;AAAA,EAChB,CAAC,GAAY,EAAG;AAAA;AAClB,GAMaE,KAAyBD,GAAmB,CAAC,EAAE,CAAC,IAAIF,IAGpDI,KAA2BF,GAAmB,CAAC,EAAE,CAAC,IAAIF;AAM5D,SAASK,GAAmBC,GAAsD;AACvF,SAAO;AAAA,IACL,SAASH,KAAyBG;AAAA,IAClC,WAAWF,KAA2BE;AAAA,EAAA;AAE1C;AAWO,SAASC,GACdtI,GAAYC,GAAYsI,GACxBC,GAAiBC,GACjBJ,GAC4B;AAC5B,QAAM,EAAE,SAAAK,EAAA,IAAYN,GAAmBC,CAAI;AAC3C,SAAO;AAAA,IACL,IAAIG,IAAUE,IAAU1I;AAAA,IACxB,IAAIyI,IAAUJ,KAAQpI,IAAKD,IAAK;AAAA,EAAA;AAEpC;AAGO,MAAM2I,KAAuD;AAAA,EAClE,CAAC,GAAK,GAAG,EAAE;AAAA,EACX,CAAE,GAAG,GAAI,EAAE;AAAA,EACX,CAAC,IAAI,GAAK,CAAC;AAAA,EACX,CAAC,IAAK,GAAG,CAAE;AAAA,EACX,CAAE,GAAG,IAAI,CAAE;AAAA,EACX,CAAC,GAAI,IAAK,CAAC;AACb;AAMO,SAASC,GAAgBC,GAA+C;AAC7E,MAAIA,MAAS,EAAG,QAAO,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;AACjC,QAAMC,IAAyC,CAAA;AAC/C,MAAI,CAAC9I,GAAIC,GAAI8I,CAAE,IAAI,CAAC,GAAG,CAACF,GAAMA,CAAI;AAClC,aAAW,CAAC9S,GAAIC,GAAIgT,CAAE,KAAKL;AACzB,aAASM,IAAO,GAAGA,IAAOJ,GAAMI;AAC9B,MAAAH,EAAM,KAAK,CAAC9I,GAAIC,GAAI8I,CAAE,CAAC,GACvB/I,KAAMjK,GAAIkK,KAAMjK,GAAI+S,KAAMC;AAG9B,SAAOF;AACT;ACpFO,MAAMI,GAAoD;AAAA;AAAA;AAAA,EAK/D,YAAYxV,GAAsByV,IAA4B,IAAI;AAChE,SAAK,SAASzV;AAAA,EAChB;AAAA,EAEA,SACE6K,GACAhD,GACAhC,IAA4D,CAAA,GAC7C;AACf,UAAMiF,IAAyB,CAAA,GACzB,EAAE,OAAAC,GAAO,QAAAC,EAAA,IAAWnD,GACpB6N,IAAc3K,IAAQ,GACtB4K,IAAc3K,IAAS,GAEvBE,IAAgBrF,EAAQ,eAAe,KAQvC+P,IALkB;AAAA,MACtB,GAAG1Y;AAAA,MACH,GAAG,KAAK,OAAO;AAAA,IAAA,EAGe,WAAW,GAKrCyX,IAAOzJ,IAAgB0K;AAE7B,QAAIC,IAAS,GACTV,IAAO;AAEX,WAAOU,IAAShL,KAAY;AAC1B,YAAMuK,IAAQF,GAAgBC,CAAI;AAElC,iBAAW,CAAC7I,GAAIC,GAAI8I,CAAE,KAAKD,GAAO;AAChC,YAAIS,KAAUhL,EAAY;AAE1B,cAAM,EAAE,IAAAiL,GAAI,IAAAC,EAAA,IAAOnB,GAAetI,GAAIC,GAAI8I,GAAIK,GAAaC,GAAahB,CAAI;AAE5E,QAAA7J,EAAQ,KAAK;AAAA,UACX,IAAI+K;AAAA,UACJ,GAAGC;AAAA,UACH,GAAGC;AAAA,UACH,UAAU;AAAA,UACV,OAAO;AAAA,UACP,UAAU7K;AAAA;AAAA,UAEV,QAAQ,KAAK,IAAI,GAAG,MAAMiK,CAAI;AAAA,QAAA,CAC/B,GAEDU;AAAA,MACF;AAEA,MAAAV;AAAA,IACF;AAEA,WAAOrK;AAAA,EACT;AACF;ACjDO,MAAMkL,GAAa;AAAA,EAMxB,YAAYhW,GAA4B;AACtC,SAAK,SAASA,EAAO,QACrB,KAAK,cAAcA,EAAO,OAE1B,KAAK,8BAAc,IAAA,GAGnB,KAAK,kBAAkB,KAAK,WAAA;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,aAA8B;AACpC,YAAQ,KAAK,OAAO,WAAA;AAAA,MAClB,KAAK;AACH,eAAO,IAAImM,GAAsB,KAAK,QAAQ,KAAK,WAAW;AAAA,MAChE,KAAK;AACH,eAAO,IAAIwB,GAAoB,KAAK,QAAQ,KAAK,WAAW;AAAA,MAC9D,KAAK;AACH,eAAO,IAAI4C,GAAsB,KAAK,QAAQ,KAAK,WAAW;AAAA,MAChE,KAAK;AACH,eAAO,IAAIkB,GAAuB,KAAK,QAAQ,KAAK,WAAW;AAAA,MACjE,KAAK;AACH,eAAO,IAAI0B,GAAoB,KAAK,QAAQ,KAAK,WAAW;AAAA,MAC9D,KAAK;AACH,eAAO,IAAIqC,GAAyB,KAAK,QAAQ,KAAK,WAAW;AAAA,MAEnE;AACE,eAAO,IAAI7K,GAAsB,KAAK,QAAQ,KAAK,WAAW;AAAA,IAAA;AAAA,EAEpE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,eAAeE,GAAoBhD,GAAkChC,IAAiC,CAAA,GAAmB;AACvH,UAAMiF,IAAU,KAAK,gBAAgB,SAASD,GAAYhD,GAAiBhC,CAAO;AAGlF,WAAAiF,EAAQ,QAAQ,CAAAkB,MAAU;AACxB,WAAK,QAAQ,IAAIA,EAAO,IAAIA,CAAM;AAAA,IACpC,CAAC,GAEMlB;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAiBmL,GAAmD;AAClE,WAAO,KAAK,QAAQ,IAAI,OAAOA,CAAO,CAAC;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,QAAQ,MAAA;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAaC,GAA8C;AAEzD,IAAIA,EAAU,WACZ,OAAO,OAAO,KAAK,QAAQA,EAAU,MAAM,GAGvCA,EAAU,OAAO,aAAaA,EAAU,OAAO,cAAc,KAAK,OAAO,cAC3E,KAAK,kBAAkB,KAAK,WAAA,KAK5BA,EAAU,SACZ,OAAO,OAAO,KAAK,aAAaA,EAAU,KAAK;AAAA,EAEnD;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAwC;AAC9C,WAAO,KAAK,OAAO,cAAc;AAAA,MAC/B,QAAQ,EAAE,UAAU,IAAA;AAAA,MACpB,QAAQ,EAAE,UAAU,KAAA;AAAA,IAAK;AAAA,EAE7B;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAkBC,GAAuD;AACvE,UAAMC,IAAc,KAAK,eAAA;AAEzB,WAAID,KAAiBC,EAAY,OAAO,WAC/B,WAELD,KAAiBC,EAAY,OAAO,WAC/B,WAEF;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAAkBD,GAA2C;AAC3D,UAAME,IAAS,KAAK,YAAY;AAGhC,QAAI,CAACA,KAAUA,EAAO,SAAS;AAC7B;AAIF,UAAMrL,IAASqL,EAAO;AAEtB,QAAIrL,MAAW;AACb;AAGF,QAAI,OAAOA,KAAW;AACpB,aAAOA;AAIT,UAAMsL,IAAmBtL,GACnBuL,IAAa,KAAK,kBAAkBJ,CAAa;AAGvD,WAAII,MAAe,WACVD,EAAiB,UAAUA,EAAiB,UAAUA,EAAiB,SAE5EC,MAAe,WACVD,EAAiB,UAAUA,EAAiB,UAAUA,EAAiB,SAGzEA,EAAiB,UAAUA,EAAiB,UAAUA,EAAiB;AAAA,EAChF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,sBACEzO,GACAgD,GACA2L,GACAL,GACsB;AACtB,UAAME,IAAS,KAAK,YAAY,QAG1BI,IAAiB,KAAK,kBAAkBN,CAAa;AAI3D,QAAIM,MAAmB;AACrB,aAAO,EAAE,QAAQA,EAAA;AAInB,UAAMC,IAAUL,GAAQ,WAAW,IAC7BM,IAAUN,GAAQ,WAAW,KAC7BO,IAAiB,KAAK,OAAO,kBAAkB,KAC/CC,IAAgB,KAAK,OAAO,iBAAiB,GAE7C,EAAE,OAAA9L,GAAO,QAAAC,EAAA,IAAWnD,GAKpBiP,IAFgB/L,IAAQC,IACK4L,IACD/L;AAIlC,QAAIkM,IAAmB,KAAK,KAAKD,IADb,GACuC;AAG3D,IAAAC,KAAoBF,GAGpBE,IAAmB,KAAK,IAAIA,GAAkBP,CAAS;AAGvD,QAAIQ,IAAc,KAAK,MAAMD,GAAkBL,GAASC,CAAO;AAG/D,QAAIK,MAAgBN,KAAWK,IAAmBL,GAAS;AAEzD,YAAMO,IAAQ,KAAK,IAAIP,IAAU,MAAM,EAAE;AACzC,MAAAM,IAAc,KAAK,IAAIC,GAAOF,CAAgB;AAAA,IAChD;AAIA,WAAI,KAAK,OAAO,cAAc,gBAC5BC,IAAc,KAAK,IAAIA,GAAa,KAAK,wBAAwBnM,GAAYhD,CAAe,CAAC,IAGxF,EAAE,QAAQmP,EAAA;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,wBAAwBnM,GAAoBhD,GAA0C;AAC5F,QAAIgD,KAAc,EAAG,QAAO;AAG5B,QAAIqM,IAAU,GACVC,IAAQ;AACZ,WAAOA,IAAQtM;AACb,MAAAqM,KACAC,KAAS,IAAID;AAGf,UAAMjM,IAAU,KAAK,OAAO,SAAS,WAAW,IAC1C2K,IAAU,KAAK,OAAO,WAAW,WAAW,GAC5CF,IAAc7N,EAAgB,QAAQ,GACtC8N,IAAc9N,EAAgB,SAAS,GAGvCuP,IAAiB,KAAK,KAAK,CAAC,IAAI,GAChCC,IAAmB,IAAI,KAAK,KAAK,CAAC,GAGlCC,KAAQ3B,IAAc1K,IAAU2K,IAAUsB,MAAYA,IAAU,MAGhEK,KAAQ7B,IAAczK,IAAUmM,IAAiBxB,IAAUsB,MACjDE,IAAiBF,IAAUG;AAE3C,WAAO,KAAK,IAAI,IAAI,KAAK,IAAIC,GAAMC,CAAI,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKQ,MAAMrE,GAAejH,GAAaC,GAAqB;AAC7D,WAAO,KAAK,IAAID,GAAK,KAAK,IAAIC,GAAKgH,CAAK,CAAC;AAAA,EAC3C;AACF;AC0fO,IAAKsE,sBAAAA,OACVA,EAAA,OAAO,QACPA,EAAA,WAAW,YACXA,EAAA,UAAU,WACVA,EAAA,aAAa,cACbA,EAAA,kBAAkB,mBALRA,IAAAA,KAAA,CAAA,CAAA;ACtxBZ,MAAMC,KAAkD;AAAA;AAAA,EAEtD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AACX,GAYMC,KAA+E;AAAA;AAAA,EAEnF,QAAQ;AAAA,IACN,WAAW;AAAA,IACX,QAAQ,CAAA;AAAA;AAAA,EAAC;AAAA;AAAA,EAGX,QAAQ;AAAA,IACN,WAAW;AAAA,IACX,QAAQ,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;AAAA,EAAA;AAAA;AAAA,EAGjD,UAAU;AAAA,IACR,WAAW;AAAA,IACX,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;AAAA,EAAA;AAAA;AAAA,EAGxC,UAAU;AAAA,IACR,WAAW;AAAA,IACX,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC;AAAA,EAAA;AAAA;AAAA,EAG5D,SAAS;AAAA,IACP,WAAWrD;AAAA,IACX,QAAQE;AAAA,EAAA;AAAA;AAAA,EAGV,SAAS;AAAA,IACP,WAAW;AAAA,IACX,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC;AAAA,EAAA;AAAA;AAAA,EAGzF,SAAS;AAAA,IACP,WAAW;AAAA,IACX,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC;AAAA,EAAA;AAEnD;AAOO,SAASoD,GAAYC,GAA+D;AACzF,MAAKA;AAGL,WAAIA,KAASH,KACJA,GAAiBG,CAAsB,IAIzCA;AACT;AAiBO,SAASC,GAAgCD,GAAsB1R,GAAqBD,GAA6B;AACtH,QAAM6R,IAAWJ,GAAiCE,CAAK;AACvD,MAAI,CAACE,EAAU,QAAO;AAEtB,QAAMvW,IAAQ2E,IAAc4R,EAAS;AAGrC,MAAIF,MAAU;AAEZ,WAAO,UADQ,KAAK,MAAM,KAAKrW,IAAQ,GAAG,IAAI,GACvB;AAMzB,QAAMwW,IAAKD,EAAS,OAAO,IAAI,CAAC,CAAC3X,CAAC,MAAMA,CAAC,GACnC6X,IAAKF,EAAS,OAAO,IAAI,CAAC,CAAA,EAAG1X,CAAC,MAAMA,CAAC,GACrC6X,KAAiB,KAAK,IAAI,GAAGF,CAAE,IAAI,KAAK,IAAI,GAAGA,CAAE,KAAK,IAAKxW,GAC3D2W,KAAiB,KAAK,IAAI,GAAGF,CAAE,IAAI,KAAK,IAAI,GAAGA,CAAE,KAAK,IAAKzW,GAG3D4W,KAAmB,KAAK,IAAI,GAAGJ,CAAE,IAAI,KAAK,IAAI,GAAGA,CAAE,KAAKxW,GAGxD6W,KAAgBnS,KAAckS,KAAmB,GACjDE,IAAenS,IAAc,GAG7BoS,IAAmBF,IAAeH,GAClCM,IAAiBF,IAAeH;AAStC,SAAO,WANcJ,EAAS,OAAO,IAAI,CAAC,CAAC3X,GAAGC,CAAC,MAAM;AACnD,UAAMoY,IAAU,KAAK,OAAOrY,IAAIoB,IAAQ+W,KAAoB,GAAG,IAAI,KAC7DG,IAAU,KAAK,OAAOrY,IAAImB,IAAQgX,KAAkB,GAAG,IAAI;AACjE,WAAO,GAAGC,CAAO,MAAMC,CAAO;AAAA,EAChC,CAAC,EAE8B,KAAK,IAAI,CAAC;AAC3C;AC/HA,SAASC,GAAexF,GAAsC;AAC5D,SAAOA,KAAS1W;AAClB;AAMO,SAASmc,GAAcC,GAAmD;AAC/E,SAAKA,IACDF,GAAeE,CAAM,IAAUpc,GAAeoc,CAAM,IACjDA,IAFapc,GAAe;AAGrC;AAKO,SAASqc,GAAkBC,GAA0C;AAC1E,MAAI,CAACA,EAAQ,QAAO;AAEpB,QAAMC,IAAkB,CAAA;AA6BxB,MA3BID,EAAO,cAAc,UACvBC,EAAM,KAAK,aAAaD,EAAO,SAAS,GAAG,GAEzCA,EAAO,SAAS,UAClBC,EAAM,KAAK,QAAQD,EAAO,IAAI,KAAK,GAEjCA,EAAO,eAAe,UACxBC,EAAM,KAAK,cAAcD,EAAO,UAAU,GAAG,GAE3CA,EAAO,aAAa,UACtBC,EAAM,KAAK,YAAYD,EAAO,QAAQ,GAAG,GAEvCA,EAAO,aAAa,UACtBC,EAAM,KAAK,YAAYD,EAAO,QAAQ,GAAG,GAEvCA,EAAO,YAAY,UACrBC,EAAM,KAAK,WAAWD,EAAO,OAAO,GAAG,GAErCA,EAAO,UAAU,UACnBC,EAAM,KAAK,SAASD,EAAO,KAAK,GAAG,GAEjCA,EAAO,cAAc,UACvBC,EAAM,KAAK,cAAcD,EAAO,SAAS,MAAM,GAE7CA,EAAO,WAAW,UACpBC,EAAM,KAAK,UAAUD,EAAO,MAAM,GAAG,GAEnCA,EAAO,eAAe;AACxB,QAAI,OAAOA,EAAO,cAAe;AAC/B,MAAAC,EAAM,KAAK,eAAeD,EAAO,UAAU,GAAG;AAAA,SACzC;AACL,YAAME,IAAKF,EAAO;AAClB,MAAAC,EAAM,KAAK,eAAeC,EAAG,CAAC,MAAMA,EAAG,CAAC,MAAMA,EAAG,IAAI,MAAMA,EAAG,KAAK,GAAG;AAAA,IACxE;AAGF,SAAOD,EAAM,KAAK,GAAG;AACvB;AAKA,SAASE,GAAkBjZ,GAA0C;AACnE,MAAI,CAACA,KAAUA,EAAO,UAAU,UAAUA,EAAO,UAAU;AACzD,WAAO;AAET,QAAM+K,IAAQ/K,EAAO,SAAS,GACxBkZ,IAAQlZ,EAAO,SAAS,SACxBmZ,IAAQnZ,EAAO,SAAS;AAC9B,SAAO,GAAG+K,CAAK,MAAMmO,CAAK,IAAIC,CAAK;AACrC;AAkCO,SAASC,GAAqBC,GAAoCnT,GAAsBD,GAAsC;AACnI,MAAI,CAACoT,EAAO,QAAO,CAAA;AAEnB,QAAMC,IAA0B,CAAA;AAQhC,MAL2BD,EAAM,wBAAwB,UAC9BA,EAAM,yBAAyB,UAC/BA,EAAM,4BAA4B,UAClCA,EAAM,2BAA2B,QAEpC;AAEtB,UAAME,IAAaF,EAAM,QAAQ,UAAU;AAC3C,IAAIA,EAAM,wBAAwB,SAChCC,EAAO,sBAAsB,GAAGD,EAAM,mBAAmB,OAChDE,MACTD,EAAO,sBAAsB,GAAGC,CAAU,OAExCF,EAAM,yBAAyB,SACjCC,EAAO,uBAAuB,GAAGD,EAAM,oBAAoB,OAClDE,MACTD,EAAO,uBAAuB,GAAGC,CAAU,OAEzCF,EAAM,4BAA4B,SACpCC,EAAO,0BAA0B,GAAGD,EAAM,uBAAuB,OACxDE,MACTD,EAAO,0BAA0B,GAAGC,CAAU,OAE5CF,EAAM,2BAA2B,SACnCC,EAAO,yBAAyB,GAAGD,EAAM,sBAAsB,OACtDE,MACTD,EAAO,yBAAyB,GAAGC,CAAU;AAAA,EAEjD,MAAA,CAAWF,EAAM,QAAQ,WAAW,WAClCC,EAAO,eAAe,GAAGD,EAAM,OAAO,MAAM;AAM9C,MAFyBA,EAAM,aAAaA,EAAM,eAAeA,EAAM,gBAAgBA,EAAM,YAEvE;AAEpB,UAAMG,IAAaH,EAAM,UAAU,CAAA,GAE7BI,IAAY,EAAE,GAAGD,GAAY,GAAGH,EAAM,UAAA,GACtCK,IAAc,EAAE,GAAGF,GAAY,GAAGH,EAAM,YAAA,GACxCM,IAAe,EAAE,GAAGH,GAAY,GAAGH,EAAM,aAAA,GACzCO,IAAa,EAAE,GAAGJ,GAAY,GAAGH,EAAM,WAAA;AAE7C,IAAAC,EAAO,YAAYL,GAAkBQ,CAAS,GAC9CH,EAAO,cAAcL,GAAkBS,CAAW,GAClDJ,EAAO,eAAeL,GAAkBU,CAAY,GACpDL,EAAO,aAAaL,GAAkBW,CAAU;AAAA,EAClD,MAAA,CAAWP,EAAM,WAEfC,EAAO,SAASL,GAAkBI,EAAM,MAAM;AAIhD,EAAIA,EAAM,WAAW,WACnBC,EAAO,YAAYX,GAAcU,EAAM,MAAM;AAI/C,QAAMQ,IAAYhB,GAAkBQ,EAAM,MAAM;AAchD,MAbAC,EAAO,SAASO,KAAa,QAGzBR,EAAM,YAAY,WACpBC,EAAO,UAAU,OAAOD,EAAM,OAAO,IAInCA,EAAM,WAAW,WACnBC,EAAO,SAASD,EAAM,SAIpBA,EAAM,WAAWA,EAAM,QAAQ,UAAU,WAAWA,EAAM,QAAQ,SAAS,KAAK,GAAG;AACrF,UAAMtO,IAAQsO,EAAM,QAAQ,SAAS,GAC/BH,IAAQG,EAAM,QAAQ,SAAS,SAC/BF,IAAQE,EAAM,QAAQ,SAAS;AACrC,IAAAC,EAAO,UAAU,GAAGvO,CAAK,MAAMmO,CAAK,IAAIC,CAAK,IACzCE,EAAM,QAAQ,WAAW,WAC3BC,EAAO,gBAAgB,GAAGD,EAAM,QAAQ,MAAM;AAAA,EAElD;AAaA,MAVIA,EAAM,cAAc,WACtBC,EAAO,YAAYD,EAAM,YAIvBA,EAAM,gBAAgB,WACxBC,EAAO,cAAcD,EAAM,cAIzBA,EAAM,aAAa,QAAW;AAChC,QAAIS;AAGJ,UAAMC,IAAW,OAAOV,EAAM,YAAa,YAAYA,EAAM,aAAa,QAAQ,WAAWA,EAAM,UAC7FrZ,IAAS+Z,IAAYV,EAAM,WAA8B;AAE/D,QAAIrZ,GAAQ,SAAS,qBAAqBkG;AAExC,MAAA4T,IAAgBjC,GAAgC7X,EAAO,OAAOkG,GAAaD,CAAU;AAAA,SAChF;AAEL,YAAM+T,IAAgBD,KAAY/Z,IAASA,EAAO,QAAQqZ,EAAM;AAChE,MAAAS,IAAgBnC,GAAYqC,CAAuC;AAAA,IACrE;AAEA,IAAIF,MAEEA,MAAkB,SACpBR,EAAO,WAAW,WAElBA,EAAO,WAAWQ,GAClBR,EAAO,WAAW;AAAA,EAGxB;AAEA,SAAOA;AACT;AAKO,SAASW,GAAqB5Z,GAAsBiZ,GAA+B;AACxF,EAAIA,EAAO,iBAAiB,WAAWjZ,EAAQ,MAAM,eAAeiZ,EAAO,eACvEA,EAAO,wBAAwB,WAAWjZ,EAAQ,MAAM,sBAAsBiZ,EAAO,sBACrFA,EAAO,yBAAyB,WAAWjZ,EAAQ,MAAM,uBAAuBiZ,EAAO,uBACvFA,EAAO,4BAA4B,WAAWjZ,EAAQ,MAAM,0BAA0BiZ,EAAO,0BAC7FA,EAAO,2BAA2B,WAAWjZ,EAAQ,MAAM,yBAAyBiZ,EAAO,yBAC3FA,EAAO,WAAW,WAAWjZ,EAAQ,MAAM,SAASiZ,EAAO,SAC3DA,EAAO,cAAc,WAAWjZ,EAAQ,MAAM,YAAYiZ,EAAO,YACjEA,EAAO,gBAAgB,WAAWjZ,EAAQ,MAAM,cAAciZ,EAAO,cACrEA,EAAO,iBAAiB,WAAWjZ,EAAQ,MAAM,eAAeiZ,EAAO,eACvEA,EAAO,eAAe,WAAWjZ,EAAQ,MAAM,aAAaiZ,EAAO,aACnEA,EAAO,cAAc,WAAWjZ,EAAQ,MAAM,YAAYiZ,EAAO,YACjEA,EAAO,WAAW,WAAWjZ,EAAQ,MAAM,SAASiZ,EAAO,SAC3DA,EAAO,YAAY,WAAWjZ,EAAQ,MAAM,UAAUiZ,EAAO,UAC7DA,EAAO,WAAW,WAAWjZ,EAAQ,MAAM,SAASiZ,EAAO,SAC3DA,EAAO,YAAY,WAAWjZ,EAAQ,MAAM,UAAUiZ,EAAO,UAC7DA,EAAO,kBAAkB,WAAWjZ,EAAQ,MAAM,gBAAgBiZ,EAAO,gBACzEA,EAAO,cAAc,WAAWjZ,EAAQ,MAAM,YAAYiZ,EAAO,YACjEA,EAAO,gBAAgB,WAAWjZ,EAAQ,MAAM,cAAciZ,EAAO,cACrEA,EAAO,aAAa,WAAWjZ,EAAQ,MAAM,WAAWiZ,EAAO,WAC/DA,EAAO,aAAa,WAAWjZ,EAAQ,MAAM,WAAWiZ,EAAO;AACrE;AAUO,SAASY,GAA8B7Z,GAAsBgZ,GAAoCnT,GAAsBD,GAA2B;AACvJ,QAAMqT,IAASF,GAAqBC,GAAOnT,GAAaD,CAAU;AAClE,EAAAgU,GAAqB5Z,GAASiZ,CAAM;AACtC;AAKO,SAASa,GAAiBC,GAAkD;AACjF,SAAKA,IACD,MAAM,QAAQA,CAAS,IAAUA,EAAU,KAAK,GAAG,IAChDA,IAFgB;AAGzB;AAKO,SAASC,GAAwBha,GAAsB+Z,GAAgD;AAC5G,QAAME,IAAWH,GAAiBC,CAAS;AAC3C,EAAIE,KACFA,EAAS,MAAM,GAAG,EAAE,QAAQ,CAAAC,MAAO;AACjC,IAAIA,EAAI,UAAQla,EAAQ,UAAU,IAAIka,EAAI,MAAM;AAAA,EAClD,CAAC;AAEL;AAKO,SAASC,GAA2Bna,GAAsB+Z,GAAgD;AAC/G,QAAME,IAAWH,GAAiBC,CAAS;AAC3C,EAAIE,KACFA,EAAS,MAAM,GAAG,EAAE,QAAQ,CAAAC,MAAO;AACjC,IAAIA,EAAI,UAAQla,EAAQ,UAAU,OAAOka,EAAI,MAAM;AAAA,EACrD,CAAC;AAEL;ACrRA,MAAME,KAAU;AAAA,EAEd,YAAY;AAAA,EACZ,UAAU;AAEZ;AAEO,MAAMC,GAAW;AAAA,EAuBtB,YAAY1a,GAAgC2a,GAAkCC,GAA8B;AAlB5G,SAAQ,QAAmBpD,EAAU,MACrC,KAAQ,eAAmC,MAC3C,KAAQ,YAA8B,MAGtC,KAAQ,WAAkC,MAC1C,KAAQ,WAAkC,MAG1C,KAAQ,kBAA0B,GAOlC,KAAQ,oBAA6D,MAGnE,KAAK,SAASxX,GACd,KAAK,kBAAkB2a,GACvB,KAAK,UAAUC,GAGf,KAAK,mBAAmBA,GAAS,SAAS;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,6BAA6BC,GAAyD;AACpF,SAAK,oBAAoBA;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,WAAsB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,cAAuB;AACrB,WAAO,KAAK,UAAUrD,EAAU,QAAQ,KAAK,UAAUA,EAAU;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAAsBtE,GAAuB;AACnD,WAAOA,IAAQ,IAAIA,IAAQ,MAAMA;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,yBAAyBjN,GAAoBC,GAAqB2B,GAAqE;AAC7I,UAAMiT,IAAoB,KAAK,sBAAsB,KAAK,OAAO,YAAY,GACvEC,IAAelT,EAAgB,SAASiT,GACxC1K,IAAcnK,IAAaC;AAEjC,QAAI8U,IAAcD,GACdE,IAAaD,IAAc5K;AAE/B,UAAM8K,IAAWrT,EAAgB,QAAQiT;AACzC,WAAIG,IAAaC,MACfD,IAAaC,GACbF,IAAcC,IAAa7K,IAGtB,EAAE,OAAO6K,GAAY,QAAQD,EAAA;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,wBACNnT,GACAlG,GACiB;AACjB,UAAMsG,IAAUJ,EAAgB,QAAQ,GAClCK,IAAUL,EAAgB,SAAS,GAEnCsT,IAAUlT,IAAUtG,EAAc,GAClCyZ,IAAUlT,IAAUvG,EAAc;AAExC,WAAO;AAAA,MACL,GAAGwZ;AAAA,MACH,GAAGC;AAAA,MACH,UAAU;AAAA,MACV,OAAO;AAAA;AAAA,IAAA;AAAA,EAEX;AAAA;AAAA;AAAA;AAAA,EAKQ,4BAA4Bnb,GAAiC;AACnE,UAAMC,IAAuB,CAAC,uBAAuB;AAErD,QAAID,EAAO,MAAM,UAAaA,EAAO,MAAM,QAAW;AACpD,YAAME,IAAIF,EAAO,KAAK,GAChBG,IAAIH,EAAO,KAAK;AACtB,MAAAC,EAAW,KAAK,aAAaC,CAAC,OAAOC,CAAC,KAAK;AAAA,IAC7C;AAEA,WAAIH,EAAO,aAAa,UACtBC,EAAW,KAAK,UAAUD,EAAO,QAAQ,MAAM,GAK1CC,EAAW,KAAK,GAAG;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,sBACNG,GACAO,GACAC,GACAwa,GACAC,GACAC,GACAC,GACAhb,GACW;AACX,UAAMib,IAAmB,KAAK,4BAA4B7a,CAAa,GACjE8a,IAAiB,KAAK,4BAA4B7a,CAAW;AAGnE,WAAAR,EAAQ,MAAM,aAAa,QAGTA,EAAQ;AAAA,MACxB;AAAA,QACE;AAAA,UACE,WAAWob;AAAA,UACX,OAAO,GAAGJ,CAAS;AAAA,UACnB,QAAQ,GAAGC,CAAU;AAAA,QAAA;AAAA,QAEvB;AAAA,UACE,WAAWI;AAAA,UACX,OAAO,GAAGH,CAAO;AAAA,UACjB,QAAQ,GAAGC,CAAQ;AAAA,QAAA;AAAA,MACrB;AAAA,MAEF;AAAA,QACE,UAAAhb;AAAA,QACA,QAAQ;AAAA,QACR,MAAM;AAAA,MAAA;AAAA,IACR;AAAA,EAIJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,oBAAoBH,GAAsB6P,GAAsB;AAOtE,QANA7P,EAAQ,MAAM,SAAS,OAAO6P,CAAM,GACpC7P,EAAQ,UAAU,IAAI,gBAAgB,GACtCga,GAAwBha,GAAS,KAAK,gBAAgB,GAIlD,KAAK,SAAS,SAAS;AACzB,YAAMiZ,IAASF,GAAqB,KAAK,QAAQ,SAAS/Y,EAAQ,cAAcA,EAAQ,WAAW;AAEnG,MAAIiZ,EAAO,iBAAiB,WAAWjZ,EAAQ,MAAM,eAAeiZ,EAAO,eACvEA,EAAO,wBAAwB,WAAWjZ,EAAQ,MAAM,sBAAsBiZ,EAAO,sBACrFA,EAAO,yBAAyB,WAAWjZ,EAAQ,MAAM,uBAAuBiZ,EAAO,uBACvFA,EAAO,4BAA4B,WAAWjZ,EAAQ,MAAM,0BAA0BiZ,EAAO,0BAC7FA,EAAO,2BAA2B,WAAWjZ,EAAQ,MAAM,yBAAyBiZ,EAAO,yBAC3FA,EAAO,WAAW,WAAWjZ,EAAQ,MAAM,SAASiZ,EAAO,SAC3DA,EAAO,cAAc,WAAWjZ,EAAQ,MAAM,YAAYiZ,EAAO,YACjEA,EAAO,gBAAgB,WAAWjZ,EAAQ,MAAM,cAAciZ,EAAO,cACrEA,EAAO,iBAAiB,WAAWjZ,EAAQ,MAAM,eAAeiZ,EAAO,eACvEA,EAAO,eAAe,WAAWjZ,EAAQ,MAAM,aAAaiZ,EAAO,aACnEA,EAAO,cAAc,WAAWjZ,EAAQ,MAAM,YAAYiZ,EAAO,YACjEA,EAAO,WAAW,WAAWjZ,EAAQ,MAAM,SAASiZ,EAAO,SAC3DA,EAAO,YAAY,WAAWjZ,EAAQ,MAAM,UAAUiZ,EAAO,UAC7DA,EAAO,WAAW,WAAWjZ,EAAQ,MAAM,SAASiZ,EAAO,SAC3DA,EAAO,YAAY,WAAWjZ,EAAQ,MAAM,UAAUiZ,EAAO,UAC7DA,EAAO,kBAAkB,WAAWjZ,EAAQ,MAAM,gBAAgBiZ,EAAO,gBACzEA,EAAO,cAAc,WAAWjZ,EAAQ,MAAM,YAAYiZ,EAAO,YACjEA,EAAO,gBAAgB,WAAWjZ,EAAQ,MAAM,cAAciZ,EAAO;AAAA,IAC3E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,qBAAqBjZ,GAAsBsb,GAA8B;AAM/E,QALAtb,EAAQ,MAAM,SAASsb,GACvBtb,EAAQ,UAAU,OAAO,gBAAgB,GACzCma,GAA2Bna,GAAS,KAAK,gBAAgB,GAGrD,KAAK,SAAS,SAAS;AACzB,YAAMiZ,IAASF,GAAqB,KAAK,QAAQ,SAAS/Y,EAAQ,cAAcA,EAAQ,WAAW;AAEnG,MAAIiZ,EAAO,iBAAiB,WAAWjZ,EAAQ,MAAM,eAAeiZ,EAAO,eACvEA,EAAO,wBAAwB,WAAWjZ,EAAQ,MAAM,sBAAsBiZ,EAAO,sBACrFA,EAAO,yBAAyB,WAAWjZ,EAAQ,MAAM,uBAAuBiZ,EAAO,uBACvFA,EAAO,4BAA4B,WAAWjZ,EAAQ,MAAM,0BAA0BiZ,EAAO,0BAC7FA,EAAO,2BAA2B,WAAWjZ,EAAQ,MAAM,yBAAyBiZ,EAAO,yBAC3FA,EAAO,WAAW,WAAWjZ,EAAQ,MAAM,SAASiZ,EAAO,SAC3DA,EAAO,cAAc,WAAWjZ,EAAQ,MAAM,YAAYiZ,EAAO,YACjEA,EAAO,gBAAgB,WAAWjZ,EAAQ,MAAM,cAAciZ,EAAO,cACrEA,EAAO,iBAAiB,WAAWjZ,EAAQ,MAAM,eAAeiZ,EAAO,eACvEA,EAAO,eAAe,WAAWjZ,EAAQ,MAAM,aAAaiZ,EAAO,aACnEA,EAAO,cAAc,WAAWjZ,EAAQ,MAAM,YAAYiZ,EAAO,YACjEA,EAAO,WAAW,WAAWjZ,EAAQ,MAAM,SAASiZ,EAAO,SAC3DA,EAAO,YAAY,WAAWjZ,EAAQ,MAAM,UAAUiZ,EAAO,UAC7DA,EAAO,WAAW,WAAWjZ,EAAQ,MAAM,SAASiZ,EAAO,SAC3DA,EAAO,YAAY,WAAWjZ,EAAQ,MAAM,UAAUiZ,EAAO,UAC7DA,EAAO,kBAAkB,WAAWjZ,EAAQ,MAAM,gBAAgBiZ,EAAO,gBACzEA,EAAO,cAAc,WAAWjZ,EAAQ,MAAM,YAAYiZ,EAAO,YACjEA,EAAO,gBAAgB,WAAWjZ,EAAQ,MAAM,cAAciZ,EAAO;AAAA,IAC3E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,uBAAuBjZ,GAAsBU,GAAyB6a,GAA4B;AAGxG,QAAIC,IAAmBD,IAClB,KAAK,SAAS,WAAW,KAAK,SAAS,UACxC,KAAK,SAAS;AAIlB,IAAIA,KAAe,KAAK,SAAS,WAAW,KAAK,QAAQ,QAAQ,aAAa,WAC5EC,IAAc,EAAE,GAAGA,GAAa,UAAU,OAAA;AAG5C,UAAMC,IAAiB,MAAM;AAE3B,YAAMC,IAAgB1b,EAAQ,cACxB2b,IAAe3b,EAAQ,aAGvBiZ,IAASF,GAAqByC,GAAaE,GAAeC,CAAY;AAG5E,MAAI1C,EAAO,aAAa,SACtBjZ,EAAQ,MAAM,WAAWiZ,EAAO,WAGhCjZ,EAAQ,MAAM,WAAW,SAEvBiZ,EAAO,aAAa,WACtBjZ,EAAQ,MAAM,WAAWiZ,EAAO,WAI9BvY,EAAO,UAAU,cAAc,aACjC,sBAAsB+a,CAAc;AAAA,IAExC;AAGA,0BAAsBA,CAAc;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,oBACNzb,GACAwH,GACAlG,GACAf,GACAqb,GACgB;AAChB,UAAMN,IAAiBtb,EAAQ,MAAM,UAAU,IACzC6b,IAAgB7b,EAAQ,aACxB8b,IAAiB9b,EAAQ,cAGzB+b,IAAkB,KAAK,yBAAyBF,GAAeC,GAAgBtU,CAAe,GAC9FwU,IAAiB,KAAK,wBAAwBxU,GAAiBlG,CAAa;AAGlF,SAAK,gBAAgB,oBAAoBtB,CAAO;AAGhD,UAAMG,IAAW,KAAK,OAAO,qBAAqB;AAGlD,SAAK,oBAAoBH,GAASoa,GAAQ,QAAQ;AAGlD,UAAM6B,IAAkC1b,KAAiB;AAAA,MACvD,GAAG;AAAA,MACH,GAAG;AAAA,MACH,UAAUe,EAAc;AAAA,MACxB,OAAO;AAAA;AAAA,IAAA,GAGH4a,IAAaN,GAAgB,SAASC,GACtCM,IAAcP,GAAgB,UAAUE,GAGxCrb,IAAY,KAAK;AAAA,MACrBT;AAAA,MACAic;AAAA,MACAD;AAAA,MACAE;AAAA,MACAC;AAAA,MACAJ,EAAgB;AAAA,MAChBA,EAAgB;AAAA,MAChB5b;AAAA,IAAA,GAIIO,IAA0B;AAAA,MAC9B,IAAI,SAAS,KAAK,IAAA,CAAK;AAAA,MACvB,SAAAV;AAAA,MACA,WAAAS;AAAA,MACA,WAAWwb;AAAA,MACX,SAASD;AAAA,MACT,WAAW,YAAY,IAAA;AAAA,MACvB,UAAA7b;AAAA,IAAA;AAIF,gBAAK,YAAY;AAAA,MACf,SAAAH;AAAA,MACA,eAAAsB;AAAA,MACA,gBAAA0a;AAAA,MACA,gBAAAV;AAAA,MACA,eAAAO;AAAA,MACA,gBAAAC;AAAA,MACA,YAAYC,EAAgB;AAAA,MAC5B,aAAaA,EAAgB;AAAA,IAAA,GAI/B,KAAK,uBAAuB/b,GAASU,GAAQ,EAAI,GAE1C;AAAA,MACL,SAAAV;AAAA,MACA,eAAAsB;AAAA,MACA,iBAAiBZ;AAAA,MACjB,WAAW;AAAA,MACX,eAAAmb;AAAA,MACA,gBAAAC;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,sBACN9b,GACAsB,GACAf,GACAqb,GACgB;AAEhB,IAAA5b,EAAQ,MAAM,SAAS,OAAOoa,GAAQ,UAAU,GAGhD,KAAK,gBAAgB,oBAAoBpa,CAAO;AAGhD,UAAMG,IAAW,KAAK,OAAO,qBAAqB;AAIlD,IAAAH,EAAQ,UAAU,OAAO,gBAAgB,GACzCma,GAA2Bna,GAAS,KAAK,gBAAgB;AAGzD,UAAMic,IAAiB1b,KAAiB,KAAK,WAAW,kBAAkB,EAAE,GAAG,GAAG,GAAG,GAAG,UAAU,GAAG,OAAO,EAAA,GACtG2b,IAAaN,GAAgB,SAAS,KAAK,WAAW,cAAc5b,EAAQ,aAC5Emc,IAAcP,GAAgB,UAAU,KAAK,WAAW,eAAe5b,EAAQ,cAG/Eoc,IAA2B;AAAA,MAC/B,GAAG;AAAA,MACH,GAAG;AAAA,MACH,UAAU9a,EAAc;AAAA,MACxB,OAAO;AAAA;AAAA,IAAA,GAGH+a,IAAc,KAAK,WAAW,iBAAiBrc,EAAQ,aACvD0a,IAAe,KAAK,WAAW,kBAAkB1a,EAAQ,cAGzDS,IAAY,KAAK;AAAA,MACrBT;AAAA,MACAic;AAAA,MACAG;AAAA,MACAF;AAAA,MACAC;AAAA,MACAE;AAAA,MACA3B;AAAA,MACAva;AAAA,IAAA,GAIIO,IAA0B;AAAA,MAC9B,IAAI,WAAW,KAAK,IAAA,CAAK;AAAA,MACzB,SAAAV;AAAA,MACA,WAAAS;AAAA,MACA,WAAWwb;AAAA,MACX,SAAAG;AAAA,MACA,WAAW,YAAY,IAAA;AAAA,MACvB,UAAAjc;AAAA,IAAA;AAIF,gBAAK,uBAAuBH,GAASU,GAAQ,EAAK,GAE3C;AAAA,MACL,SAAAV;AAAA,MACA,eAAAsB;AAAA,MACA,iBAAiBZ;AAAA,MACjB,WAAW;AAAA,MACX,eAAe2b;AAAA,MACf,gBAAgB3B;AAAA,IAAA;AAAA,EAEpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBQ,yBAAyB1a,GAG/B;AACA,UAAMsc,IAAW,iBAAiBtc,CAAO,GACnCiB,IAAS,IAAI,UAAUqb,EAAS,SAAS,GACzCC,IAAWvc,EAAQ,aACnBwc,IAAYxc,EAAQ,cAGpByc,IAAQxb,EAAO,IAAIsb,IAAW,KAC9BG,IAAQzb,EAAO,IAAIub,IAAY,KAC/Brb,IAAW,KAAK,MAAMF,EAAO,GAAGA,EAAO,CAAC,KAAK,MAAM,KAAK;AAI9D,WAAAjB,EAAQ,MAAM,QAAQ,GAAGuc,CAAQ,MACjCvc,EAAQ,MAAM,SAAS,GAAGwc,CAAS,MACnCxc,EAAQ,MAAM,YAAY,mCAAmCyc,CAAK,OAAOC,CAAK,cAAcvb,CAAQ,QACpGnB,EAAQ,MAAM,aAAa,QAEpB;AAAA,MACL,WAAW,EAAE,GAAGyc,GAAO,GAAGC,GAAO,UAAAvb,GAAU,OAAO,EAAA;AAAA,MAClD,YAAY,EAAE,OAAOob,GAAU,QAAQC,EAAA;AAAA,IAAU;AAAA,EAErD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBAAiB9b,GAAwC;AACrE,QAAI;AACF,YAAMA,EAAO,UAAU;AAAA,IACzB,QAAQ;AAAA,IAER;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,sBACNV,GACAsB,GACAga,GACAO,GACAC,GACM;AAEN,SAAK,gBAAgB,oBAAoB9b,CAAO;AAGhD,UAAMH,IAAa,CAAC,uBAAuB;AAC3C,IAAAA,EAAW,KAAK,qBAAqB,GACrCA,EAAW,KAAK,UAAUyB,EAAc,QAAQ,MAAM,GAGtDtB,EAAQ,MAAM,aAAa,QAC3BA,EAAQ,MAAM,YAAYH,EAAW,KAAK,GAAG,GAGzCgc,MAAkB,UAAaC,MAAmB,WACpD9b,EAAQ,MAAM,QAAQ,GAAG6b,CAAa,MACtC7b,EAAQ,MAAM,SAAS,GAAG8b,CAAc,OAI1C,KAAK,qBAAqB9b,GAASsb,CAAc;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WACJqB,GACAnV,GACAlG,GACe;AAEf,QAAI,KAAK,iBAAiBqb,KAAgB,KAAK,UAAUxF,EAAU;AACjE,aAAO,KAAK,aAAA;AAId,QAAI,KAAK,UAAU,YAAYwF,KAAgB,KAAK,UAAUxF,EAAU,UAAU;AAGhF,YAAM,EAAE,WAAW5W,GAAe,YAAYqb,MAC5C,KAAK,yBAAyBe,CAAY;AAC5C,WAAK,gBAAgB,oBAAoBA,CAAY,GAErD,KAAK,WAAW,KAAK;AAAA,QACnBA;AAAA,QACA,KAAK,SAAS;AAAA,QACdpc;AAAA,QACAqb;AAAA,MAAA,GAEF,KAAK,WAAW,MAChB,KAAK,QAAQzE,EAAU,YAEvB,MAAM,KAAK,iBAAiB,KAAK,SAAS,eAAe,GACzD,KAAK,qBAAqB,KAAK,SAAS,SAAS,KAAK,WAAW,kBAAkB,EAAE,GACrF,KAAK,WAAW,MAChB,KAAK,eAAe,MACpB,KAAK,YAAY,MACjB,KAAK,QAAQA,EAAU;AACvB;AAAA,IACF;AAGA,UAAMyF,IAAe,EAAE,KAAK;AAE5B,YAAQ,KAAK,OAAA;AAAA,MACX,KAAKzF,EAAU;AAQb,YANA,KAAK,QAAQA,EAAU,UACvB,KAAK,WAAW,KAAK,oBAAoBwF,GAAcnV,GAAiBlG,CAAa,GAErF,MAAM,KAAK,iBAAiB,KAAK,SAAS,eAAe,GAGrD,KAAK,oBAAoBsb,EAAc;AAE3C,aAAK,eAAeD,GACpB,KAAK,WAAW,MAChB,KAAK,QAAQxF,EAAU;AACvB;AAAA,MAEF,KAAKA,EAAU;AAsBb,YApBA,KAAK,QAAQA,EAAU,iBAGnB,KAAK,gBAAgB,KAAK,cAC5B,KAAK,WAAW,KAAK;AAAA,UACnB,KAAK;AAAA,UACL,KAAK,UAAU;AAAA,QAAA,IAKnB,KAAK,WAAW,KAAK,oBAAoBwF,GAAcnV,GAAiBlG,CAAa,GAGrF,MAAM,QAAQ,IAAI;AAAA,UAChB,KAAK,WAAW,KAAK,iBAAiB,KAAK,SAAS,eAAe,IAAI,QAAQ,QAAA;AAAA,UAC/E,KAAK,iBAAiB,KAAK,SAAS,eAAe;AAAA,QAAA,CACpD,GAGG,KAAK,oBAAoBsb;AAC3B;AAIF,YAAI,KAAK,UAAU;AACjB,gBAAMC,IAAoB,KAAK,SAAS;AACxC,eAAK,qBAAqBA,GAAmB,KAAK,SAAS,cAAc,QAAQ,SAAA,KAAc,EAAE,GACjG,KAAK,WAAW,MAChB,KAAK,oBAAoBA,CAAiB;AAAA,QAC5C;AAEA,aAAK,eAAeF,GACpB,KAAK,WAAW,MAChB,KAAK,QAAQxF,EAAU;AACvB;AAAA,MAEF,KAAKA,EAAU;AAqBb,YAlBI,KAAK,aACP,KAAK,gBAAgB,gBAAgB,KAAK,SAAS,iBAAiB,EAAK,GACzE,KAAK;AAAA,UACH,KAAK,SAAS;AAAA,UACd,KAAK,SAAS;AAAA,UACd,KAAK,WAAW,kBAAkB;AAAA,UAClC,KAAK,WAAW;AAAA,UAChB,KAAK,WAAW;AAAA,QAAA,GAElB,KAAK,WAAW,OAIlB,KAAK,WAAW,KAAK,oBAAoBwF,GAAcnV,GAAiBlG,CAAa,GAErF,MAAM,KAAK,iBAAiB,KAAK,SAAS,eAAe,GAGrD,KAAK,oBAAoBsb,EAAc;AAE3C,aAAK,eAAeD,GACpB,KAAK,WAAW,MAChB,KAAK,QAAQxF,EAAU;AACvB;AAAA,MAEF,KAAKA,EAAU;AAYb,YATA,KAAK,QAAQA,EAAU,iBACvB,KAAK,WAAW,KAAK,oBAAoBwF,GAAcnV,GAAiBlG,CAAa,GAErF,MAAM,QAAQ,IAAI;AAAA,UAChB,KAAK,WAAW,KAAK,iBAAiB,KAAK,SAAS,eAAe,IAAI,QAAQ,QAAA;AAAA,UAC/E,KAAK,iBAAiB,KAAK,SAAS,eAAe;AAAA,QAAA,CACpD,GAGG,KAAK,oBAAoBsb,EAAc;AAE3C,YAAI,KAAK,UAAU;AACjB,gBAAMC,IAAoB,KAAK,SAAS;AACxC,eAAK,qBAAqBA,GAAmB,KAAK,SAAS,cAAc,QAAQ,SAAA,KAAc,EAAE,GACjG,KAAK,WAAW,MAChB,KAAK,oBAAoBA,CAAiB;AAAA,QAC5C;AAEA,aAAK,eAAeF,GACpB,KAAK,WAAW,MAChB,KAAK,QAAQxF,EAAU;AACvB;AAAA,MAEF,KAAKA,EAAU;AAIb,YAAI,KAAK,UAAU,YAAYwF;AAC7B;AAKF,YAAI,KAAK,UAAU,YAAYA,GAAc;AAE3C,gBAAM,EAAE,WAAWpc,GAAe,YAAYqb,MAC5C,KAAK,yBAAyBe,CAAY;AAI5C,cAHA,KAAK,gBAAgB,oBAAoBA,CAAY,GAGjD,KAAK,UAAU;AACjB,kBAAM,EAAE,WAAWG,GAAc,YAAYC,EAAA,IAC3C,KAAK,yBAAyB,KAAK,SAAS,OAAO;AACrD,iBAAK,gBAAgB,oBAAoB,KAAK,SAAS,OAAO,GAC9D,KAAK,WAAW,KAAK;AAAA,cACnB,KAAK,SAAS;AAAA,cACd,KAAK,SAAS;AAAA,cACdD;AAAA,cACAC;AAAA,YAAA;AAAA,UAEJ;AACE,iBAAK,WAAW;AAYlB,cAPA,KAAK,WAAW,KAAK,oBAAoBJ,GAAcnV,GAAiBlG,GAAef,GAAeqb,CAAc,GAEpH,MAAM,QAAQ,IAAI;AAAA,YAChB,KAAK,WAAW,KAAK,iBAAiB,KAAK,SAAS,eAAe,IAAI,QAAQ,QAAA;AAAA,YAC/E,KAAK,iBAAiB,KAAK,SAAS,eAAe;AAAA,UAAA,CACpD,GAEG,KAAK,oBAAoBgB,EAAc;AAE3C,cAAI,KAAK,UAAU;AACjB,kBAAMC,IAAoB,KAAK,SAAS;AACxC,iBAAK,qBAAqBA,GAAmB,KAAK,SAAS,cAAc,QAAQ,SAAA,KAAc,EAAE,GACjG,KAAK,WAAW,MAChB,KAAK,oBAAoBA,CAAiB;AAAA,UAC5C;AAEA,eAAK,eAAeF,GACpB,KAAK,WAAW,MAChB,KAAK,QAAQxF,EAAU;AACvB;AAAA,QACF;AAqBA,YAbI,KAAK,aACP,KAAK,gBAAgB,gBAAgB,KAAK,SAAS,iBAAiB,EAAK,GACzE,KAAK;AAAA,UACH,KAAK,SAAS;AAAA,UACd,KAAK,SAAS;AAAA,UACd,KAAK,SAAS,cAAc,QAAQ,cAAc;AAAA,UAClD,KAAK,SAAS;AAAA,UACd,KAAK,SAAS;AAAA,QAAA,GAEhB,KAAK,WAAW,OAId,KAAK,UAAU;AACjB,gBAAM,EAAE,WAAW5W,GAAe,YAAYqb,EAAA,IAC5C,KAAK,yBAAyB,KAAK,SAAS,OAAO;AACrD,eAAK,gBAAgB,oBAAoB,KAAK,SAAS,OAAO,GAE9D,KAAK,WAAW,KAAK;AAAA,YACnB,KAAK,SAAS;AAAA,YACd,KAAK,SAAS;AAAA,YACdrb;AAAA,YACAqb;AAAA,UAAA;AAAA,QAEJ;AAYA,YATA,KAAK,WAAW,KAAK,oBAAoBe,GAAcnV,GAAiBlG,CAAa,GAGrF,MAAM,QAAQ,IAAI;AAAA,UAChB,KAAK,WAAW,KAAK,iBAAiB,KAAK,SAAS,eAAe,IAAI,QAAQ,QAAA;AAAA,UAC/E,KAAK,iBAAiB,KAAK,SAAS,eAAe;AAAA,QAAA,CACpD,GAGG,KAAK,oBAAoBsb,EAAc;AAE3C,YAAI,KAAK,UAAU;AACjB,gBAAMC,IAAoB,KAAK,SAAS;AACxC,eAAK,qBAAqBA,GAAmB,KAAK,SAAS,cAAc,QAAQ,SAAA,KAAc,EAAE,GACjG,KAAK,WAAW,MAChB,KAAK,oBAAoBA,CAAiB;AAAA,QAC5C;AAEA,aAAK,eAAeF,GACpB,KAAK,WAAW,MAChB,KAAK,QAAQxF,EAAU;AACvB;AAAA,IAAA;AAAA,EAEN;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,eAA8B;AAElC,QAAI,KAAK,UAAUA,EAAU;AAC3B;AAIF,UAAMyF,IAAe,EAAE,KAAK;AAE5B,QAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,WAAW;AAEzC,UAAI,KAAK,YAAY,KAAK,UAAUzF,EAAU,UAAU;AACtD,cAAM,EAAE,WAAW5W,GAAe,YAAYqb,EAAA,IAC5C,KAAK,yBAAyB,KAAK,SAAS,OAAO;AAcrD,YAbA,KAAK,gBAAgB,oBAAoB,KAAK,SAAS,OAAO,GAE9D,KAAK,WAAW,KAAK;AAAA,UACnB,KAAK,SAAS;AAAA,UACd,KAAK,SAAS;AAAA,UACdrb;AAAA,UACAqb;AAAA,QAAA,GAEF,KAAK,WAAW,MAChB,KAAK,QAAQzE,EAAU,YAEvB,MAAM,KAAK,iBAAiB,KAAK,SAAS,eAAe,GAErD,KAAK,oBAAoByF,EAAc;AAE3C,cAAMI,IAAoB,KAAK,SAAS;AACxC,aAAK,qBAAqBA,GAAmB,KAAK,WAAW,kBAAkB,EAAE,GACjF,KAAK,WAAW,MAChB,KAAK,YAAY,MACjB,KAAK,QAAQ7F,EAAU,MACvB,KAAK,oBAAoB6F,CAAiB;AAAA,MAC5C;AACA;AAAA,IACF;AAGA,QAAI,KAAK,UAAU7F,EAAU,mBAEvB,KAAK,UAAU;AACjB,YAAM,EAAE,WAAW5W,GAAe,YAAYqb,EAAA,IAC5C,KAAK,yBAAyB,KAAK,SAAS,OAAO;AACrD,WAAK,gBAAgB,oBAAoB,KAAK,SAAS,OAAO;AAG9D,YAAMqB,IAAkB,KAAK;AAAA,QAC3B,KAAK,SAAS;AAAA,QACd,KAAK,SAAS;AAAA,QACd1c;AAAA,QACAqb;AAAA,MAAA;AASF,UALA,MAAM,QAAQ,IAAI;AAAA,QAChB,KAAK,WAAW,KAAK,iBAAiB,KAAK,SAAS,eAAe,IAAI,QAAQ,QAAA;AAAA,QAC/E,KAAK,iBAAiBqB,EAAgB,eAAe;AAAA,MAAA,CACtD,GAEG,KAAK,oBAAoBL,EAAc;AAG3C,UAAIM,IAAsC;AAC1C,MAAI,KAAK,aACPA,IAAkB,KAAK,SAAS,SAChC,KAAK,qBAAqBA,GAAiB,KAAK,SAAS,cAAc,QAAQ,SAAA,KAAc,EAAE;AAEjG,YAAMC,IAAyBF,EAAgB;AAC/C,WAAK,qBAAqBE,GAAwB,KAAK,SAAS,cAAc,QAAQ,SAAA,KAAc,EAAE,GAEtG,KAAK,WAAW,MAChB,KAAK,WAAW,MAChB,KAAK,eAAe,MACpB,KAAK,YAAY,MACjB,KAAK,QAAQhG,EAAU,MAEnB+F,KAAiB,KAAK,oBAAoBA,CAAe,GAC7D,KAAK,oBAAoBC,CAAsB;AAC/C;AAAA,IACF;AAIF,SAAK,QAAQhG,EAAU;AACvB,UAAMnX,IAAU,KAAK,cACfsB,IAAgB,KAAK,UAAU,eAC/Bga,IAAiB,KAAK,UAAU;AAMtC,IAJA,KAAK,WAAW,KAAK,sBAAsBtb,GAASsB,CAAa,GAEjE,MAAM,KAAK,iBAAiB,KAAK,SAAS,eAAe,GAErD,KAAK,oBAAoBsb,MAE7B,KAAK,qBAAqB5c,GAASsb,CAAc,GACjD,KAAK,WAAW,MAChB,KAAK,eAAe,MACpB,KAAK,YAAY,MACjB,KAAK,QAAQnE,EAAU,MACvB,KAAK,oBAAoBnX,CAAO;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UACJod,GACA5V,GACAlG,GACe;AACf,WAAO,KAAK,WAAW8b,GAAiB5V,GAAiBlG,CAAa;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAsC;AACpC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,UAAUqb,GAAoC;AAC5C,WAAO,KAAK,iBAAiBA,KAAgB,KAAK,UAAUxF,EAAU;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiBwF,GAAoC;AACnD,WAAO,KAAK,UAAU,YAAYA;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAWA,GAAoC;AAC7C,WACE,KAAK,iBAAiBA,KACtB,KAAK,UAAU,YAAYA,KAC3B,KAAK,UAAU,YAAYA;AAAA,EAE/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAchV,GAAsB;AAClC,QAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,aAAa,KAAK,UAAUwP,EAAU,QAAS;AAE/E,UAAMnX,IAAU,KAAK,cACfgc,IAAiB,KAAK,UAAU,gBAGhCnc,IAAuB,CAAC,uBAAuB,GAC/CC,KAAKkc,EAAe,KAAK,KAAKrU,GAC9B5H,IAAIic,EAAe,KAAK;AAC9B,IAAAnc,EAAW,KAAK,aAAaC,CAAC,OAAOC,CAAC,KAAK,GACvCic,EAAe,aAAa,UAC9Bnc,EAAW,KAAK,UAAUmc,EAAe,QAAQ,MAAM,GAGzDhc,EAAQ,MAAM,aAAa,QAC3BA,EAAQ,MAAM,YAAYH,EAAW,KAAK,GAAG;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAgBwd,GAAkBld,IAAmB,KAAW;AAC9D,QAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,aAAa,KAAK,UAAUgX,EAAU,QAAS;AAE/E,UAAMnX,IAAU,KAAK,cACfgc,IAAiB,KAAK,UAAU,gBAGhCnc,IAAuB,CAAC,uBAAuB,GAC/CC,IAAIkc,EAAe,KAAK,GACxBjc,IAAIic,EAAe,KAAK;AAC9B,IAAAnc,EAAW,KAAK,aAAaC,CAAC,OAAOC,CAAC,KAAK,GACvCic,EAAe,aAAa,UAC9Bnc,EAAW,KAAK,UAAUmc,EAAe,QAAQ,MAAM;AAEzD,UAAMsB,IAAoBzd,EAAW,KAAK,GAAG;AAE7C,IAAIwd,KACFrd,EAAQ,MAAM,aAAa,aAAaG,CAAQ,eAChDH,EAAQ,MAAM,YAAYsd,GAE1B,WAAW,MAAM;AACf,MAAI,KAAK,iBAAiBtd,MACxBA,EAAQ,MAAM,aAAa;AAAA,IAE/B,GAAGG,CAAQ,MAEXH,EAAQ,MAAM,aAAa,QAC3BA,EAAQ,MAAM,YAAYsd;AAAA,EAE9B;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AAEZ,IAAI,KAAK,aACP,KAAK,gBAAgB,gBAAgB,KAAK,SAAS,iBAAiB,EAAK,GACzE,KAAK;AAAA,MACH,KAAK,SAAS;AAAA,MACd,KAAK,SAAS;AAAA,MACd,KAAK,SAAS,cAAc,QAAQ,cAAc;AAAA,MAClD,KAAK,SAAS;AAAA,MACd,KAAK,SAAS;AAAA,IAAA,IAId,KAAK,aACP,KAAK,gBAAgB,gBAAgB,KAAK,SAAS,iBAAiB,EAAK,GACzE,KAAK;AAAA,MACH,KAAK,SAAS;AAAA,MACd,KAAK,SAAS;AAAA,MACd,KAAK,WAAW,kBAAkB;AAAA,MAClC,KAAK,WAAW;AAAA,MAChB,KAAK,WAAW;AAAA,IAAA,IAIhB,KAAK,gBAAgB,KAAK,aAC5B,KAAK;AAAA,MACH,KAAK;AAAA,MACL,KAAK,UAAU;AAAA,MACf,KAAK,UAAU;AAAA,MACf,KAAK,UAAU;AAAA,MACf,KAAK,UAAU;AAAA,IAAA,GAInB,KAAK,QAAQnG,EAAU,MACvB,KAAK,eAAe,MACpB,KAAK,YAAY,MACjB,KAAK,WAAW,MAChB,KAAK,WAAW;AAAA,EAClB;AACF;AC/jCA,MAAMoG,KAAqB,IACrBC,KAA2B,KAC3BC,KAAkC,IAClCC,KAAe,KACfC,KAAwB,KACxBC,KAAiC,IAkB1BC,KAAN,MAAMA,GAAY;AAAA,EAgBvB,YAAYC,GAAwBC,GAA2B;AAb/D,SAAQ,UAAmB,IAC3B,KAAQ,aAAgC,MAGxC,KAAQ,uBAA+B,GAUrC,KAAK,YAAYD,GACjB,KAAK,YAAYC,GAGjB,KAAK,kBAAkB,KAAK,iBAAiB,KAAK,IAAI,GACtD,KAAK,iBAAiB,KAAK,gBAAgB,KAAK,IAAI,GACpD,KAAK,gBAAgB,KAAK,eAAe,KAAK,IAAI,GAClD,KAAK,mBAAmB,KAAK,kBAAkB,KAAK,IAAI;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA,EAKA,SAAe;AACb,IAAI,KAAK,YACT,KAAK,UAAU,IAGf,KAAK,UAAU,MAAM,cAAc,SAEnC,KAAK,UAAU,iBAAiB,cAAc,KAAK,iBAAiB,EAAE,SAAS,IAAO,GACtF,KAAK,UAAU,iBAAiB,aAAa,KAAK,gBAAgB,EAAE,SAAS,IAAO,GACpF,KAAK,UAAU,iBAAiB,YAAY,KAAK,eAAe,EAAE,SAAS,IAAM,GACjF,KAAK,UAAU,iBAAiB,eAAe,KAAK,kBAAkB,EAAE,SAAS,IAAM;AAAA,EACzF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,IAAK,KAAK,YACV,KAAK,UAAU,IAGf,KAAK,UAAU,MAAM,cAAc,IAEnC,KAAK,UAAU,oBAAoB,cAAc,KAAK,eAAe,GACrE,KAAK,UAAU,oBAAoB,aAAa,KAAK,cAAc,GACnE,KAAK,UAAU,oBAAoB,YAAY,KAAK,aAAa,GACjE,KAAK,UAAU,oBAAoB,eAAe,KAAK,gBAAgB,GAGnE,KAAK,YAAY,cACnB,KAAK,UAAU,UAAU,EAAK,GAEhC,KAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,SAAK,QAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,iBAA0B;AACxB,WAAO,KAAK,IAAA,IAAQ,KAAK,uBAAuBF,GAAY;AAAA,EAC9D;AAAA,EAEQ,iBAAiBG,GAAqB;AAC5C,QAAIA,EAAE,QAAQ,WAAW,EAAG;AAG5B,SAAK,uBAAuB,KAAK,IAAA;AAEjC,UAAMC,IAAQD,EAAE,QAAQ,CAAC;AACzB,SAAK,aAAa;AAAA,MAChB,QAAQC,EAAM;AAAA,MACd,QAAQA,EAAM;AAAA,MACd,WAAW,YAAY,IAAA;AAAA,MACvB,UAAUA,EAAM;AAAA,MAChB,YAAY;AAAA,MACZ,mBAAmB;AAAA,IAAA;AAAA,EAEvB;AAAA,EAEQ,gBAAgBD,GAAqB;AAC3C,QAAI,CAAC,KAAK,cAAcA,EAAE,QAAQ,WAAW,EAAG;AAEhD,UAAMC,IAAQD,EAAE,QAAQ,CAAC,GACnBE,IAASD,EAAM,UAAU,KAAK,WAAW,QACzCE,IAASF,EAAM,UAAU,KAAK,WAAW;AAG/C,QAAI,KAAK,WAAW,sBAAsB,QAClB,KAAK,KAAKC,IAASA,IAASC,IAASA,CAAM,IAC7C,IAAI;AAGtB,YAAMC,IADW,KAAK,MAAM,KAAK,IAAID,CAAM,GAAG,KAAK,IAAID,CAAM,CAAC,KACjC,MAAM,KAAK;AACxC,WAAK,WAAW,oBAAoBE,KAAYR;AAAA,IAClD;AAIF,QAAI,KAAK,WAAW,sBAAsB,MAKtC,KAAK,WAAW,sBAAsB,IAAM;AAC9C,MAAAI,EAAE,eAAA,GACF,KAAK,WAAW,aAAa,IAC7B,KAAK,WAAW,WAAWC,EAAM;AAGjC,YAAMI,IAAeH,IAASR;AAC9B,WAAK,UAAU,aAAaW,CAAY;AAAA,IAC1C;AAAA,EACF;AAAA,EAEQ,eAAeC,GAAsB;AAC3C,QAAI,CAAC,KAAK,WAAY;AAGtB,SAAK,uBAAuB,KAAK,IAAA;AAEjC,UAAMJ,IAAS,KAAK,WAAW,WAAW,KAAK,WAAW,QACpDK,IAAY,YAAY,IAAA,IAAQ,KAAK,WAAW,WAChDC,IAAW,KAAK,IAAIN,CAAM,IAAIK,GAC9BE,IAAc,KAAK,IAAIP,CAAM;AAEnC,QAAIQ,IAAY;AAGhB,IAAI,KAAK,WAAW,sBAAsB,MAAQ,KAAK,WAAW,eAG9DD,KAAelB,MACdiB,KAAYhB,MAA4BiB,KAAehB,QAGxDiB,IAAY,IACRR,IAAS,IAEX,KAAK,UAAU,OAAA,IAGf,KAAK,UAAU,OAAA,IAMjB,KAAK,WAAW,cAClB,KAAK,UAAU,UAAUQ,CAAS,GAGpC,KAAK,aAAa;AAAA,EACpB;AAAA,EAEQ,kBAAkBJ,GAAsB;AAC9C,IAAI,KAAK,YAAY,cACnB,KAAK,UAAU,UAAU,EAAK,GAEhC,KAAK,aAAa;AAAA,EACpB;AACF;AA1KET,GAAwB,oBAAoB;AARvC,IAAMc,KAANd;AClBA,MAAMe,GAAyC;AAAA,EAUpD,YAAYjf,GAAsC;AAOhD,QAVF,KAAQ,YAAqB,IAC7B,KAAQ,kBAA4B,CAAA,GAGlC,KAAK,SAASA,EAAO,UAAU,IAC/B,KAAK,cAAcA,EAAO,eAAe,6CACzC,KAAK,eAAeA,EAAO,gBAAgB,IAC3C,KAAK,UAAUA,EAAO,WAAW,CAAA,GAG7B,CAAC,KAAK,WAAW,KAAK,QAAQ,WAAW;AAC3C,YAAM,IAAI,MAAM,iEAAiE;AAAA,EAErF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAQ8Y,GAAqC;AACjD,SAAK,kBAAkB,CAAA;AAEvB,eAAWoG,KAAU,KAAK;AACxB,UAAI,aAAaA;AACf,mBAAWC,KAAaD,EAAO,SAAS;AACtC,gBAAME,IAAYF,EAAO,cAAc,SAAYA,EAAO,YAAY,IAChEG,IAAO,MAAM,KAAK,eAAeF,GAAWrG,GAAQsG,CAAS;AACnE,eAAK,gBAAgB,KAAK,GAAGC,CAAI;AAAA,QACnC;AAAA,eACS,WAAWH,GAAQ;AAC5B,cAAMG,IAAO,MAAM,KAAK,UAAUH,EAAO,OAAOpG,CAAM;AACtD,aAAK,gBAAgB,KAAK,GAAGuG,CAAI;AAAA,MACnC;AAGF,SAAK,YAAY;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAuB;AACrB,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,0DAA0D;AAE5E,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAsB;AACpB,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,uDAAuD;AAEzE,WAAO,CAAC,GAAG,KAAK,eAAe;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,aAAsB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAgBF,GAAkC;AAKhD,UAAMG,IAAW;AAAA,MACf;AAAA;AAAA,MACA;AAAA;AAAA,IAAA;AAGF,eAAW1P,KAAW0P,GAAU;AAC9B,YAAMC,IAAQJ,EAAU,MAAMvP,CAAO;AACrC,UAAI2P,KAASA,EAAM,CAAC;AAClB,eAAOA,EAAM,CAAC;AAAA,IAElB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,eAAeJ,GAAmBrG,GAAsBsG,IAAqB,IAAyB;AAClH,UAAMI,IAAW,KAAK,gBAAgBL,CAAS;AAE/C,QAAI,CAACK;AACH,YAAM,IAAI,MAAM,+DAA+D;AAIjF,QAAI,CAAC,KAAK,UAAU,KAAK,WAAW;AAClC,aAAO,KAAK,mBAAmBA,GAAU1G,CAAM;AAGjD,QAAI;AACF,aAAIsG,IACK,MAAM,KAAK,sBAAsBI,GAAU1G,CAAM,IAEjD,MAAM,KAAK,2BAA2B0G,GAAU1G,CAAM;AAAA,IAEjE,SAAS2G,GAAO;AACd,qBAAQ,MAAM,wCAAwCA,CAAK,GAEpD,KAAK,mBAAmBD,GAAU1G,CAAM;AAAA,IACjD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,2BAA2B0G,GAAkB1G,GAAyC;AAClG,UAAM4G,IAAsB,CAAA,GAGtBC,IAAQ,IAAIH,CAAQ,kCAEpBI,IAAM,GAAG,KAAK,WAAW,MAAM,mBAAmBD,CAAK,CAAC,qDAAyB,KAAK,MAAM,IAE5FE,IAAW,MAAM,MAAMD,CAAG;AAEhC,QAAI,CAACC,EAAS;AACZ,YAAM,IAAI,MAAM,uBAAuBA,EAAS,MAAM,IAAIA,EAAS,UAAU,EAAE;AAMjF,UAAMC,KAH4B,MAAMD,EAAS,KAAA,GAGzB,MAAM;AAAA,MAAO,CAAAE,MACnCA,EAAK,SAAS,WAAW,QAAQ,KAAKjH,EAAO,UAAUiH,EAAK,IAAI;AAAA,IAAA;AAGlE,gBAAK,IAAI,SAASD,EAAW,MAAM,qBAAqBN,CAAQ,kBAAkB,GAGlFM,EAAW,QAAQ,CAAAC,MAAQ;AACzB,MAAAL,EAAU,KAAK,uCAAuCK,EAAK,EAAE,QAAQ,GACrE,KAAK,IAAI,eAAeA,EAAK,IAAI,EAAE;AAAA,IACrC,CAAC,GAEML;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,UAAUM,GAAoBlH,GAAyC;AACnF,UAAM4G,IAAsB,CAAA;AAE5B,eAAWO,KAAWD,GAAU;AAC9B,YAAME,IAAS,KAAK,cAAcD,CAAO;AAEzC,UAAI,CAACC,GAAQ;AACX,aAAK,IAAI,8BAA8BD,CAAO,EAAE;AAChD;AAAA,MACF;AAGA,UAAI,KAAK,UAAU,KAAK,WAAW;AACjC,YAAI;AAEF,gBAAME,IAAc,GAAG,KAAK,WAAW,IAAID,CAAM,6BAA6B,KAAK,MAAM,IACnFL,IAAW,MAAM,MAAMM,CAAW;AAExC,cAAIN,EAAS,IAAI;AACf,kBAAMO,IAAW,MAAMP,EAAS,KAAA;AAChC,YAAIO,EAAS,SAAS,WAAW,QAAQ,KAAKtH,EAAO,UAAUsH,EAAS,IAAI,KAC1EV,EAAU,KAAK,uCAAuCQ,CAAM,QAAQ,GACpE,KAAK,IAAI,eAAeE,EAAS,IAAI,EAAE,KAEvC,KAAK,IAAI,4BAA4BA,EAAS,IAAI,KAAKA,EAAS,QAAQ,GAAG;AAAA,UAE/E;AACE,iBAAK,IAAI,qCAAqCF,CAAM,KAAKL,EAAS,MAAM,EAAE;AAAA,QAE9E,SAASJ,GAAO;AACd,eAAK,IAAI,oCAAoCS,CAAM,KAAKT,CAAK;AAAA,QAC/D;AAAA;AAGA,QAAAC,EAAU,KAAK,uCAAuCQ,CAAM,QAAQ;AAAA,IAExE;AAEA,WAAOR;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,cAAcO,GAAgC;AAOpD,QAAI,CAAC,QAAQ,KAAKA,CAAO;AACvB,aAAOA;AAGT,UAAMX,IAAW;AAAA,MACf;AAAA;AAAA,MACA;AAAA;AAAA,MACA;AAAA;AAAA,IAAA;AAGF,eAAW1P,KAAW0P,GAAU;AAC9B,YAAMC,IAAQU,EAAQ,MAAMrQ,CAAO;AACnC,UAAI2P,KAASA,EAAM,CAAC;AAClB,eAAOA,EAAM,CAAC;AAAA,IAElB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,sBAAsBC,GAAkB1G,GAAyC;AAC7F,UAAM4G,IAAsB,CAAA,GAGtBC,IAAQ,IAAIH,CAAQ,kCAGpBI,IAAM,GAAG,KAAK,WAAW,MAAM,mBAAmBD,CAAK,CAAC,qDAAyB,KAAK,MAAM,IAE5FE,IAAW,MAAM,MAAMD,CAAG;AAEhC,QAAI,CAACC,EAAS;AACZ,YAAM,IAAI,MAAM,uBAAuBA,EAAS,MAAM,IAAIA,EAAS,UAAU,EAAE;AAGjF,UAAMQ,IAA4B,MAAMR,EAAS,KAAA,GAG3CC,IAAaO,EAAK,MAAM;AAAA,MAAO,CAAAN,MACnCA,EAAK,SAAS,WAAW,QAAQ,KAAKjH,EAAO,UAAUiH,EAAK,IAAI;AAAA,IAAA,GAG5DO,IAAaD,EAAK,MAAM;AAAA,MAAO,CAAAN,MACnCA,EAAK,aAAa;AAAA,IAAA;AAGpB,SAAK,IAAI,SAASM,EAAK,MAAM,MAAM,0BAA0Bb,CAAQ,EAAE,GAEvEa,EAAK,MAAM,QAAQ,CAAAE,MAAK,KAAK,IAAI,YAAYA,EAAE,IAAI,KAAKA,EAAE,QAAQ,GAAG,CAAC,GAEtE,KAAK,IAAI,KAAKT,EAAW,MAAM,4BAA4B,GAC3D,KAAK,IAAI,KAAKQ,EAAW,MAAM,aAAa,GAG5CR,EAAW,QAAQ,CAAAC,MAAQ;AASzB,MAAAL,EAAU,KAAK,uCAAuCK,EAAK,EAAE,QAAQ,GAErE,KAAK,IAAI,eAAeA,EAAK,IAAI,EAAE;AAAA,IACrC,CAAC;AAGD,eAAWS,KAAUF,GAAY;AAC/B,WAAK,IAAI,kCAAkCE,EAAO,IAAI,EAAE;AACxD,YAAMC,IAAkB,MAAM,KAAK,sBAAsBD,EAAO,IAAI1H,CAAM;AAC1E,MAAA4G,EAAU,KAAK,GAAGe,CAAe;AAAA,IACnC;AAEA,WAAOf;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,mBAAmBF,GAAkBkB,GAA0C;AAO3F,QAAI;AAEF,YAAMvB,IAAY,kDAAkDK,CAAQ,IACtEK,IAAW,MAAM,MAAMV,GAAW,EAAE,MAAM,QAAQ;AAExD,UAAI,CAACU,EAAS;AACZ,cAAM,IAAI,MAAM,2DAA2D;AAG7E,YAAMc,IAAO,MAAMd,EAAS,KAAA,GAGtBe,IAAiB,gCACjBC,IAAU,CAAC,GAAGF,EAAK,SAASC,CAAc,CAAC;AAOjD,aANiB,CAAC,GAAG,IAAI,IAAIC,EAAQ,IAAI,CAAAC,MAAKA,EAAE,CAAC,CAAC,CAAC,CAAC,EAEzB;AAAA,QAAI,CAAAC,MAC7B,8CAA8CA,CAAE;AAAA,MAAA;AAAA,IAKpD,SAAStB,GAAO;AACd,oBAAQ,MAAM,0BAA0BA,CAAK,GACvC,IAAI;AAAA,QACR;AAAA;AAAA;AAAA;AAAA,MAAA;AAAA,IAKJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAgBuB,GAA8B;AAC5C,WAAOA,EAAS,IAAI,CAAAD,MAAM,8CAA8CA,CAAE,EAAE;AAAA,EAC9E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,OAAOE,GAAuB;AACpC,IAAI,KAAK,gBAAgB,OAAO,UAAY,OAC1C,QAAQ,IAAI,GAAGA,CAAI;AAAA,EAEvB;AACF;AC5XO,MAAMC,GAAyC;AAAA,EAWpD,YAAYlhB,GAAiC;AAQ3C,QAXF,KAAQ,YAAqB,IAC7B,KAAQ,kBAA4B,CAAA,GAGlC,KAAK,eAAeA,EAAO,iBAAiB,IAC5C,KAAK,oBAAoBA,EAAO,qBAAqB,KACrD,KAAK,mBAAmBA,EAAO,oBAAoB,QACnD,KAAK,eAAeA,EAAO,gBAAgB,IAC3C,KAAK,UAAUA,EAAO,WAAW,CAAA,GAG7B,CAAC,KAAK,WAAW,KAAK,QAAQ,WAAW;AAC3C,YAAM,IAAI,MAAM,iEAAiE;AAGnF,SAAK,IAAI,8CAA8CA,CAAM;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAQ8Y,GAAqC;AACjD,SAAK,kBAAkB,CAAA,GAEvB,KAAK,IAAI,cAAc,KAAK,QAAQ,MAAM,YAAY;AAGtD,eAAWoG,KAAU,KAAK;AACxB,UAAI;AACF,cAAMG,IAAO,MAAM,KAAK,cAAcH,GAAQpG,CAAM;AACpD,aAAK,gBAAgB,KAAK,GAAGuG,CAAI;AAAA,MACnC,SAASI,GAAO;AACd,gBAAQ,KAAK,6BAA6BP,GAAQO,CAAK;AAAA,MAEzD;AAGF,SAAK,YAAY,IACjB,KAAK,IAAI,uBAAuB,KAAK,gBAAgB,MAAM,WAAW;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAuB;AACrB,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,0DAA0D;AAE5E,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAsB;AACpB,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,uDAAuD;AAEzE,WAAO,CAAC,GAAG,KAAK,eAAe;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,aAAsB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,cAAcP,GAAsBpG,GAAyC;AACzF,WAAKoG,IAKD,UAAUA,IACL,MAAM,KAAK,YAAYA,EAAO,MAAMpG,CAAM,IACxC,UAAUoG,IACZ,MAAM,KAAK,YAAYA,EAAO,MAAMA,EAAO,OAAOpG,CAAM,IACtD,UAAUoG,IACZ,MAAM,KAAK,YAAYA,EAAO,MAAMpG,CAAM,KAEjD,QAAQ,KAAK,yBAAyBoG,CAAM,GACrC,CAAA,MAZP,QAAQ,KAAK,0BAA0BA,CAAM,GACtC,CAAA;AAAA,EAaX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,YAAYG,GAAgBvG,GAAyC;AACjF,QAAI,CAAC,MAAM,QAAQuG,CAAI;AACrB,qBAAQ,KAAK,0BAA0BA,CAAI,GACpC,CAAA;AAGT,UAAM8B,IAAsB,CAAA;AAE5B,eAAWvB,KAAOP,GAAM;AAEtB,YAAM+B,IAAWxB,EAAI,MAAM,GAAG,EAAE,SAASA;AACzC,UAAI,CAAC9G,EAAO,UAAUsI,CAAQ,GAAG;AAC/B,aAAK,IAAI,0BAA0BxB,CAAG,EAAE;AACxC;AAAA,MACF;AAEA,MAAI,KAAK,eACS,MAAM,KAAK,YAAYA,CAAG,IAExCuB,EAAU,KAAKvB,CAAG,IAElB,QAAQ,KAAK,iCAAiCA,CAAG,EAAE,IAIrDuB,EAAU,KAAKvB,CAAG;AAAA,IAEtB;AAEA,WAAOuB;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,YAAYE,GAAkBC,GAAiBxI,GAAyC;AAEpG,QAAI,CAAC,MAAM,QAAQwI,CAAK;AACtB,qBAAQ,KAAK,2BAA2BA,CAAK,GACtC,CAAA;AAGT,UAAMH,IAAsB,CAAA;AAE5B,eAAWpB,KAAQuB,GAAO;AAExB,UAAI,CAACxI,EAAO,UAAUiH,CAAI,GAAG;AAC3B,aAAK,IAAI,2BAA2BA,CAAI,EAAE;AAC1C;AAAA,MACF;AAEA,YAAMH,IAAM,KAAK,aAAayB,GAAUtB,CAAI;AAE5C,MAAI,KAAK,eACS,MAAM,KAAK,YAAYH,CAAG,IAExCuB,EAAU,KAAKvB,CAAG,IAElB,QAAQ,KAAK,kCAAkCA,CAAG,EAAE,IAItDuB,EAAU,KAAKvB,CAAG;AAAA,IAEtB;AAEA,WAAOuB;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,YAAYvB,GAAa9G,GAAyC;AAE9E,SAAK,IAAI,2BAA2B8G,CAAG,EAAE;AAEzC,UAAM2B,IAAa,IAAI,gBAAA,GACjBC,IAAY,WAAW,MAAMD,EAAW,MAAA,GAAS,GAAK;AAE5D,QAAI;AACF,YAAM1B,IAAW,MAAM,MAAMD,GAAK,EAAE,QAAQ2B,EAAW,QAAQ;AAG/D,UAFA,aAAaC,CAAS,GAElB,CAAC3B,EAAS;AACZ,cAAM,IAAI,MAAM,QAAQA,EAAS,MAAM,aAAaD,CAAG,EAAE;AAG3D,YAAMS,IAAO,MAAMR,EAAS,KAAA;AAE5B,UAAI,CAACQ,KAAQ,CAAC,MAAM,QAAQA,EAAK,MAAM;AACrC,cAAM,IAAI,MAAM,6EAA6E;AAG/F,kBAAK,IAAI,0BAA0BA,EAAK,OAAO,MAAM,WAAW,GAGzD,MAAM,KAAK,YAAYA,EAAK,QAAQvH,CAAM;AAAA,IACnD,SAAS2G,GAAO;AAEd,YADA,aAAa+B,CAAS,GAClB/B,aAAiB,SAASA,EAAM,SAAS,eACrC,IAAI,MAAM,mCAAmCG,CAAG,EAAE,IAEpDH;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,YAAYG,GAA+B;AACvD,QAAI,KAAK,qBAAqB;AAC5B,aAAO;AAGT,QAAI,KAAK,qBAAqB;AAE5B,UAAI;AACF,eAAI,OAAO,SAAW,MACpB,IAAI,IAAIA,GAAK,OAAO,SAAS,MAAM,IAEnC,IAAI,IAAIA,CAAG,GAEN;AAAA,MACT,QAAQ;AACN,eAAO;AAAA,MACT;AAMF,QAAI,OAAO,SAAW;AACpB,aAAO;AAMT,QAAI,EAHiBA,EAAI,WAAW,OAAO,SAAS,MAAM,KACrCA,EAAI,WAAW,GAAG;AAIrC,kBAAK,IAAI,6CAA6CA,CAAG,EAAE,GACpD;AAIT,QAAI;AACF,YAAM2B,IAAa,IAAI,gBAAA,GACjBC,IAAY,WAAW,MAAMD,EAAW,MAAA,GAAS,KAAK,iBAAiB,GAEvE1B,IAAW,MAAM,MAAMD,GAAK;AAAA,QAChC,QAAQ;AAAA,QACR,QAAQ2B,EAAW;AAAA,MAAA,CACpB;AAID,aAFA,aAAaC,CAAS,GAElB3B,EAAS,KACJ,MAEP,KAAK,IAAI,yBAAyBD,CAAG,UAAUC,EAAS,MAAM,EAAE,GACzD;AAAA,IAGX,SAASJ,GAAO;AACd,aAAIA,aAAiB,UACfA,EAAM,SAAS,eACjB,KAAK,IAAI,0BAA0BG,CAAG,EAAE,IAExC,KAAK,IAAI,yBAAyBA,CAAG,KAAKH,EAAM,OAAO,IAGpD;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,aAAa4B,GAAkBD,GAA0B;AAE/D,UAAMK,IAAYJ,EAAS,QAAQ,OAAO,EAAE;AAG5C,QAAI,KAAK,cAAcA,CAAQ;AAC7B,aAAO,GAAGI,CAAS,IAAIL,CAAQ;AAIjC,QAAI,OAAO,SAAW;AACpB,aAAO,GAAGK,CAAS,IAAIL,CAAQ;AAGjC,UAAMM,IAAS,OAAO,SAAS,QAGzBC,KADiBN,EAAS,WAAW,GAAG,IAAIA,IAAW,MAAMA,GAClC,QAAQ,OAAO,EAAE;AAElD,WAAO,GAAGK,CAAM,GAAGC,CAAS,IAAIP,CAAQ;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,cAAcxB,GAAsB;AAC1C,QAAI;AACF,iBAAI,IAAIA,CAAG,GACJ;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,OAAOqB,GAAuB;AACpC,IAAI,KAAK,gBAAgB,OAAO,UAAY,OAC1C,QAAQ,IAAI,GAAGA,CAAI;AAAA,EAEvB;AACF;ACrVO,MAAMW,GAAuC;AAAA,EAQlD,YAAY5hB,GAA+B;AAKzC,QARF,KAAQ,YAAqB,IAC7B,KAAQ,kBAA4B,CAAA,GAGlC,KAAK,UAAUA,EAAO,SACtB,KAAK,eAAeA,EAAO,gBAAgB,IAGvC,CAAC,KAAK,WAAW,KAAK,QAAQ,WAAW;AAC3C,YAAM,IAAI,MAAM,+DAA+D;AAGjF,SAAK,IAAI,oCAAoC,KAAK,QAAQ,MAAM,YAAY;AAAA,EAC9E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAQ8Y,GAAqC;AACjD,SAAK,kBAAkB,CAAA,GAEvB,KAAK,IAAI,aAAa,KAAK,QAAQ,MAAM,wBAAwB;AAGjE,UAAM+I,IAAkB,KAAK,QAAQ,IAAI,CAACC,GAAQ/X,MACzC+X,EAAO,QAAQhJ,CAAM,EAAE,KAAK,MAAM;AACvC,WAAK,IAAI,UAAU/O,CAAK,kBAAkB+X,EAAO,cAAc,SAAS;AAAA,IAC1E,CAAC,EAAE,MAAM,CAAArC,MAAS;AAChB,cAAQ,KAAK,UAAU1V,CAAK,uBAAuB0V,CAAK;AAAA,IAE1D,CAAC,CACF;AAED,UAAM,QAAQ,IAAIoC,CAAe;AAGjC,eAAWC,KAAU,KAAK;AACxB,UAAIA,EAAO,cAAc;AACvB,cAAMzC,IAAOyC,EAAO,UAAA;AACpB,aAAK,gBAAgB,KAAK,GAAGzC,CAAI;AAAA,MACnC;AAGF,SAAK,YAAY,IACjB,KAAK,IAAI,iCAAiC,KAAK,gBAAgB,MAAM,eAAe;AAAA,EACtF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAuB;AACrB,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,wDAAwD;AAE1E,WAAO,KAAK,gBAAgB;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAsB;AACpB,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,qDAAqD;AAEvE,WAAO,CAAC,GAAG,KAAK,eAAe;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,aAAsB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,OAAO4B,GAAuB;AACpC,IAAI,KAAK,gBAAgB,OAAO,UAAY,OAC1C,QAAQ,IAAI,qBAAqB,GAAGA,CAAI;AAAA,EAE5C;AACF;ACvGO,MAAMc,GAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQvB,YAAYC,GAAuB;AACjC,SAAK,oBAAoBA,KAAc;AAAA,MACrC;AAAA,MAAO;AAAA,MAAQ;AAAA,MAAO;AAAA,MAAO;AAAA,MAAQ;AAAA,IAAA;AAAA,EAEzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAUZ,GAA2B;AAGnC,UAAMa,IADeb,EAAS,MAAM,GAAG,EAAE,CAAC,EACX,MAAM,GAAG,EAAE,IAAA,GAAO,YAAA;AACjD,WAAOa,IAAY,KAAK,kBAAkB,SAASA,CAAS,IAAI;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,uBAAiC;AAC/B,WAAO,CAAC,GAAG,KAAK,iBAAiB;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAMF;ACxCO,MAAMC,KAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsFvB,SAASC,KAA+B;AAC7C,MAAI,OAAO,WAAa,IAAa;AACrC,QAAMpB,IAAK;AACX,MAAI,SAAS,eAAeA,CAAE,EAAG;AACjC,QAAM7H,IAAQ,SAAS,cAAc,OAAO;AAC5C,EAAAA,EAAM,KAAK6H,GACX7H,EAAM,cAAcgJ,IACpB,SAAS,KAAK,YAAYhJ,CAAK;AACjC;AC5EO,IAAAkJ,KAAA,MAAiB;AAAA,EA6CtB,YAAYvc,IAA6B,IAAI;AAC3C,SAAK,aAAazG,GAAYyG,CAAO,GAGjCA,EAAQ,qBAAqB,eAC/B,KAAK,eAAeA,EAAQ,WAC5B,KAAK,cAAc,SAEnB,KAAK,eAAe,MACpB,KAAK,cAAcA,EAAQ,aAAa,eAI1C,KAAK,eAAe,IACpB,KAAK,gBAAgB,CAAA,GACrB,KAAK,eAAe,CAAA,GACpB,KAAK,qBAAqB,KAC1B,KAAK,oBAAoB,MACzB,KAAK,eAAe,MACpB,KAAK,gBAAgB,MACrB,KAAK,eAAe,CAAA,GACpB,KAAK,gBAAgB,MACrB,KAAK,iBAAiB,GACtB,KAAK,uBAAuB,IAC5B,KAAK,qBAAqB,IAC1B,KAAK,YAAY,MACjB,KAAK,uBAAuB,IAC5B,KAAK,eAAe,MACpB,KAAK,eAAe,MACpB,KAAK,0BAA0B,IAC/B,KAAK,0BAA0B,IAG/B,KAAK,kBAAkB,IAAI9F,GAAgB,KAAK,WAAW,SAAS,GACpE,KAAK,eAAe,IAAIiW,GAAa;AAAA,MACnC,QAAQ,KAAK,WAAW;AAAA,MACxB,OAAO,KAAK,WAAW;AAAA,IAAA,CACxB,GACD,KAAK,aAAa,IAAI0E,GAAW,KAAK,WAAW,YAAY,OAAO,KAAK,iBAAiB,KAAK,WAAW,OAAO,GAGjH,KAAK,gBAAgBtB,GAAqB,KAAK,WAAW,SAAS,OAAO,GAC1E,KAAK,mBAAmB,KAAK,WAAW,SAAS,SAAS,WAC1D,KAAK,iBAAiB,KAAK,WAAW,SAAS,OAAO;AAGtD,UAAMiJ,IAAc,KAAK,WAAW,UAAU,SAASvkB,EAAe,UAAU;AAChF,SAAK,uBAAuB,IAAI2J;AAAA,MAC9B4a;AAAA,MACA,KAAK,WAAW,OAAO;AAAA,IAAA;AAIzB,UAAMC,IAAa,KAAK,WAAW,UAAU;AAC7C,IAAIA,KAAcA,EAAW,SAAS,SACpC,KAAK,sBAAsB,IAAIzY;AAAA,MAC7ByY;AAAA,MACCD,EAAqC,QAAQ,YAAY;AAAA,IAAA,IAG5D,KAAK,sBAAsB,MAI7B,KAAK,WAAW,6BAA6B,CAACE,MAAO;AACnD,WAAK,qBAAqB,eAAeA,CAAsB;AAG/D,YAAMC,IAAMD;AACZ,4BAAsB,MAAM;AAC1B,YAAIC,EAAI,QAAQ,QAAQ,KAAK,KAAK,WAAW,SAAS,OAAO;AAC3D,gBAAMC,IAAM,KAAK,cAAc,QAAQD,CAAG;AAC1C,cAAIC,MAAQ,IAAI;AACd,kBAAMvc,IAAcsc,EAAI,cAClBE,IAAeF,EAAY;AACjC,YAAAtI,GAA8BsI,GAAK,KAAK,WAAW,QAAQ,OAAOtc,GAAawc,CAAW,GAC1FrI,GAAwBmI,GAAK,KAAK,cAAc,GAChD,KAAK,eAAe,EAAE,SAASA,GAAK,QAAQ,KAAK,aAAaC,CAAG,EAAA;AAAA,UACnE;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,CAAC,GAGD,KAAK,cAAc,MAGnB,KAAK,cAAc,KAAK,kBAAA,GAGxB,KAAK,cAAc,KAAK,aAAA,GAGxB,KAAK,cAAc,MACnB,KAAK,YAAY,MACjB,KAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAiC;AACvC,UAAMT,IAAa,KAAK,WAAW,OAAO,SAAS;AACnD,WAAO,IAAID,GAAYC,CAAU;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,eAA4B;AAClC,UAAMW,IAAU,KAAK,WAAW,SAC1BC,IAAS,KAAK,WAAW,OAAO,WAAW,CAAA;AAEjD,QAAI,CAACD,KAAWA,EAAQ,WAAW;AACjC,YAAM,IAAI,MAAM,8DAA8D;AAGhF,UAAME,IAAeF,EAAQ,IAAI,CAAAxY,MAAS,KAAK,sBAAsBA,GAAOyY,CAAM,CAAC;AAEnF,WAAIC,EAAa,WAAW,IACnBA,EAAa,CAAC,IAGhB,IAAIjB,GAAgB;AAAA,MACzB,SAASiB;AAAA,MACT,cAAc,KAAK,WAAW,OAAO,OAAO;AAAA,IAAA,CAC7C;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAAsB1Y,GAAoByY,GAAyC;AACzF,QAAI,YAAYzY,GAAO;AACrB,YAAM2Y,IAAQ3Y,EAAM,QACdjM,IAAkC;AAAA,QACtC,GAAG4kB;AAAA,QACH,cAAcA,EAAM,gBAAgBF,EAAO;AAAA,QAC3C,mBAAmBE,EAAM,qBAAqBF,EAAO;AAAA,QACrD,kBAAkBE,EAAM,oBAAoBF,EAAO;AAAA,QACnD,mBAAmBE,EAAM,qBAAqBF,EAAO;AAAA,QACrD,cAAcE,EAAM,gBAAgB,KAAK,WAAW,OAAO,OAAO;AAAA,MAAA;AAEpE,aAAO,IAAI5B,GAAkBhjB,CAAM;AAAA,IACrC,WAAW,iBAAiBiM,GAAO;AACjC,YAAM2Y,IAAQ3Y,EAAM,aACdjM,IAAuC;AAAA,QAC3C,GAAG4kB;AAAA,QACH,mBAAmBA,EAAM,qBAAqBF,EAAO;AAAA,QACrD,cAAcE,EAAM,gBAAgB,KAAK,WAAW,OAAO,OAAO;AAAA,MAAA;AAEpE,aAAO,IAAI7D,GAAkB/gB,CAAM;AAAA,IACrC;AACE,YAAM,IAAI,MAAM,yBAAyB,KAAK,UAAUiM,CAAK,CAAC,EAAE;AAAA,EAEpE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAsB;AAC1B,QAAI;AAKF,UAHAgY,GAAA,GAGI,KAAK;AACP,aAAK,cAAc,KAAK;AAAA,eAExB,KAAK,cAAc,SAAS,eAAe,KAAK,WAAY,GACxD,CAAC,KAAK;AACR,cAAM,IAAI,MAAM,cAAc,KAAK,WAAW,YAAY;AAK9D,WAAK,YAAY,UAAU,IAAI,gBAAgB,GAC/C,KAAK,YAAY,aAAa,YAAY,GAAG,GAGzC,KAAK,WAAW,YAAY,YAAY,UAAU,OACpD,KAAK,cAAc,IAAInD,GAAY,KAAK,aAAa;AAAA,QACnD,QAAQ,MAAM,KAAK,oBAAA;AAAA,QACnB,QAAQ,MAAM,KAAK,wBAAA;AAAA,QACnB,cAAc,CAAChX,MAAW,KAAK,WAAW,cAAcA,CAAM;AAAA,QAC9D,WAAW,CAAC+W,MAAc;AACxB,UAAKA,IAKH,KAAK,WAAW,gBAAgB,EAAK,IAHrC,KAAK,WAAW,gBAAgB,IAAMf,EAAqB;AAAA,QAK/D;AAAA,MAAA,CACD,IAIH,KAAK,QAAA,GAGL,KAAK,oBAAA,GAGL,KAAK,SAAS,wBAAwB,GACtC,MAAM,KAAK,WAAA;AAAA,IAEb,SAASyB,GAAO;AACd,cAAQ,MAAM,kCAAkCA,CAAK,GACjD,KAAK,WAAWA,aAAiB,SACnC,KAAK,UAAU,mCAAmCA,EAAM,OAAO;AAAA,IAEnE;AAAA,EACF;AAAA,EAEQ,UAAgB;AACtB,UAAMsD,IAAW,KAAK,WAAW;AAGjC,IAAKA,EAAS,mBAGZ,KAAK,aAAa,UAAU,OAAO,yBAAyB,IAF5D,KAAK,aAAa,UAAU,IAAI,yBAAyB,GAMvDA,EAAS,uBACPA,EAAS,kBACX,KAAK,YAAY,KAAK,eAAeA,EAAS,cAAc,GAC5D,KAAK,uBAAuB,OAE5B,KAAK,YAAY,KAAK,4BAAA,GACtB,KAAK,uBAAuB,MAK5BA,EAAS,gBACX,KAAK,UAAU,KAAK,eAAeA,EAAS,YAAY,GACxD,KAAK,qBAAqB,OAE1B,KAAK,UAAU,KAAK,0BAAA,GACpB,KAAK,qBAAqB,KAIxBA,EAAS,qBACPA,EAAS,kBACX,KAAK,YAAY,KAAK,eAAeA,EAAS,cAAc,GAC5D,KAAK,uBAAuB,OAE5B,KAAK,YAAY,KAAK,4BAAA,GACtB,KAAK,uBAAuB,MAK5BA,EAAS,mBACPA,EAAS,qBACX,KAAK,eAAe,KAAK,eAAeA,EAAS,iBAAiB,GAClE,KAAK,0BAA0B,OAE/B,KAAK,eAAe,KAAK,+BAAA,GACzB,KAAK,0BAA0B,KAE7BA,EAAS,qBACX,KAAK,eAAe,KAAK,eAAeA,EAAS,iBAAiB,GAClE,KAAK,0BAA0B,OAE/B,KAAK,eAAe,KAAK,+BAAA,GACzB,KAAK,0BAA0B,KAEjC,KAAK,cAAc,iBAAiB,SAAS,CAAC1E,MAAM;AAClD,MAAAA,EAAE,gBAAA,GACF,KAAK,wBAAA;AAAA,IACP,CAAC,GACD,KAAK,cAAc,iBAAiB,SAAS,CAACA,MAAM;AAClD,MAAAA,EAAE,gBAAA,GACF,KAAK,oBAAA;AAAA,IACP,CAAC;AAAA,EAEL;AAAA,EAEQ,eAAe2E,GAA+C;AACpE,WAAIA,aAAe,cAAoBA,IAChC,SAAS,eAAeA,CAAG;AAAA,EACpC;AAAA,EAEQ,8BAA2C;AACjD,UAAMT,IAAK,SAAS,cAAc,KAAK;AACvC,IAAAA,EAAG,YAAY;AACf,UAAMU,IAAU,SAAS,cAAc,KAAK;AAC5C,IAAAA,EAAQ,YAAY,kBACpBV,EAAG,YAAYU,CAAO;AACtB,UAAMC,IAAO,SAAS,cAAc,GAAG;AACvC,WAAAA,EAAK,cAAc,qBACnBX,EAAG,YAAYW,CAAI,GACnB,KAAK,YAAa,YAAYX,CAAE,GACzBA;AAAA,EACT;AAAA,EAEQ,4BAAyC;AAC/C,UAAMA,IAAK,SAAS,cAAc,KAAK;AACvC,WAAAA,EAAG,YAAY,8BACf,KAAK,YAAa,YAAYA,CAAE,GACzBA;AAAA,EACT;AAAA,EAEQ,8BAA2C;AACjD,UAAMA,IAAK,SAAS,cAAc,KAAK;AACvC,WAAAA,EAAG,YAAY,gCACf,KAAK,YAAa,YAAYA,CAAE,GACzBA;AAAA,EACT;AAAA,EAEQ,iCAA8C;AACpD,UAAMA,IAAK,SAAS,cAAc,QAAQ;AAC1C,WAAAA,EAAG,YAAY,oDACfA,EAAG,cAAc,KACjBA,EAAG,aAAa,cAAc,gBAAgB,GAC9CA,EAAG,aAAa,YAAY,IAAI,GAChC,KAAK,YAAa,YAAYA,CAAE,GACzBA;AAAA,EACT;AAAA,EAEQ,iCAA8C;AACpD,UAAMA,IAAK,SAAS,cAAc,QAAQ;AAC1C,WAAAA,EAAG,YAAY,oDACfA,EAAG,cAAc,KACjBA,EAAG,aAAa,cAAc,YAAY,GAC1CA,EAAG,aAAa,YAAY,IAAI,GAChC,KAAK,YAAa,YAAYA,CAAE,GACzBA;AAAA,EACT;AAAA,EAEQ,sBAA4B;AAElC,IAAI,KAAK,WAAW,YAAY,YAAY,aAAa,MACvD,KAAK,YAAa,iBAAiB,WAAW,CAAClE,MAAqB;AAClE,MAAIA,EAAE,QAAQ,YACZ,KAAK,WAAW,aAAA,GAChB,KAAK,oBAAoB,MACzB,KAAK,aAAa,QAAA,GAClB,KAAK,YAAA,GACL,KAAK,eAAA,GACL,KAAK,mBAAA,KACIA,EAAE,QAAQ,eACnB,KAAK,oBAAA,IACIA,EAAE,QAAQ,cACnB,KAAK,wBAAA,KACKA,EAAE,QAAQ,WAAWA,EAAE,QAAQ,QAAQ,KAAK,iBACtD,KAAK,iBAAiB,KAAK,aAAa,SAAS,KAAK,aAAa,MAAM,GACzEA,EAAE,eAAA;AAAA,IAEN,CAAC,GAGH,SAAS,iBAAiB,SAAS,CAACA,MAAkB;AAEpD,MAAI,KAAK,aAAa,oBAGlB,CAAEA,EAAE,OAAuB,QAAQ,eAAe,KAClD,CAAEA,EAAE,OAAuB,QAAQ,iBAAiB,MACtD,KAAK,WAAW,aAAA,GAChB,KAAK,oBAAoB,MACzB,KAAK,aAAa,QAAA,GAClB,KAAK,YAAA,GACL,KAAK,eAAA,GACL,KAAK,mBAAA;AAAA,IAET,CAAC,GAGD,OAAO,iBAAiB,UAAU,MAAM,KAAK,cAAc;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAA4B;AAClC,QAAI,KAAK,sBAAsB,QAAQ,KAAK,cAAc,WAAW,EAAG;AAExE,UAAM8E,KAAU,KAAK,oBAAoB,KAAK,KAAK,aAAa,QAC1DC,IAAc,KAAK,cAAc;AAAA,MACrC,CAAAb,MAAMA,EAAG,QAAQ,YAAY,OAAOY,CAAM;AAAA,IAAA;AAE5C,QAAI,CAACC,EAAa;AAElB,UAAMpX,IAAS,KAAK,aAAamX,CAAM;AACvC,IAAKnX,MAEL,KAAK,oBAAoBmX,GACzB,KAAK,iBAAiBC,GAAapX,CAAM,GACzC,KAAK,cAAcmX,CAAM,GACzB,KAAK,eAAA,GACL,KAAK,mBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKQ,0BAAgC;AACtC,QAAI,KAAK,sBAAsB,QAAQ,KAAK,cAAc,WAAW,EAAG;AAExE,UAAME,KAAU,KAAK,oBAAoB,IAAI,KAAK,aAAa,UAAU,KAAK,aAAa,QACrFC,IAAc,KAAK,cAAc;AAAA,MACrC,CAAAf,MAAMA,EAAG,QAAQ,YAAY,OAAOc,CAAM;AAAA,IAAA;AAE5C,QAAI,CAACC,EAAa;AAElB,UAAMtX,IAAS,KAAK,aAAaqX,CAAM;AACvC,IAAKrX,MAEL,KAAK,oBAAoBqX,GACzB,KAAK,iBAAiBC,GAAatX,CAAM,GACzC,KAAK,cAAcqX,CAAM,GACzB,KAAK,eAAA,GACL,KAAK,mBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAqB;AAC3B,IAAK,KAAK,iBAEN,KAAK,kBAAkB,QACzB,aAAa,KAAK,aAAa,GAGjC,KAAK,gBAAgB,OAAO,WAAW,MAAM;AAC3C,YAAME,IAAY,KAAK,eAAA;AAEvB,MAAIA,MAAc,KAAK,sBACrB,KAAK,SAAS,6CAA6CA,CAAS,0BAA0B,GAE9F,KAAK,WAAA,KAEL,KAAK,SAAS,uCAAuC;AAAA,IAEzD,GAAG,GAAG;AAAA,EACR;AAAA,EAEQ,iBAAyB;AAC/B,UAAMxY,IAAQ,OAAO,YACfyY,IAAa,KAAK,WAAW,OAAO,YAIpC7M,IADS,KAAK,WAAW,MAAM,QACb,WAAW;AAInC,WAAK6M,IAODzY,KAASyY,EAAW,OAAO,WACtB,KAAK,IAAI,KAAK7M,CAAO,IAE1B5L,KAASyY,EAAW,OAAO,WACtB,KAAK,IAAI,KAAK7M,CAAO,IAEvB,KAAK,IAAI,KAAKA,CAAO,IAXtB5L,KAAS,MAAY,KAAK,IAAI,KAAK4L,CAAO,IAC1C5L,KAAS,OAAa,KAAK,IAAI,KAAK4L,CAAO,IACxC,KAAK,IAAI,KAAKA,CAAO;AAAA,EAUhC;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAAwD;AAC9D,WAAK,KAAK,cAGH;AAAA,MACL,OAAO,KAAK,YAAY;AAAA,MACxB,QAAQ,KAAK,YAAY,gBAAgB,OAAO,cAAc;AAAA,IAAA,IAJvD,EAAE,OAAO,OAAO,YAAY,QAAQ,OAAO,cAAc,IAAA;AAAA,EAMpE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,aAA4B;AACxC,QAAI;AACF,WAAK,YAAY,EAAI,GACrB,KAAK,UAAA,GACL,KAAK,gBAAA,GAGL,MAAM,KAAK,YAAY,QAAQ,KAAK,WAAW;AAG/C,YAAM9L,IAAa,KAAK,YAAY,aAAA;AACpC,UAAI6U,IAAY,KAAK,YAAY,UAAA;AAEjC,UAAI7U,MAAe,GAAG;AACpB,aAAK,UAAU,kBAAkB,GACjC,KAAK,YAAY,EAAK;AACtB;AAAA,MACF;AAGA,YAAMhD,IAAkB,KAAK,mBAAA,GACvByO,IAAmB,KAAK,eAAA,GACxBH,IAAgB,OAAO;AAE7B,WAAK,SAAS,oCAAoCtO,EAAgB,KAAK,IAAIA,EAAgB,MAAM,cAAcgD,CAAU,mBAAmByL,CAAgB,IAAI;AAEhK,YAAMmN,IAAe,KAAK,aAAa;AAAA,QACrC5b;AAAA,QACAgD;AAAA,QACAyL;AAAA,QACAH;AAAA,MAAA;AAGF,WAAK,SAAS,kCAAkCsN,EAAa,MAAM,IAAI,GAEvE,MAAM,KAAK,iBAAiB/D,GAAW+D,EAAa,MAAM,GAE1D,KAAK,YAAY,EAAK,GACtB,KAAK,eAAe;AAAA,IAEtB,SAAShE,GAAO;AACd,cAAQ,MAAM,yBAAyBA,CAAK,GACxCA,aAAiB,SACnB,KAAK,UAAUA,EAAM,WAAW,wBAAwB,GAE1D,KAAK,YAAY,EAAK;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAYwB,GAAuB;AACzC,IAAI,KAAK,WAAW,OAAO,OAAO,WAAW,OAAO,UAAY,OAC9D,QAAQ,IAAI,GAAGA,CAAI;AAAA,EAEvB;AAAA,EAEA,MAAc,iBAAiBvB,GAAqBxZ,GAAoC;AACtF,QAAI,CAAC,KAAK,YAAa;AAEvB,UAAM2B,IAAkB,KAAK,mBAAA;AAC7B,SAAK,qBAAqB3B;AAG1B,UAAMwd,IAAoB,KAAK,gBAGzB5Y,IAAU,KAAK,aAAa,eAAe4U,EAAU,QAAQ7X,GAAiB,EAAE,aAAa3B,GAAoB;AACvH,SAAK,eAAe4E,GAEpB,KAAK,eAAe,CAAA;AACpB,QAAI8B,IAAiB;AAGrB,UAAM+W,IAAe,CAACnB,MAA0B;AAC9C,MAAK,KAAK,gBAEV,KAAK,YAAY,YAAYA,CAAG,GAChC,KAAK,cAAc,KAAKA,CAAG,GAE3B,sBAAsB,MAAM;AAa1B,YAZKA,EAAI,aAETA,EAAI,MAAM,UAAU,KAAK,cAAc,WAAW,KAGzBA,EAAI,QAAQ,WAClC,KAAK,qBAAqB,oBAAA,KAC1B,KAAK,qBAAqB,mBAAA,KAC1B,KAAK,qBAAqB,gBAAA,KAC1BA,EAAI,QAAQ,kBAAkBA,EAAI,QAAQ,YAC1CA,EAAI,QAAQ,eAAeA,EAAI,QAAQ,QAEpB;AAEpB,gBAAM1c,IAAgB;AAAA,YACpB,GAAG,WAAW0c,EAAI,QAAQ,MAAO;AAAA,YACjC,GAAG,WAAWA,EAAI,QAAQ,MAAO;AAAA,UAAA,GAE7Bzc,IAAc;AAAA,YAClB,GAAG,WAAWyc,EAAI,QAAQ,IAAK;AAAA,YAC/B,GAAG,WAAWA,EAAI,QAAQ,IAAK;AAAA,UAAA,GAE3Bvc,IAAa,WAAWuc,EAAI,QAAQ,UAAW,GAC/Ctc,IAAc,WAAWsc,EAAI,QAAQ,WAAY,GACjDhhB,IAAW,WAAWghB,EAAI,QAAQ,QAAS,GAC3CjhB,IAAQ,WAAWihB,EAAI,QAAQ,KAAM,GACrCnc,IAAgBmc,EAAI,QAAQ,gBAC9B,WAAWA,EAAI,QAAQ,aAAa,IACpChhB,GACE+E,IAAaic,EAAI,QAAQ,aAC3B,WAAWA,EAAI,QAAQ,UAAU,IACjCjhB,GACEqiB,IAAS,KAAK,qBAAqB,UAAA;AAEzC,UAAAhe,GAAY;AAAA,YACV,SAAS4c;AAAA,YACT,eAAA1c;AAAA,YACA,aAAAC;AAAA,YACA,YAAY,KAAK,qBAAqB,cAAA;AAAA,YACtC,UAAU6d,EAAO;AAAA,YACjB,YAAA3d;AAAA,YACA,aAAAC;AAAAA,YACA,UAAA1E;AAAA,YACA,OAAAD;AAAA,YACA,gBAAgB,KAAK,qBAAqB,kBAAA;AAAA,YAC1C,eAAA8E;AAAA,YACA,aAAa,KAAK,qBAAqB,eAAA;AAAA,YACvC,YAAAE;AAAA,UAAA,CACD;AAAA,QACH,OAAO;AAEL,gBAAMsd,IAAiBrB,EAAI,QAAQ,kBAAkB;AACrD,UAAAA,EAAI,MAAM,YAAYqB;AAAA,QACxB;AAGA,cAAMC,IAAW,SAAStB,EAAI,QAAQ,WAAW,GAAG;AACpD,YAAI,KAAK,WAAW,OAAO,OAAO,WAAWsB,IAAW,GAAG;AACzD,gBAAMD,IAAiBrB,EAAI,QAAQ,kBAAkB;AACrD,kBAAQ,IAAI,SAASsB,CAAQ,iBAAiB;AAAA,YAC5C,MAAMtB,EAAI,MAAM;AAAA,YAChB,KAAKA,EAAI,MAAM;AAAA,YACf,OAAOA,EAAI,MAAM;AAAA,YACjB,QAAQA,EAAI,MAAM;AAAA,YAClB,eAAeA,EAAI;AAAA,YACnB,gBAAgBA,EAAI;AAAA,YACpB,WAAWqB;AAAA,YACX,UAAU,KAAK,qBAAqB,YAAA;AAAA,UAAY,CACjD;AAAA,QACH;AAGA,YAAI,KAAK,qBAAqB;AAC5B,gBAAM7Z,IAAgB,KAAK,qBAAqB,UAAA,EAAY;AAC5D,eAAK,oBAAoB,SAASwY,GAAKsB,GAAU,KAAK,cAAc,QAAQ9Z,CAAa;AAAA,QAC3F;AAAA,MACF,CAAC,GAED4C;AAAA,IACF,GAEMmX,IAAuB,MAAM;AAIjC,UAHA,KAAK,SAAS,uCAAuC,KAAK,WAAW,UAAU,MAAM,OAAO,GAGxF,CAAC,KAAK,WAAW,UAAU,MAAM,SAAS;AAC5C,eAAO,KAAK,aAAa,SAAS,KAAG;AACnC,gBAAMvB,IAAM,KAAK,aAAa,MAAA;AAC9B,UAAIA,KACFmB,EAAanB,CAAG;AAAA,QAEpB;AACA;AAAA,MACF;AAIA,MAAI,KAAK,kBAAkB,QACzB,cAAc,KAAK,aAAa,GAElC,KAAK,gBAAgB,OAAO,YAAY,MAAM;AAE5C,YAAIkB,MAAsB,KAAK,gBAAgB;AAC7C,UAAI,KAAK,kBAAkB,SACzB,cAAc,KAAK,aAAa,GAChC,KAAK,gBAAgB;AAEvB;AAAA,QACF;AAEA,YAAI,KAAK,aAAa,SAAS,GAAG;AAChC,gBAAMlB,IAAM,KAAK,aAAa,MAAA;AAC9B,UAAIA,KACFmB,EAAanB,CAAG;AAAA,QAEpB;AAEA,QAAI5V,KAAkB8S,EAAU,UAAU,KAAK,aAAa,WAAW,KACjE,KAAK,kBAAkB,SACzB,cAAc,KAAK,aAAa,GAChC,KAAK,gBAAgB;AAAA,MAG3B,GAAG,KAAK,WAAW,UAAU,MAAM,QAAQ;AAAA,IAC7C;AAGA,QAAI,0BAA0B,UAAU,KAAK,aAAa;AACxD,YAAMsE,IAAW,IAAI,qBAAqB,CAACrB,MAAY;AACrD,QAAAA,EAAQ,QAAQ,CAAAxY,MAAS;AACvB,UAAIA,EAAM,mBACR4Z,EAAA,GACAC,EAAS,WAAA;AAAA,QAEb,CAAC;AAAA,MACH,GAAG,EAAE,WAAW,KAAK,YAAY,QAAQ;AACzC,MAAAA,EAAS,QAAQ,KAAK,WAAW;AAAA,IACnC;AACE,MAAAD,EAAA;AAIF,IAAI,KAAK,WAAW,OAAO,OAAO,WAAW,KAAK,gBAEhD,KAAK,YAAY,iBAAiB,sBAAsB,EAAE,QAAQ,CAAAxB,MAAMA,EAAG,QAAQ,GAEnFzX,EAAQ,QAAQ,CAACkB,GAAQjC,MAAU;AACjC,YAAMka,IAAS,SAAS,cAAc,KAAK;AAC3C,MAAAA,EAAO,YAAY,uBACnBA,EAAO,MAAM,WAAW,YACxBA,EAAO,MAAM,QAAQ,QACrBA,EAAO,MAAM,SAAS,QACtBA,EAAO,MAAM,eAAe,OAC5BA,EAAO,MAAM,kBAAkB,OAC/BA,EAAO,MAAM,SAAS,oBACtBA,EAAO,MAAM,SAAS,QACtBA,EAAO,MAAM,gBAAgB;AAE7B,YAAMhc,IAAU+D,EAAO,GACjB9D,IAAU8D,EAAO;AACvB,MAAAiY,EAAO,MAAM,OAAO,GAAGhc,IAAU,CAAC,MAClCgc,EAAO,MAAM,MAAM,GAAG/b,IAAU,CAAC,MACjC+b,EAAO,QAAQ,SAASla,CAAK,aAAa,KAAK,MAAM9B,CAAO,CAAC,KAAK,KAAK,MAAMC,CAAO,CAAC,KACrF,KAAK,YAAa,YAAY+b,CAAM;AAAA,IACtC,CAAC,IAIHvE,EAAU,QAAQ,CAACE,GAAK7V,MAAU;AAChC,YAAMyY,IAAM,SAAS,cAAc,KAAK;AAExC,MAAAA,EAAI,iBAAiB,eACrBA,EAAI,UAAU,IAAI,cAAc,GAC5B,KAAK,WAAW,YAAY,aAAa,OAC3CA,EAAI,YAAY,KAElBA,EAAI,QAAQ,UAAU,OAAOzY,CAAK,GAClCyY,EAAI,QAAQ,cAAc;AAE1B,YAAMxW,IAASlB,EAAQf,CAAK;AAC5B,MAAAyY,EAAI,MAAM,WAAW,YACrBA,EAAI,MAAM,QAAQ,QAClBA,EAAI,MAAM,SAAS,GAAGtc,CAAW,MACjCsc,EAAI,MAAM,OAAO,GAAGxW,EAAO,CAAC,MAC5BwW,EAAI,MAAM,MAAM,GAAGxW,EAAO,CAAC,MAGvBA,EAAO,WAAQwW,EAAI,MAAM,SAAS,OAAOxW,EAAO,MAAM,IAK1DqO,GAAwBmI,GAAK,KAAK,gBAAgB,GAIlDA,EAAI,iBAAiB,cAAc,MAAM;AAEvC,YADA,KAAK,eAAe,EAAE,SAASA,GAAK,QAAAxW,EAAA,GAChC,CAAC,KAAK,WAAW,WAAWwW,CAAG,GAAG;AAEpC,gBAAME,IAAeF,EAAY;AACjC,UAAAtI,GAA8BsI,GAAK,KAAK,WAAW,SAAS,OAAOtc,GAAawc,CAAW,GAC3FrI,GAAwBmI,GAAK,KAAK,cAAc;AAAA,QAClD;AAAA,MACF,CAAC,GAEDA,EAAI,iBAAiB,cAAc,MAAM;AAEvC,YADA,KAAK,eAAe,MAChB,CAAC,KAAK,WAAW,WAAWA,CAAG,GAAG;AAEpC,gBAAME,IAAeF,EAAY;AACjC,UAAAtI,GAA8BsI,GAAK,KAAK,WAAW,SAAS,SAAStc,GAAawc,CAAW,GAC7FlI,GAA2BgI,GAAK,KAAK,cAAc,GACnDnI,GAAwBmI,GAAK,KAAK,gBAAgB;AAAA,QACpD;AAAA,MACF,CAAC,GAEDA,EAAI,iBAAiB,SAAS,CAACnE,MAAkB;AAC/C,QAAAA,EAAE,gBAAA,GACF,KAAK,iBAAiBmE,GAAKxW,CAAM;AAAA,MACnC,CAAC,GAEDwW,EAAI,MAAM,UAAU,KACpBA,EAAI,MAAM,aAAa,KAAK,qBAAqB,iBAAA,GAEjDA,EAAI,SAAS,MAAM;AAEjB,YAAIkB,MAAsB,KAAK;AAC7B;AAGF,cAAMtT,IAAcoS,EAAI,eAAeA,EAAI,eACrC0B,IAAgBhe,IAAckK;AAGpC,QAAAoS,EAAI,QAAQ,eAAe,QACtB,OAAe,kBAClB,QAAQ,IAAI,YAAYzY,CAAK,6BAA6B7D,CAAW,mBAAmBge,CAAa,EAAE,GAIzG1B,EAAI,MAAM,QAAQ,GAAG0B,CAAa,MAGjC1B,EAAY,sBAAsB0B,GAClC1B,EAAY,cAAcpS,GAI3B8J,GAA8BsI,GAAK,KAAK,WAAW,SAAS,SAAStc,GAAage,CAAa;AAG/F,cAAMvc,IAAgB,EAAE,GAAGqE,EAAO,GAAG,GAAGA,EAAO,EAAA,GACzCpE,IAAY,EAAE,OAAOsc,GAAe,QAAQhe,EAAA,GAE5CJ,IAAgB,KAAK,qBAAqB;AAAA,UAC9C6B;AAAA,UACAC;AAAA,UACAC;AAAA,UACAkC;AAAA,UACA2V,EAAU;AAAA,QAAA,GAINrZ,IAAgB,KAAK,qBAAqB,uBAAuB2F,EAAO,QAAQ,GAGhFzF,IAAa,KAAK,qBAAqB,oBAAoByF,EAAO,KAAK,GAEvE6X,IAAiB,KAAK,qBAAqB;AAAA,UAC/C7X,EAAO;AAAA,UACPA,EAAO;AAAA,UACPkY;AAAA,UACAhe;AAAA,QAAA,GAEIoW,IAAiB,KAAK,qBAAqB;AAAA,UAC/CxW;AAAA,UACA6B;AAAA,UACAqE,EAAO;AAAA,UACPA,EAAO;AAAA,UACPkY;AAAA,UACAhe;AAAA,UACAG;AAAA,UACAE;AAAA,QAAA;AAGF,QAAI,KAAK,WAAW,OAAO,OAAO,WAAWwD,IAAQ,KACnD,QAAQ,IAAI,SAASA,CAAK,KAAK;AAAA,UAC7B,eAAApC;AAAA,UACA,WAAAC;AAAA,UACA,MAAMoE,EAAO;AAAA,UACb,KAAKA,EAAO;AAAA,UACZ,gBAAA6X;AAAA,UACA,eAAAK;AAAA,UACA,gBAAgBhe;AAAA,QAAA,CACjB,GAGHsc,EAAI,MAAM,YAAYlG,GACtBkG,EAAI,QAAQ,iBAAiBqB,IAIJ,KAAK,qBAAqB,oBAAA,KACjD,KAAK,qBAAqB,mBAAA,KAC1B,KAAK,qBAAqB,gBAAA,KAC1Bxd,MAAkB2F,EAAO,YACzBzF,MAAeyF,EAAO,WAGtBwW,EAAI,QAAQ,SAAS,OAAO1c,EAAc,CAAC,GAC3C0c,EAAI,QAAQ,SAAS,OAAO1c,EAAc,CAAC,GAC3C0c,EAAI,QAAQ,OAAO,OAAO7a,EAAc,CAAC,GACzC6a,EAAI,QAAQ,OAAO,OAAO7a,EAAc,CAAC,GACzC6a,EAAI,QAAQ,aAAa,OAAO0B,CAAa,GAC7C1B,EAAI,QAAQ,cAAc,OAAOtc,CAAW,GAC5Csc,EAAI,QAAQ,WAAW,OAAOxW,EAAO,QAAQ,GAC7CwW,EAAI,QAAQ,QAAQ,OAAOxW,EAAO,KAAK,GACvCwW,EAAI,QAAQ,gBAAgB,OAAOnc,CAAa,GAChDmc,EAAI,QAAQ,aAAa,OAAOjc,CAAU,IAG5C,KAAK,aAAa,KAAKic,CAAG;AAAA,MAC5B,GAEAA,EAAI,UAAU,MAAM5V,KAGpB4V,EAAI,MAAM5C;AAAA,IACZ,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,iBAAiB5C,GAAgCmH,GAA4C;AACzG,QAAI,CAAC,KAAK,YAAa;AAEvB,UAAMC,IAAY,KAAK,WAAW,UAAUpH,CAAY,GAClDqH,IAA0B;AAAA,MAC9B,OAAO,KAAK,YAAY;AAAA,MACxB,QAAQ,KAAK,YAAY;AAAA,IAAA;AAG3B,QAAID;AACF,YAAM,KAAK,WAAW,aAAA,GACtB,KAAK,oBAAoB,MACzB,KAAK,aAAa,QAAA,GAClB,KAAK,YAAA,GACL,KAAK,eAAA,GACL,KAAK,mBAAA;AAAA,SACA;AAEL,WAAK,qBAAqB,cAAcpH,CAAY;AAGpD,YAAM/G,IAAU+G,EAAa,QAAQ;AACrC,WAAK,oBAAoB/G,MAAY,SAAY,SAASA,GAAS,EAAE,IAAI,MACzE,KAAK,aAAa,OAAA,GAClB,KAAK,aAAa,MAAM,EAAE,eAAe,IAAM,GAC/C,MAAM,KAAK,WAAW,WAAW+G,GAAcqH,GAAQF,CAAc,GACjE,KAAK,sBAAsB,QAC7B,KAAK,cAAc,KAAK,iBAAiB,GAE3C,KAAK,eAAA,GACL,KAAK,mBAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAwB;AAEtB,IAAI,KAAK,kBAAkB,SACzB,cAAc,KAAK,aAAa,GAChC,KAAK,gBAAgB,OAGvB,KAAK,kBACL,KAAK,eAAe,CAAA,GAEpB,KAAK,mBAAA,GAED,KAAK,eACP,KAAK,YAAY,iBAAiB,qCAAqC,EAAE,QAAQ,CAAA5B,MAAMA,EAAG,QAAQ,GAEpG,KAAK,gBAAgB,CAAA,GACrB,KAAK,eAAe,CAAA,GACpB,KAAK,oBAAoB,MACzB,KAAK,eAAe,MACpB,KAAK,aAAa,MAAA,GAClB,KAAK,WAAW,MAAA,GAChB,KAAK,qBAAqB,QAAA,GAC1B,KAAK,eAAe;AAAA,EACtB;AAAA,EAEQ,YAAY+B,GAAqB;AACvC,IAAI,CAAC,KAAK,WAAW,GAAG,sBAAsB,CAAC,KAAK,cAChDA,IACF,KAAK,UAAU,UAAU,OAAO,eAAe,IAE/C,KAAK,UAAU,UAAU,IAAI,eAAe;AAAA,EAEhD;AAAA,EAEQ,UAAUC,GAAuB;AACvC,IAAK,KAAK,YACV,KAAK,QAAQ,cAAcA,GAC3B,KAAK,QAAQ,UAAU,OAAO,eAAe;AAAA,EAC/C;AAAA,EAEQ,YAAkB;AACxB,IAAI,KAAK,WACP,KAAK,QAAQ,UAAU,IAAI,eAAe;AAAA,EAE9C;AAAA,EAEQ,cAAcxa,GAAqB;AACzC,IAAI,CAAC,KAAK,WAAW,GAAG,oBAAoB,CAAC,KAAK,cAClD,KAAK,UAAU,cAAc,GAAGA,IAAQ,CAAC,OAAO,KAAK,cAAc,MAAM,IACzE,KAAK,UAAU,UAAU,OAAO,eAAe;AAAA,EACjD;AAAA,EAEQ,cAAoB;AAC1B,IAAI,KAAK,aACP,KAAK,UAAU,UAAU,IAAI,eAAe;AAAA,EAEhD;AAAA,EAEQ,qBAA2B;AACjC,SAAK,aAAa,UAAU,IAAI,kBAAkB;AAAA,EACpD;AAAA,EAEQ,qBAA2B;AACjC,SAAK,aAAa,UAAU,OAAO,kBAAkB;AAAA,EACvD;AAAA,EAEQ,iBAAuB;AAC7B,SAAK,cAAc,UAAU,OAAO,eAAe,GACnD,KAAK,cAAc,UAAU,OAAO,eAAe;AAAA,EACrD;AAAA,EAEQ,iBAAuB;AAC7B,SAAK,cAAc,UAAU,IAAI,eAAe,GAChD,KAAK,cAAc,UAAU,IAAI,eAAe;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,SAAK,gBAAA,GAED,KAAK,wBAAwB,KAAK,cACpC,KAAK,UAAU,OAAA,GACf,KAAK,YAAY,OAEf,KAAK,sBAAsB,KAAK,YAClC,KAAK,QAAQ,OAAA,GACb,KAAK,UAAU,OAEb,KAAK,wBAAwB,KAAK,cACpC,KAAK,UAAU,OAAA,GACf,KAAK,YAAY,OAEf,KAAK,2BAA2B,KAAK,iBACvC,KAAK,aAAa,OAAA,GAClB,KAAK,eAAe,OAElB,KAAK,2BAA2B,KAAK,iBACvC,KAAK,aAAa,OAAA,GAClB,KAAK,eAAe,OAGlB,KAAK,kBAAkB,QACzB,aAAa,KAAK,aAAa,GAEjC,KAAK,aAAa,QAAA,GAClB,KAAK,qBAAqB,QAAA,GAC1B,KAAK,sBAAsB;AAAA,EAC7B;AACF;ACxkCO,MAAMya,KAAaC;AAAA,EACxB,SAAoB,EAAE,WAAArK,GAAW,OAAAlB,GAAO,GAAGrT,EAAA,GAAWmd,GAAK;AACzD,UAAM0B,IAAeC,GAAuB,IAAI,GAC1CC,IAAcD,GAA8B,IAAI;AAEtD,WAAAE,GAAoB7B,GAAK,OAAO;AAAA,MAC9B,IAAI,WAAW;AACb,eAAO4B,EAAY;AAAA,MACrB;AAAA,IAAA,EACA,GAEFE,GAAU,MAAM;AACd,UAAI,CAACJ,EAAa,QAAS;AAE3B,YAAMK,IAAQ,IAAIC,GAAe;AAAA,QAC/B,WAAWN,EAAa;AAAA,QACxB,GAAG7e;AAAA,MAAA,CACJ;AACD,aAAA+e,EAAY,UAAUG,GAEtBA,EAAM,KAAA,EAAO,MAAM,CAACE,MAAQ;AAC1B,gBAAQ,MAAM,2BAA2BA,CAAG;AAAA,MAC9C,CAAC,GAEM,MAAM;AACX,QAAAF,EAAM,QAAA,GACNH,EAAY,UAAU;AAAA,MACxB;AAAA,IAEF,GAAG,CAAC,KAAK,UAAU/e,CAAO,CAAC,CAAC,GAErB,gBAAAqf,GAAC,OAAA,EAAI,KAAKR,GAAc,WAAAtK,GAAsB,OAAAlB,GAAc;AAAA,EACrE;AACF;"}
|
|
1
|
+
{"version":3,"file":"react.js","sources":["../src/config/defaults.ts","../src/engines/AnimationEngine.ts","../src/engines/PathAnimator.ts","../src/engines/EntryAnimationEngine.ts","../src/engines/IdleAnimationEngine.ts","../src/layouts/RandomPlacementLayout.ts","../src/layouts/RadialPlacementLayout.ts","../src/layouts/GridPlacementLayout.ts","../src/layouts/SpiralPlacementLayout.ts","../src/layouts/ClusterPlacementLayout.ts","../src/layouts/WavePlacementLayout.ts","../src/utils/hexagonGeometry.ts","../src/layouts/HoneycombPlacementLayout.ts","../src/engines/LayoutEngine.ts","../src/config/types.ts","../src/utils/clipPathGenerator.ts","../src/utils/styleUtils.ts","../src/engines/ZoomEngine.ts","../src/engines/SwipeEngine.ts","../src/loaders/GoogleDriveLoader.ts","../src/loaders/StaticImageLoader.ts","../src/loaders/CompositeLoader.ts","../src/loaders/ImageFilter.ts","../src/styles/functionalStyles.ts","../src/ImageCloud.ts","../src/react/index.tsx"],"sourcesContent":["/**\n * Default configuration for Image Gallery\n * Centralized settings for animation, layout, and API configuration\n */\n\nimport type { ImageCloudConfig, ImageCloudOptions, DeepPartial, ImageStylingConfig, ImageStyleState, ShadowPreset, WaveAlgorithmConfig, HoneycombAlgorithmConfig, RadialAlgorithmConfig, BouncePathConfig, ElasticPathConfig, WavePathConfig, BouncePreset, ElasticPreset, WavePathPreset, EntryPathConfig, EntryRotationConfig, EntryScaleConfig, ImageConfig, ImageSizingConfig, ImageRotationConfig, ImageVarianceConfig, ResponsiveBreakpoints, SharedLoaderConfig, ConfigSection, LoaderEntry, DebugConfig, IdleWiggleConfig, IdlePulseConfig, IdleBlinkConfig, IdleSpinConfig, IdleAnimationConfig } from './types';\n\n/**\n * Shadow presets for image styling\n */\nexport const SHADOW_PRESETS: Record<ShadowPreset, string> = Object.freeze({\n 'none': 'none',\n 'sm': '0 2px 4px rgba(0,0,0,0.1)',\n 'md': '0 4px 16px rgba(0,0,0,0.4)',\n 'lg': '0 8px 32px rgba(0,0,0,0.5)',\n 'glow': '0 0 30px rgba(255,255,255,0.6)'\n});\n\n/**\n * Bounce path presets - overshoot and settle animations\n */\nexport const BOUNCE_PRESETS: Record<BouncePreset, BouncePathConfig> = Object.freeze({\n energetic: Object.freeze({ overshoot: 0.25, bounces: 2, decayRatio: 0.5 }),\n playful: Object.freeze({ overshoot: 0.15, bounces: 1, decayRatio: 0.5 }),\n subtle: Object.freeze({ overshoot: 0.08, bounces: 1, decayRatio: 0.5 })\n});\n\n/**\n * Elastic path presets - spring-like oscillation animations\n */\nexport const ELASTIC_PRESETS: Record<ElasticPreset, ElasticPathConfig> = Object.freeze({\n gentle: Object.freeze({ stiffness: 150, damping: 30, mass: 1, oscillations: 2 }),\n bouncy: Object.freeze({ stiffness: 300, damping: 15, mass: 1, oscillations: 4 }),\n wobbly: Object.freeze({ stiffness: 180, damping: 12, mass: 1.5, oscillations: 5 }),\n snappy: Object.freeze({ stiffness: 400, damping: 25, mass: 0.8, oscillations: 2 })\n});\n\n/**\n * Wave path presets - sinusoidal path animations\n */\nexport const WAVE_PATH_PRESETS: Record<WavePathPreset, WavePathConfig> = Object.freeze({\n gentle: Object.freeze({ amplitude: 30, frequency: 1.5, decay: true, decayRate: 0.9, phase: 0 }),\n playful: Object.freeze({ amplitude: 50, frequency: 2.5, decay: true, decayRate: 0.7, phase: 0 }),\n serpentine: Object.freeze({ amplitude: 60, frequency: 3, decay: false, decayRate: 1, phase: 0 }),\n flutter: Object.freeze({ amplitude: 20, frequency: 4, decay: true, decayRate: 0.5, phase: 0 })\n});\n\n/**\n * Default path configuration (linear - no special path effects)\n */\nexport const DEFAULT_PATH_CONFIG: EntryPathConfig = Object.freeze({\n type: 'linear' as const\n});\n\n/**\n * Default entry rotation configuration (no rotation animation)\n */\nexport const DEFAULT_ENTRY_ROTATION: EntryRotationConfig = Object.freeze({\n mode: 'none' as const\n});\n\n/**\n * Default entry scale configuration (no scale animation)\n */\nexport const DEFAULT_ENTRY_SCALE: EntryScaleConfig = Object.freeze({\n mode: 'none' as const\n});\n\n\n/**\n * Default image styling configuration\n */\nexport const DEFAULT_STYLING: ImageStylingConfig = Object.freeze({\n default: Object.freeze({\n border: Object.freeze({\n width: 0,\n color: '#000000',\n radius: 0,\n style: 'solid' as const\n }),\n shadow: 'none' as ShadowPreset,\n clipPath: undefined,\n filter: Object.freeze({}),\n opacity: 1,\n cursor: 'pointer',\n outline: Object.freeze({\n width: 0,\n color: '#000000',\n style: 'solid' as const,\n offset: 0\n })\n }),\n hover: Object.freeze({\n shadow: 'none' as ShadowPreset\n }),\n focused: Object.freeze({\n shadow: 'none' as ShadowPreset\n })\n});\n\n/**\n * Default radial layout configuration\n */\nexport const DEFAULT_RADIAL_CONFIG: RadialAlgorithmConfig = Object.freeze({\n tightness: 1.0,\n});\n\n/**\n * Default wave layout configuration\n */\nexport const DEFAULT_WAVE_CONFIG: WaveAlgorithmConfig = Object.freeze({\n rows: 1,\n amplitude: 100,\n frequency: 2,\n phaseShift: 0,\n synchronization: 'offset' as const\n // Note: Image rotation along wave is now controlled via image.rotation.mode = 'tangent'\n});\n\nexport const DEFAULT_HONEYCOMB_CONFIG: HoneycombAlgorithmConfig = Object.freeze({\n spacing: 0\n});\n\n/**\n * Default responsive breakpoints for layout\n */\nexport const DEFAULT_RESPONSIVE_BREAKPOINTS: ResponsiveBreakpoints = Object.freeze({\n mobile: Object.freeze({ maxWidth: 767 }),\n tablet: Object.freeze({ maxWidth: 1199 })\n});\n\n/**\n * Default image sizing configuration\n */\nexport const DEFAULT_IMAGE_SIZING: ImageSizingConfig = Object.freeze({\n mode: 'adaptive' as const, // Default to adaptive sizing\n minSize: 50, // Adaptive mode minimum\n maxSize: 400, // Adaptive mode maximum\n variance: Object.freeze({\n min: 1.0, // No variance by default\n max: 1.0\n })\n});\n\n/**\n * Default image rotation configuration\n */\nexport const DEFAULT_IMAGE_ROTATION: ImageRotationConfig = Object.freeze({\n mode: 'none' as const,\n range: Object.freeze({\n min: -15,\n max: 15\n })\n});\n\n/**\n * Default image configuration\n */\nexport const DEFAULT_IMAGE_CONFIG: ImageConfig = Object.freeze({\n sizing: DEFAULT_IMAGE_SIZING,\n rotation: DEFAULT_IMAGE_ROTATION\n});\n\n/**\n * Default configuration object\n * Frozen to prevent accidental modifications\n */\n/**\n * Default shared loader configuration\n */\nexport const DEFAULT_SHARED_LOADER_CONFIG: SharedLoaderConfig = Object.freeze({\n validateUrls: true,\n validationTimeout: 5000,\n validationMethod: 'head' as const,\n allowedExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp']\n});\n\n/**\n * Default debug configuration\n */\nexport const DEFAULT_DEBUG_CONFIG: DebugConfig = Object.freeze({\n enabled: false,\n centers: false,\n loaders: false\n});\n\nexport const DEFAULT_IDLE_WIGGLE: IdleWiggleConfig = Object.freeze({ maxAngle: 5, speed: 2000, sync: 'random' as const });\nexport const DEFAULT_IDLE_PULSE: IdlePulseConfig = Object.freeze({ minScale: 0.95, maxScale: 1.05, speed: 2400, sync: 'random' as const });\nexport const DEFAULT_IDLE_BLINK: IdleBlinkConfig = Object.freeze({ onRatio: 0.7, speed: 3000, style: 'snap' as const });\nexport const DEFAULT_IDLE_SPIN: IdleSpinConfig = Object.freeze({ speed: 4000, direction: 'clockwise' as const });\nexport const DEFAULT_IDLE_CONFIG: IdleAnimationConfig = Object.freeze({ type: 'none' as const });\n\nexport const DEFAULT_CONFIG: ImageCloudConfig = Object.freeze({\n // Loader configuration (always an array, composite behavior is implicit)\n loaders: [] as LoaderEntry[],\n\n // Shared loader settings and debug config\n config: Object.freeze({\n loaders: DEFAULT_SHARED_LOADER_CONFIG,\n debug: DEFAULT_DEBUG_CONFIG\n }),\n\n // Image sizing and rotation configuration\n image: DEFAULT_IMAGE_CONFIG,\n\n // Pattern-based layout configuration\n layout: Object.freeze({\n algorithm: 'radial' as const,\n scaleDecay: 0, // No decay by default (0-1 for radial/spiral)\n responsive: DEFAULT_RESPONSIVE_BREAKPOINTS,\n targetCoverage: 0.6, // Target 60% of container area\n densityFactor: 1.0, // Default density\n spacing: Object.freeze({\n padding: 50 // padding from viewport edges\n })\n }),\n\n // Pattern-based animation configuration\n animation: Object.freeze({\n duration: 600, // milliseconds\n easing: Object.freeze({\n default: 'cubic-bezier(0.4, 0.0, 0.2, 1)', // smooth easing\n bounce: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)', // bounce effect\n focus: 'cubic-bezier(0.4, 0.0, 0.2, 1)' // focus/zoom easing\n }),\n queue: Object.freeze({\n enabled: true, // When false, all images display simultaneously\n interval: 150, // ms between processing queue items (when enabled)\n }),\n entry: Object.freeze({\n start: Object.freeze({\n position: 'nearest-edge' as const, // Default to nearest edge (current behavior)\n offset: 100, // pixels beyond edge\n circular: Object.freeze({\n radius: '120%', // 120% of container diagonal\n distribution: 'even' as const\n })\n }),\n timing: Object.freeze({\n duration: 600 // ms\n }),\n easing: 'cubic-bezier(0.25, 1, 0.5, 1)', // smooth deceleration\n path: DEFAULT_PATH_CONFIG,\n rotation: DEFAULT_ENTRY_ROTATION,\n scale: DEFAULT_ENTRY_SCALE\n }),\n idle: DEFAULT_IDLE_CONFIG\n }),\n\n // Pattern-based interaction configuration\n interaction: Object.freeze({\n focus: Object.freeze({\n scalePercent: 0.8, // 80% of container height\n zIndex: 1000,\n animationDuration: undefined // Use default animation duration\n }),\n navigation: Object.freeze({\n keyboard: true,\n swipe: true,\n mouseWheel: undefined // STUB: Not implemented yet\n }),\n dragging: true\n }),\n\n // UI configuration\n ui: Object.freeze({\n showLoadingSpinner: false,\n showImageCounter: false,\n showNavButtons: false,\n showFocusOutline: false\n }),\n\n // Image styling\n styling: DEFAULT_STYLING\n});\n\n/**\n * Deep merge a single style state (border, filter, outline, etc.)\n */\nfunction deepMergeStyleState(\n base: ImageStyleState | undefined,\n override: Partial<ImageStyleState> | undefined\n): ImageStyleState {\n if (!base) return override as ImageStyleState || {};\n if (!override) return { ...base };\n\n const merged: ImageStyleState = { ...base };\n\n // Merge border\n if (override.border !== undefined) {\n merged.border = { ...base.border, ...override.border };\n }\n\n // Merge per-side borders\n if (override.borderTop !== undefined) {\n merged.borderTop = { ...base.borderTop, ...override.borderTop };\n }\n if (override.borderRight !== undefined) {\n merged.borderRight = { ...base.borderRight, ...override.borderRight };\n }\n if (override.borderBottom !== undefined) {\n merged.borderBottom = { ...base.borderBottom, ...override.borderBottom };\n }\n if (override.borderLeft !== undefined) {\n merged.borderLeft = { ...base.borderLeft, ...override.borderLeft };\n }\n\n // Merge filter\n if (override.filter !== undefined) {\n merged.filter = { ...base.filter, ...override.filter };\n }\n\n // Merge outline\n if (override.outline !== undefined) {\n merged.outline = { ...base.outline, ...override.outline };\n }\n\n // Override simple properties\n if (override.shadow !== undefined) merged.shadow = override.shadow;\n if (override.clipPath !== undefined) merged.clipPath = override.clipPath;\n if (override.opacity !== undefined) merged.opacity = override.opacity;\n if (override.cursor !== undefined) merged.cursor = override.cursor;\n if (override.className !== undefined) merged.className = override.className;\n if (override.objectFit !== undefined) merged.objectFit = override.objectFit;\n if (override.aspectRatio !== undefined) merged.aspectRatio = override.aspectRatio;\n\n // Override per-corner border radius\n if (override.borderRadiusTopLeft !== undefined) merged.borderRadiusTopLeft = override.borderRadiusTopLeft;\n if (override.borderRadiusTopRight !== undefined) merged.borderRadiusTopRight = override.borderRadiusTopRight;\n if (override.borderRadiusBottomRight !== undefined) merged.borderRadiusBottomRight = override.borderRadiusBottomRight;\n if (override.borderRadiusBottomLeft !== undefined) merged.borderRadiusBottomLeft = override.borderRadiusBottomLeft;\n\n return merged;\n}\n\n/**\n * Deep merge styling config with proper state inheritance\n * - hover inherits from default, then applies overrides\n * - focused inherits from default, then applies overrides\n */\nfunction deepMergeStyling(\n defaults: ImageStylingConfig,\n userStyling: Partial<ImageStylingConfig> | undefined\n): ImageStylingConfig {\n if (!userStyling) return { ...defaults };\n\n // First, merge the default state\n const mergedDefault = deepMergeStyleState(defaults.default, userStyling.default);\n\n // Hover inherits from merged default, then user hover overrides\n const mergedHover = deepMergeStyleState(\n deepMergeStyleState(mergedDefault, defaults.hover),\n userStyling.hover\n );\n\n // Focused inherits from merged default, then user focused overrides\n const mergedFocused = deepMergeStyleState(\n deepMergeStyleState(mergedDefault, defaults.focused),\n userStyling.focused\n );\n\n return {\n default: mergedDefault,\n hover: mergedHover,\n focused: mergedFocused\n };\n}\n\n/**\n * Deep merge utility for config objects\n * Merges user config with default config\n */\n/**\n * Deep merge image config with validation\n */\nfunction deepMergeImageConfig(\n defaults: ImageConfig,\n userImage: Partial<ImageConfig> | undefined\n): ImageConfig {\n if (!userImage) return { ...defaults };\n\n const merged: ImageConfig = { ...defaults };\n\n // Deep merge sizing config\n if (userImage.sizing !== undefined) {\n merged.sizing = {\n ...defaults.sizing,\n ...userImage.sizing\n };\n\n // Deep merge variance with validation (min: 0.25-1, max: 1-1.75)\n if (userImage.sizing.variance) {\n const userVariance = userImage.sizing.variance as ImageVarianceConfig;\n const validMin = userVariance.min !== undefined && userVariance.min >= 0.25 && userVariance.min <= 1\n ? userVariance.min\n : defaults.sizing?.variance?.min ?? 1.0;\n const validMax = userVariance.max !== undefined && userVariance.max >= 1 && userVariance.max <= 1.75\n ? userVariance.max\n : defaults.sizing?.variance?.max ?? 1.0;\n merged.sizing!.variance = { min: validMin, max: validMax };\n }\n }\n\n // Deep merge rotation config\n if (userImage.rotation !== undefined) {\n merged.rotation = {\n ...defaults.rotation,\n ...userImage.rotation\n };\n\n // Deep merge rotation range with validation\n if (userImage.rotation.range) {\n const userRange = userImage.rotation.range;\n const validMin = userRange.min !== undefined && userRange.min >= -180 && userRange.min <= 0\n ? userRange.min\n : defaults.rotation?.range?.min ?? -15;\n const validMax = userRange.max !== undefined && userRange.max >= 0 && userRange.max <= 180\n ? userRange.max\n : defaults.rotation?.range?.max ?? 15;\n merged.rotation!.range = { min: validMin, max: validMax };\n }\n }\n\n return merged;\n}\n\n/**\n * Convert legacy layout.rotation config to new image.rotation format\n * This provides backward compatibility with the old API\n */\nfunction convertLegacyRotationConfig(userConfig: DeepPartial<ImageCloudConfig>): Partial<ImageConfig> | undefined {\n const legacyRotation = (userConfig.layout as any)?.rotation;\n if (!legacyRotation) return undefined;\n\n // Legacy format: { enabled: boolean, range: { min, max } }\n if ('enabled' in legacyRotation) {\n return {\n rotation: {\n mode: legacyRotation.enabled ? 'random' : 'none',\n range: legacyRotation.range\n }\n };\n }\n\n return undefined;\n}\n\n/**\n * Convert legacy layout.sizing.variance config to new image.sizing.variance format\n */\nfunction convertLegacyVarianceConfig(userConfig: DeepPartial<ImageCloudConfig>): Partial<ImageConfig> | undefined {\n const legacyVariance = (userConfig.layout as any)?.sizing?.variance;\n if (!legacyVariance) return undefined;\n\n return {\n sizing: {\n mode: 'adaptive', // Legacy variance config implies adaptive mode\n variance: legacyVariance\n }\n };\n}\n\nexport function mergeConfig(\n userConfig: ImageCloudOptions = {}\n): ImageCloudConfig {\n // Convert legacy configs to new format\n const legacyRotation = convertLegacyRotationConfig(userConfig as any);\n const legacyVariance = convertLegacyVarianceConfig(userConfig as any);\n\n // Combine user image config with converted legacy configs\n // User's explicit image config takes precedence over legacy conversions\n let combinedImageConfig: Partial<ImageConfig> | undefined = userConfig.image as Partial<ImageConfig> | undefined;\n if (legacyRotation || legacyVariance) {\n combinedImageConfig = {\n ...(legacyVariance || {}),\n ...(legacyRotation || {}),\n ...combinedImageConfig\n };\n // Deep merge the rotation config if both exist\n if (combinedImageConfig.rotation && legacyRotation?.rotation && userConfig.image?.rotation) {\n combinedImageConfig.rotation = {\n ...legacyRotation.rotation,\n ...(userConfig.image as any).rotation\n };\n }\n }\n\n // Build loaders array: images shorthand prepended, then explicit loaders\n const loaders = [...(userConfig.loaders ?? [])];\n if (userConfig.images && userConfig.images.length > 0) {\n loaders.unshift({\n static: {\n sources: [{ urls: userConfig.images }]\n }\n });\n }\n\n // Merge shared loader config\n const sharedLoaderConfig: SharedLoaderConfig = {\n ...DEFAULT_SHARED_LOADER_CONFIG,\n ...(userConfig.config?.loaders ?? {})\n };\n\n const mergedConfig: ConfigSection = {\n loaders: sharedLoaderConfig\n };\n\n const merged: ImageCloudConfig = {\n loaders,\n config: mergedConfig,\n image: deepMergeImageConfig(DEFAULT_IMAGE_CONFIG, combinedImageConfig),\n layout: { ...DEFAULT_CONFIG.layout },\n animation: { ...DEFAULT_CONFIG.animation },\n interaction: { ...DEFAULT_CONFIG.interaction },\n ui: { ...DEFAULT_CONFIG.ui },\n styling: deepMergeStyling(DEFAULT_STYLING, userConfig.styling as Partial<ImageStylingConfig> | undefined)\n };\n\n // Deep merge layout config\n if (userConfig.layout) {\n merged.layout = {\n ...DEFAULT_CONFIG.layout,\n ...userConfig.layout\n } as any;\n\n // Deep merge responsive breakpoints config\n if (userConfig.layout.responsive) {\n merged.layout.responsive = {\n ...DEFAULT_CONFIG.layout.responsive!,\n mobile: userConfig.layout.responsive.mobile\n ? { ...DEFAULT_CONFIG.layout.responsive!.mobile, ...userConfig.layout.responsive.mobile }\n : DEFAULT_CONFIG.layout.responsive!.mobile,\n tablet: userConfig.layout.responsive.tablet\n ? { ...DEFAULT_CONFIG.layout.responsive!.tablet, ...userConfig.layout.responsive.tablet }\n : DEFAULT_CONFIG.layout.responsive!.tablet\n };\n }\n\n // Deep merge spacing config\n if (userConfig.layout.spacing) {\n merged.layout.spacing = {\n ...DEFAULT_CONFIG.layout.spacing,\n ...userConfig.layout.spacing\n };\n }\n }\n\n // Deep merge animation config\n if (userConfig.animation) {\n merged.animation = {\n ...DEFAULT_CONFIG.animation,\n ...userConfig.animation\n } as any;\n\n // Deep merge easing config\n if (userConfig.animation.easing) {\n merged.animation.easing = {\n ...DEFAULT_CONFIG.animation.easing,\n ...userConfig.animation.easing\n };\n }\n\n // Deep merge queue config\n if (userConfig.animation.queue) {\n merged.animation.queue = {\n ...DEFAULT_CONFIG.animation.queue,\n ...userConfig.animation.queue\n };\n }\n\n // Deep merge entry animation config\n if (userConfig.animation.entry) {\n merged.animation.entry = {\n ...DEFAULT_CONFIG.animation.entry!,\n ...userConfig.animation.entry,\n start: userConfig.animation.entry.start\n ? {\n ...DEFAULT_CONFIG.animation.entry!.start,\n ...userConfig.animation.entry.start,\n circular: userConfig.animation.entry.start.circular\n ? { ...DEFAULT_CONFIG.animation.entry!.start.circular, ...userConfig.animation.entry.start.circular }\n : DEFAULT_CONFIG.animation.entry!.start.circular\n }\n : DEFAULT_CONFIG.animation.entry!.start,\n timing: userConfig.animation.entry.timing\n ? { ...DEFAULT_CONFIG.animation.entry!.timing, ...userConfig.animation.entry.timing }\n : DEFAULT_CONFIG.animation.entry!.timing,\n path: userConfig.animation.entry.path\n ? { ...DEFAULT_PATH_CONFIG, ...userConfig.animation.entry.path }\n : DEFAULT_CONFIG.animation.entry!.path,\n rotation: userConfig.animation.entry.rotation\n ? { ...DEFAULT_ENTRY_ROTATION, ...userConfig.animation.entry.rotation }\n : DEFAULT_CONFIG.animation.entry!.rotation,\n scale: userConfig.animation.entry.scale\n ? { ...DEFAULT_ENTRY_SCALE, ...userConfig.animation.entry.scale }\n : DEFAULT_CONFIG.animation.entry!.scale\n };\n }\n\n // Deep merge idle animation config\n if (userConfig.animation.idle) {\n merged.animation.idle = {\n ...DEFAULT_IDLE_CONFIG,\n ...userConfig.animation.idle\n };\n }\n }\n\n // Deep merge interaction config\n if (userConfig.interaction) {\n merged.interaction = {\n ...DEFAULT_CONFIG.interaction,\n ...userConfig.interaction\n } as any;\n\n // Deep merge focus config\n if (userConfig.interaction.focus) {\n merged.interaction.focus = {\n ...DEFAULT_CONFIG.interaction.focus,\n ...userConfig.interaction.focus\n };\n }\n\n // Deep merge navigation config\n if (userConfig.interaction.navigation) {\n merged.interaction.navigation = {\n ...DEFAULT_CONFIG.interaction.navigation,\n ...userConfig.interaction.navigation\n };\n }\n\n }\n\n // Merge ui config (with backwards compat for old rendering.ui)\n const legacyUi = (userConfig as any).rendering?.ui;\n if (legacyUi) {\n console.warn('[ImageCloud] rendering.ui is deprecated. Use top-level ui instead.');\n }\n merged.ui = {\n ...DEFAULT_CONFIG.ui,\n ...legacyUi,\n ...userConfig.ui\n };\n\n // Merge debug config\n merged.config.debug = {\n ...DEFAULT_DEBUG_CONFIG,\n ...(userConfig.config?.debug ?? {})\n };\n\n // Honeycomb forces default/hover clip path to hexagon height-relative for edge-to-edge tiling\n if (merged.layout.algorithm === 'honeycomb' && merged.styling) {\n const honeycombClip = { shape: 'hexagon' as const, mode: 'height-relative' as const };\n merged.styling = {\n ...merged.styling,\n default: { ...merged.styling.default, clipPath: honeycombClip },\n hover: { ...merged.styling.hover, clipPath: honeycombClip },\n // focused: untouched — user config respected\n };\n }\n\n return merged;\n}\n\n/**\n * Resolve bounce path config from preset and overrides\n */\nexport function resolveBounceConfig(\n preset?: BouncePreset,\n overrides?: Partial<BouncePathConfig>\n): BouncePathConfig {\n const base = preset ? BOUNCE_PRESETS[preset] : BOUNCE_PRESETS.playful;\n return { ...base, ...overrides };\n}\n\n/**\n * Resolve elastic path config from preset and overrides\n */\nexport function resolveElasticConfig(\n preset?: ElasticPreset,\n overrides?: Partial<ElasticPathConfig>\n): ElasticPathConfig {\n const base = preset ? ELASTIC_PRESETS[preset] : ELASTIC_PRESETS.gentle;\n return { ...base, ...overrides };\n}\n\n/**\n * Resolve wave path config from preset and overrides\n */\nexport function resolveWavePathConfig(\n preset?: WavePathPreset,\n overrides?: Partial<WavePathConfig>\n): WavePathConfig {\n const base = preset ? WAVE_PATH_PRESETS[preset] : WAVE_PATH_PRESETS.gentle;\n return { ...base, ...overrides };\n}\n\n/**\n * Debug logger\n */\nexport function debugLog(config: ImageCloudConfig, ...args: unknown[]): void {\n if (config.config.debug?.enabled && typeof console !== 'undefined') {\n console.log(...args);\n }\n}","/**\n * AnimationEngine.ts\n * Handles smooth animations with easing for the image cloud\n *\n * Public API:\n * - animateTransform(element, properties, duration, easing)\n * - animateTransformCancellable(element, from, to, duration, easing) - Web Animations API\n * - cancelAnimation(handle, commitStyle) - Cancel and optionally keep current position\n * - getCurrentTransform(element) - Get current transform state mid-animation\n * - hasActiveAnimation(element) - Check if element has active animation\n * - resetTransform(element, originalState)\n * - clearTransition(element)\n * - wait(ms)\n */\n\nimport type { AnimationConfig, TransformParams, ImageLayout, AnimationHandle, AnimationSnapshot } from '../config/types';\n\nexport class AnimationEngine {\n private config: AnimationConfig;\n private activeAnimations: Map<HTMLElement, AnimationHandle> = new Map();\n private animationIdCounter = 0;\n\n constructor(config: AnimationConfig) {\n this.config = config;\n }\n\n /**\n * Build transform string from transform params\n * Always starts with centering transform to match image positioning system\n */\n private buildTransformString(params: TransformParams): string {\n const transforms: string[] = ['translate(-50%, -50%)'];\n\n if (params.x !== undefined || params.y !== undefined) {\n const x = params.x ?? 0;\n const y = params.y ?? 0;\n transforms.push(`translate(${x}px, ${y}px)`);\n }\n\n if (params.rotation !== undefined) {\n transforms.push(`rotate(${params.rotation}deg)`);\n }\n\n if (params.scale !== undefined) {\n transforms.push(`scale(${params.scale})`);\n }\n\n return transforms.join(' ');\n }\n\n /**\n * Start a cancellable transform animation using Web Animations API\n * @param element - The element to animate\n * @param from - Starting transform state\n * @param to - Ending transform state\n * @param duration - Animation duration in ms (optional)\n * @param easing - CSS easing function (optional)\n * @returns AnimationHandle that can be used to cancel or query the animation\n */\n animateTransformCancellable(\n element: HTMLElement,\n from: TransformParams,\n to: TransformParams,\n duration: number | null = null,\n easing: string | null = null\n ): AnimationHandle {\n // Cancel any existing animation on this element\n this.cancelAllAnimations(element);\n\n const animDuration = duration ?? this.config.duration;\n const animEasing = easing ?? this.config.easing.default;\n\n const fromTransform = this.buildTransformString(from);\n const toTransform = this.buildTransformString(to);\n\n // Clear any CSS transitions to avoid conflicts\n element.style.transition = 'none';\n\n // Create Web Animation\n const animation = element.animate(\n [\n { transform: fromTransform },\n { transform: toTransform }\n ],\n {\n duration: animDuration,\n easing: animEasing,\n fill: 'forwards' // Keep final state after animation\n }\n );\n\n const handle: AnimationHandle = {\n id: `anim-${++this.animationIdCounter}`,\n element,\n animation,\n fromState: from,\n toState: to,\n startTime: performance.now(),\n duration: animDuration\n };\n\n this.activeAnimations.set(element, handle);\n\n // Clean up when animation finishes (normally or cancelled)\n animation.finished\n .then(() => {\n // Apply final transform as inline style for consistency\n element.style.transform = toTransform;\n this.activeAnimations.delete(element);\n })\n .catch(() => {\n // Animation was cancelled - cleanup handled by cancelAnimation\n this.activeAnimations.delete(element);\n });\n\n return handle;\n }\n\n /**\n * Cancel an active animation\n * @param handle - The animation handle to cancel\n * @param commitStyle - If true, keeps current position; if false, no style change\n * @returns Snapshot of where the animation was when cancelled\n */\n cancelAnimation(handle: AnimationHandle, commitStyle: boolean = true): AnimationSnapshot {\n const snapshot = this.getCurrentTransform(handle.element);\n\n // Cancel the Web Animation\n handle.animation.cancel();\n\n if (commitStyle) {\n // Apply current position as inline style\n const currentTransform = this.buildTransformString({\n x: snapshot.x,\n y: snapshot.y,\n rotation: snapshot.rotation,\n scale: snapshot.scale\n });\n handle.element.style.transform = currentTransform;\n }\n\n this.activeAnimations.delete(handle.element);\n\n return snapshot;\n }\n\n /**\n * Cancel all animations on an element\n * Uses Web Animations API to find and cancel ALL animations, not just tracked ones\n * @param element - The element to cancel animations for\n */\n cancelAllAnimations(element: HTMLElement): void {\n // Cancel tracked animation\n const handle = this.activeAnimations.get(element);\n if (handle) {\n this.cancelAnimation(handle, false);\n }\n\n // Also cancel any other animations on the element (e.g., completed animations with fill: 'forwards')\n const allAnimations = element.getAnimations();\n for (const anim of allAnimations) {\n anim.cancel();\n }\n }\n\n /**\n * Get current transform state of an element (works mid-animation)\n * Uses DOMMatrix to parse the computed transform\n * @param element - The element to query\n * @returns Current transform snapshot\n */\n getCurrentTransform(element: HTMLElement): AnimationSnapshot {\n const computed = getComputedStyle(element);\n const transformStr = computed.transform;\n\n if (transformStr === 'none' || !transformStr) {\n return { x: 0, y: 0, rotation: 0, scale: 1 };\n }\n\n const matrix = new DOMMatrix(transformStr);\n\n // Extract scale from matrix (for uniform scale)\n const scale = Math.sqrt(matrix.a * matrix.a + matrix.b * matrix.b);\n\n // Extract rotation from matrix (in degrees)\n const rotation = Math.atan2(matrix.b, matrix.a) * (180 / Math.PI);\n\n // Extract translation\n // Note: matrix.e and matrix.f include ALL translations including the centering offset\n // The centering is translate(-50%, -50%) which depends on element dimensions\n // Since our transform chain is: translate(-50%, -50%) translate(x, y) rotate scale\n // The additional x,y offset we applied is already baked into e,f along with centering\n // For cross-animation, we need the relative offset, not absolute position\n // We'll return e,f directly and let ZoomEngine handle the interpretation\n const x = matrix.e;\n const y = matrix.f;\n\n return { x, y, rotation, scale };\n }\n\n /**\n * Check if an element has an active animation\n * @param element - The element to check\n * @returns True if animation is in progress\n */\n hasActiveAnimation(element: HTMLElement): boolean {\n return this.activeAnimations.has(element);\n }\n\n /**\n * Get animation handle for an element if it exists\n * @param element - The element to query\n * @returns AnimationHandle or undefined\n */\n getAnimationHandle(element: HTMLElement): AnimationHandle | undefined {\n return this.activeAnimations.get(element);\n }\n\n /**\n * Animate element transform with smooth easing (CSS transitions - legacy method)\n * @param element - The element to animate\n * @param properties - Transform properties {x, y, rotation, scale}\n * @param duration - Animation duration in ms (optional)\n * @param easing - CSS easing function (optional)\n * @returns Promise that resolves when animation completes\n */\n animateTransform(\n element: HTMLElement,\n properties: TransformParams,\n duration: number | null = null,\n easing: string | null = null\n ): Promise<void> {\n return new Promise((resolve) => {\n const animDuration = duration ?? this.config.duration;\n const animEasing = easing ?? this.config.easing.default;\n\n // Apply transition\n element.style.transition = `transform ${animDuration}ms ${animEasing}, box-shadow ${animDuration}ms ${animEasing}`;\n\n // Apply transform using shared helper\n element.style.transform = this.buildTransformString(properties);\n\n // Resolve promise when animation completes\n setTimeout(() => {\n resolve();\n }, animDuration);\n });\n }\n\n /**\n * Reset element to its original transform\n * @param element - The element to reset\n * @param originalState - Original transform state {x, y, rotation, scale}\n * @returns Promise that resolves when animation completes\n */\n resetTransform(element: HTMLElement, originalState: TransformParams | ImageLayout): Promise<void> {\n return this.animateTransform(element, originalState);\n }\n\n /**\n * Remove transition styles from element\n * @param element - The element to clear\n */\n clearTransition(element: HTMLElement): void {\n element.style.transition = '';\n }\n\n /**\n * Utility: Wait for a specified duration\n * @param ms - Milliseconds to wait\n * @returns Promise that resolves after the specified duration\n */\n wait(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n}","/**\n * PathAnimator.ts\n * Provides path calculation functions for bounce, elastic, and wave entry animations\n *\n * These animations require JavaScript-driven frame updates because they involve\n * complex mathematical curves that can't be expressed with CSS transitions alone.\n *\n * Public API:\n * - animatePath(element, startPos, endPos, pathConfig, duration, onComplete)\n * - calculateBouncePosition(t, start, end, config)\n * - calculateElasticPosition(t, start, end, config)\n * - calculateWavePosition(t, start, end, config)\n */\n\nimport type {\n BouncePathConfig,\n ElasticPathConfig,\n WavePathConfig,\n EntryPathConfig,\n EntryPathType,\n EntryRotationConfig,\n EntryScaleConfig\n} from '../config/types';\nimport {\n resolveBounceConfig,\n resolveElasticConfig,\n resolveWavePathConfig\n} from '../config/defaults';\n\nexport interface Point {\n x: number;\n y: number;\n}\n\nexport interface PathAnimationOptions {\n element: HTMLElement;\n startPosition: Point;\n endPosition: Point;\n pathConfig: EntryPathConfig;\n duration: number;\n imageWidth: number;\n imageHeight: number;\n rotation: number; // Final rotation\n scale: number; // Final scale\n onComplete?: () => void;\n // Rotation animation options\n rotationConfig?: EntryRotationConfig;\n startRotation?: number; // Starting rotation (if different from final)\n // Scale animation options\n scaleConfig?: EntryScaleConfig;\n startScale?: number; // Starting scale (if different from final)\n}\n\n/**\n * Linear interpolation helper\n */\nfunction lerp(start: number, end: number, t: number): number {\n return start + (end - start) * t;\n}\n\n/**\n * Calculate position along a bounce path\n * Overshoot and settle animation\n */\nexport function calculateBouncePosition(\n t: number,\n start: Point,\n end: Point,\n config: BouncePathConfig\n): Point {\n const { overshoot, bounces, decayRatio } = config;\n\n // Direction vector from start to end\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n\n // Calculate keyframe timings based on number of bounces\n const keyframes = generateBounceKeyframes(bounces, decayRatio);\n\n // Find current segment\n let progress = 0;\n let segmentStart = 0;\n let segmentEnd = 1;\n let segmentOvershoot = overshoot;\n let isOvershootPhase = false;\n\n for (let i = 0; i < keyframes.length; i++) {\n if (t <= keyframes[i].time) {\n segmentStart = i === 0 ? 0 : keyframes[i - 1].time;\n segmentEnd = keyframes[i].time;\n segmentOvershoot = keyframes[i].overshoot;\n isOvershootPhase = keyframes[i].isOvershoot;\n break;\n }\n }\n\n // Calculate progress within current segment\n const segmentT = (t - segmentStart) / (segmentEnd - segmentStart);\n\n if (isOvershootPhase) {\n // Ease out into overshoot\n progress = 1 + segmentOvershoot * easeOutQuad(segmentT);\n } else if (segmentStart === 0) {\n // Initial travel to target - ease out\n progress = easeOutQuad(segmentT);\n } else {\n // Settling back from overshoot\n const prevOvershoot = keyframes.find((k, i) =>\n k.time > segmentStart && i > 0 && keyframes[i - 1].isOvershoot\n );\n const fromProgress = 1 + (prevOvershoot?.overshoot || segmentOvershoot);\n progress = lerp(fromProgress, 1, easeOutQuad(segmentT));\n }\n\n return {\n x: start.x + dx * progress,\n y: start.y + dy * progress\n };\n}\n\n/**\n * Generate keyframe timings for bounce animation\n */\nfunction generateBounceKeyframes(\n bounces: number,\n decayRatio: number\n): Array<{ time: number; overshoot: number; isOvershoot: boolean }> {\n const keyframes: Array<{ time: number; overshoot: number; isOvershoot: boolean }> = [];\n\n // Initial travel takes 60% of time\n let currentTime = 0.6;\n keyframes.push({ time: currentTime, overshoot: 0, isOvershoot: false });\n\n let currentOvershoot = 0.15; // Initial overshoot amount\n const remainingTime = 0.4;\n const bounceTime = remainingTime / (bounces * 2);\n\n for (let i = 0; i < bounces; i++) {\n // Overshoot phase\n currentTime += bounceTime;\n keyframes.push({ time: currentTime, overshoot: currentOvershoot, isOvershoot: true });\n\n // Settle phase\n currentTime += bounceTime;\n keyframes.push({ time: currentTime, overshoot: currentOvershoot * decayRatio, isOvershoot: false });\n\n currentOvershoot *= decayRatio;\n }\n\n // Final settle\n keyframes.push({ time: 1, overshoot: 0, isOvershoot: false });\n\n return keyframes;\n}\n\n/**\n * Calculate position along an elastic path\n * Spring-like oscillation animation\n */\nexport function calculateElasticPosition(\n t: number,\n start: Point,\n end: Point,\n config: ElasticPathConfig\n): Point {\n const { stiffness, damping, mass, oscillations } = config;\n\n // Direction vector from start to end\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n\n // Normalized spring physics\n // Natural frequency based on stiffness and mass\n const omega = Math.sqrt(stiffness / mass);\n\n // Damping ratio (normalized)\n const zeta = damping / (2 * Math.sqrt(stiffness * mass));\n\n // Progress using damped harmonic oscillator\n let progress: number;\n\n if (zeta < 1) {\n // Underdamped - oscillates\n const dampedFreq = omega * Math.sqrt(1 - zeta * zeta);\n const envelope = Math.exp(-zeta * omega * t * 3);\n const oscillation = Math.cos(dampedFreq * t * oscillations * Math.PI);\n progress = 1 - envelope * oscillation;\n } else {\n // Critically damped or overdamped - no oscillation\n progress = 1 - Math.exp(-omega * t * 3);\n }\n\n // Clamp progress\n progress = Math.max(0, Math.min(progress, 1.3)); // Allow slight overshoot\n\n return {\n x: start.x + dx * progress,\n y: start.y + dy * progress\n };\n}\n\n/**\n * Calculate position along a wave path\n * Sinusoidal path from start to end\n */\nexport function calculateWavePosition(\n t: number,\n start: Point,\n end: Point,\n config: WavePathConfig\n): Point {\n const { amplitude, frequency, decay, decayRate, phase } = config;\n\n // Direction vector from start to end\n const dx = end.x - start.x;\n const dy = end.y - start.y;\n const length = Math.sqrt(dx * dx + dy * dy);\n\n // Perpendicular vector (normalized)\n const perpX = length > 0 ? -dy / length : 0;\n const perpY = length > 0 ? dx / length : 1;\n\n // Wave calculation\n const wavePhase = frequency * Math.PI * 2 * t + phase;\n const decayFactor = decay ? Math.pow(1 - t, decayRate) : 1;\n const waveOffset = amplitude * Math.sin(wavePhase) * decayFactor;\n\n // Ease out for smooth arrival\n const progressT = easeOutCubic(t);\n\n // Linear interpolation + wave offset\n return {\n x: lerp(start.x, end.x, progressT) + waveOffset * perpX,\n y: lerp(start.y, end.y, progressT) + waveOffset * perpY\n };\n}\n\n/**\n * Easing functions\n */\nfunction easeOutQuad(t: number): number {\n return 1 - (1 - t) * (1 - t);\n}\n\nfunction easeOutCubic(t: number): number {\n return 1 - Math.pow(1 - t, 3);\n}\n\n/**\n * Calculate wobble rotation for a given animation progress\n */\nfunction calculateWobbleRotation(\n progress: number,\n finalRotation: number,\n wobbleConfig: { amplitude: number; frequency: number; decay: boolean }\n): number {\n const { amplitude, frequency, decay } = wobbleConfig;\n\n // Oscillation using sine wave\n const oscillation = Math.sin(progress * frequency * Math.PI * 2);\n\n // Apply decay if enabled (stronger decay toward end)\n const decayFactor = decay ? Math.pow(1 - progress, 2) : 1;\n\n // Calculate wobble offset\n const wobbleOffset = amplitude * oscillation * decayFactor;\n\n return finalRotation + wobbleOffset;\n}\n\n/**\n * Calculate pop scale for a given animation progress\n */\nfunction calculatePopScale(\n progress: number,\n finalScale: number,\n popConfig: { overshoot: number; bounces: number }\n): number {\n const { overshoot, bounces } = popConfig;\n\n // Generate keyframes for bounce effect\n const keyframes: Array<{ time: number; scale: number }> = [];\n\n // Reach overshoot at 50% of animation\n keyframes.push({ time: 0.5, scale: overshoot });\n\n // Add bounces\n let currentOvershoot = overshoot;\n const bounceDecay = 0.5;\n const remainingTime = 0.5;\n const bounceTime = remainingTime / (bounces * 2);\n\n let currentTime = 0.5;\n for (let i = 0; i < bounces; i++) {\n const undershoot = 1 - (currentOvershoot - 1) * bounceDecay;\n currentTime += bounceTime;\n keyframes.push({ time: currentTime, scale: undershoot });\n\n currentOvershoot = 1 + (currentOvershoot - 1) * bounceDecay * bounceDecay;\n currentTime += bounceTime;\n if (i < bounces - 1) {\n keyframes.push({ time: currentTime, scale: currentOvershoot });\n }\n }\n\n keyframes.push({ time: 1, scale: 1 });\n\n // Find current segment\n let currentScale = 1;\n for (let i = 0; i < keyframes.length; i++) {\n if (progress <= keyframes[i].time) {\n const prevTime = i === 0 ? 0 : keyframes[i - 1].time;\n const prevScale = i === 0 ? 1 : keyframes[i - 1].scale;\n const segmentProgress = (progress - prevTime) / (keyframes[i].time - prevTime);\n const easedProgress = easeOutQuad(segmentProgress);\n currentScale = prevScale + (keyframes[i].scale - prevScale) * easedProgress;\n break;\n }\n }\n\n return currentScale * finalScale;\n}\n\n/**\n * Animate an element along a path using requestAnimationFrame\n */\nexport function animatePath(options: PathAnimationOptions): void {\n const {\n element,\n startPosition,\n endPosition,\n pathConfig,\n duration,\n imageWidth,\n imageHeight,\n rotation: finalRotation,\n scale: finalScale,\n onComplete,\n rotationConfig,\n startRotation,\n scaleConfig,\n startScale\n } = options;\n\n const pathType = pathConfig.type;\n\n // Determine if we need to animate rotation\n const animateRotation = startRotation !== undefined && startRotation !== finalRotation;\n const isWobbleMode = rotationConfig?.mode === 'wobble';\n const wobbleConfig = rotationConfig?.wobble || { amplitude: 15, frequency: 3, decay: true };\n const needsRotationAnimation = animateRotation || isWobbleMode;\n\n // Determine if we need to animate scale\n const animateScale = startScale !== undefined && startScale !== finalScale;\n const isPopMode = scaleConfig?.mode === 'pop';\n const popConfig = scaleConfig?.pop || { overshoot: 1.2, bounces: 1 };\n const needsScaleAnimation = animateScale || isPopMode;\n\n // For linear/arc paths WITHOUT rotation or scale animation, use CSS transitions (handled elsewhere)\n if ((pathType === 'linear' || pathType === 'arc') && !needsRotationAnimation && !needsScaleAnimation) {\n if (onComplete) onComplete();\n return;\n }\n\n const startTime = performance.now();\n\n // Build center offset for transform\n const centerOffsetX = -imageWidth / 2;\n const centerOffsetY = -imageHeight / 2;\n\n function tick(currentTime: number): void {\n const elapsed = currentTime - startTime;\n const t = Math.min(elapsed / duration, 1);\n\n // Calculate position based on path type\n let position: Point;\n\n switch (pathType) {\n case 'bounce': {\n const config = resolveBounceConfig(\n pathConfig.bouncePreset,\n pathConfig.bounce\n );\n position = calculateBouncePosition(t, startPosition, endPosition, config);\n break;\n }\n case 'elastic': {\n const config = resolveElasticConfig(\n pathConfig.elasticPreset,\n pathConfig.elastic\n );\n position = calculateElasticPosition(t, startPosition, endPosition, config);\n break;\n }\n case 'wave': {\n const config = resolveWavePathConfig(\n pathConfig.wavePreset,\n pathConfig.wave\n );\n position = calculateWavePosition(t, startPosition, endPosition, config);\n break;\n }\n default:\n position = {\n x: lerp(startPosition.x, endPosition.x, t),\n y: lerp(startPosition.y, endPosition.y, t)\n };\n }\n\n // Calculate translate offset from final position\n const translateX = position.x - endPosition.x;\n const translateY = position.y - endPosition.y;\n\n // Calculate current rotation\n let currentRotation: number;\n if (isWobbleMode) {\n currentRotation = calculateWobbleRotation(t, finalRotation, wobbleConfig);\n } else if (animateRotation) {\n currentRotation = lerp(startRotation!, finalRotation, t);\n } else {\n currentRotation = finalRotation;\n }\n\n // Calculate current scale\n let currentScale: number;\n if (isPopMode) {\n currentScale = calculatePopScale(t, finalScale, popConfig);\n } else if (animateScale) {\n currentScale = lerp(startScale!, finalScale, t);\n } else {\n currentScale = finalScale;\n }\n\n // Apply transform\n element.style.transform =\n `translate(${centerOffsetX}px, ${centerOffsetY}px) ` +\n `translate(${translateX}px, ${translateY}px) ` +\n `rotate(${currentRotation}deg) scale(${currentScale})`;\n\n if (t < 1) {\n requestAnimationFrame(tick);\n } else {\n // Ensure we end exactly at the final position, rotation, and scale\n element.style.transform =\n `translate(${centerOffsetX}px, ${centerOffsetY}px) ` +\n `rotate(${finalRotation}deg) scale(${finalScale})`;\n if (onComplete) onComplete();\n }\n }\n\n requestAnimationFrame(tick);\n}\n\n/**\n * Check if a path type requires JavaScript animation (vs CSS transitions)\n */\nexport function requiresJSAnimation(pathType: EntryPathType): boolean {\n return pathType === 'bounce' || pathType === 'elastic' || pathType === 'wave';\n}\n\n","/**\n * EntryAnimationEngine.ts\n * Calculates starting positions for entry animations based on configuration\n *\n * Public API:\n * - calculateStartPosition(finalPosition, imageSize, containerBounds, imageIndex, totalImages)\n * - getAnimationParams(imageIndex)\n */\n\nimport type {\n EntryAnimationConfig,\n EntryStartPosition,\n ContainerBounds,\n LayoutAlgorithm,\n EntryPathConfig,\n EntryPathType,\n EntryRotationConfig,\n EntryRotationMode,\n EntryScaleConfig,\n EntryScaleMode\n} from '../config/types';\nimport { DEFAULT_PATH_CONFIG, DEFAULT_ENTRY_ROTATION, DEFAULT_ENTRY_SCALE } from '../config/defaults';\nimport { requiresJSAnimation } from './PathAnimator';\n\n/** Layout-aware default start positions */\nconst LAYOUT_ENTRY_DEFAULTS: Record<LayoutAlgorithm, EntryStartPosition> = {\n radial: 'center',\n spiral: 'center',\n grid: 'top',\n cluster: 'nearest-edge',\n random: 'nearest-edge',\n wave: 'left',\n honeycomb: 'center'\n};\n\nexport interface StartPosition {\n x: number;\n y: number;\n useScale?: boolean; // For center position, start with scale 0\n}\n\nexport interface AnimationParams {\n startTransform: string;\n duration: number;\n delay: number;\n easing: string;\n}\n\nexport class EntryAnimationEngine {\n private config: EntryAnimationConfig;\n private layoutAlgorithm: LayoutAlgorithm;\n private resolvedStartPosition: EntryStartPosition;\n private pathConfig: EntryPathConfig;\n private rotationConfig: EntryRotationConfig;\n private scaleConfig: EntryScaleConfig;\n\n constructor(config: EntryAnimationConfig, layoutAlgorithm: LayoutAlgorithm) {\n this.config = config;\n this.layoutAlgorithm = layoutAlgorithm;\n\n // Resolve the start position, using layout-aware defaults if not specified\n this.resolvedStartPosition = this.resolveStartPosition();\n\n // Resolve path config\n this.pathConfig = config.path || DEFAULT_PATH_CONFIG;\n\n // Resolve rotation config\n this.rotationConfig = config.rotation || DEFAULT_ENTRY_ROTATION;\n\n // Resolve scale config\n this.scaleConfig = config.scale || DEFAULT_ENTRY_SCALE;\n }\n\n /**\n * Get the effective start position, considering layout-aware defaults\n */\n private resolveStartPosition(): EntryStartPosition {\n // If explicitly configured, use that\n if (this.config.start.position) {\n return this.config.start.position;\n }\n // Otherwise use layout-aware default\n return LAYOUT_ENTRY_DEFAULTS[this.layoutAlgorithm] || 'nearest-edge';\n }\n\n /**\n * Calculate the starting position for an image's entry animation\n */\n calculateStartPosition(\n finalPosition: { x: number; y: number },\n imageSize: { width: number; height: number },\n containerBounds: ContainerBounds,\n imageIndex: number,\n totalImages: number\n ): StartPosition {\n const position = this.resolvedStartPosition;\n const offset = this.config.start.offset ?? 100;\n\n switch (position) {\n case 'nearest-edge':\n return this.calculateNearestEdge(finalPosition, imageSize, containerBounds, offset);\n\n case 'top':\n return this.calculateEdgePosition('top', finalPosition, imageSize, containerBounds, offset);\n\n case 'bottom':\n return this.calculateEdgePosition('bottom', finalPosition, imageSize, containerBounds, offset);\n\n case 'left':\n return this.calculateEdgePosition('left', finalPosition, imageSize, containerBounds, offset);\n\n case 'right':\n return this.calculateEdgePosition('right', finalPosition, imageSize, containerBounds, offset);\n\n case 'center':\n return this.calculateCenterPosition(containerBounds, finalPosition, imageSize);\n\n case 'random-edge':\n return this.calculateRandomEdge(finalPosition, imageSize, containerBounds, offset);\n\n case 'circular':\n return this.calculateCircularPosition(\n finalPosition,\n imageSize,\n containerBounds,\n imageIndex,\n totalImages\n );\n\n default:\n return this.calculateNearestEdge(finalPosition, imageSize, containerBounds, offset);\n }\n }\n\n /**\n * Calculate start position from the nearest edge (current default behavior)\n */\n private calculateNearestEdge(\n finalPosition: { x: number; y: number },\n imageSize: { width: number; height: number },\n containerBounds: ContainerBounds,\n offset: number\n ): StartPosition {\n // finalPosition now stores center position directly\n const centerX = finalPosition.x;\n const centerY = finalPosition.y;\n\n const distLeft = centerX;\n const distRight = containerBounds.width - centerX;\n const distTop = centerY;\n const distBottom = containerBounds.height - centerY;\n\n const minDist = Math.min(distLeft, distRight, distTop, distBottom);\n\n let startX = finalPosition.x;\n let startY = finalPosition.y;\n\n if (minDist === distLeft) {\n // Start from left edge\n startX = -(imageSize.width + offset);\n } else if (minDist === distRight) {\n // Start from right edge\n startX = containerBounds.width + offset;\n } else if (minDist === distTop) {\n // Start from top edge\n startY = -(imageSize.height + offset);\n } else {\n // Start from bottom edge\n startY = containerBounds.height + offset;\n }\n\n return { x: startX, y: startY };\n }\n\n /**\n * Calculate start position from a specific edge\n */\n private calculateEdgePosition(\n edge: 'top' | 'bottom' | 'left' | 'right',\n finalPosition: { x: number; y: number },\n imageSize: { width: number; height: number },\n containerBounds: ContainerBounds,\n offset: number\n ): StartPosition {\n let startX = finalPosition.x;\n let startY = finalPosition.y;\n\n switch (edge) {\n case 'top':\n startY = -(imageSize.height + offset);\n break;\n case 'bottom':\n startY = containerBounds.height + offset;\n break;\n case 'left':\n startX = -(imageSize.width + offset);\n break;\n case 'right':\n startX = containerBounds.width + offset;\n break;\n }\n\n return { x: startX, y: startY };\n }\n\n /**\n * Calculate start position from center with scale animation\n */\n private calculateCenterPosition(\n containerBounds: ContainerBounds,\n _finalPosition: { x: number; y: number },\n _imageSize: { width: number; height: number }\n ): StartPosition {\n // Start at center of container (using center position, not top-left)\n const centerX = containerBounds.width / 2;\n const centerY = containerBounds.height / 2;\n\n return {\n x: centerX,\n y: centerY,\n useScale: true // Signal to use scale animation from 0\n };\n }\n\n /**\n * Calculate start position from a random edge\n */\n private calculateRandomEdge(\n finalPosition: { x: number; y: number },\n imageSize: { width: number; height: number },\n containerBounds: ContainerBounds,\n offset: number\n ): StartPosition {\n const edges: ('top' | 'bottom' | 'left' | 'right')[] = ['top', 'bottom', 'left', 'right'];\n const randomEdge = edges[Math.floor(Math.random() * edges.length)];\n return this.calculateEdgePosition(randomEdge, finalPosition, imageSize, containerBounds, offset);\n }\n\n /**\n * Calculate start position on a circle around the container\n */\n private calculateCircularPosition(\n _finalPosition: { x: number; y: number },\n _imageSize: { width: number; height: number },\n containerBounds: ContainerBounds,\n imageIndex: number,\n totalImages: number\n ): StartPosition {\n const circularConfig = this.config.start.circular || {};\n const distribution = circularConfig.distribution || 'even';\n\n // Calculate radius\n let radius: number;\n const radiusConfig = circularConfig.radius || '120%';\n\n if (typeof radiusConfig === 'string' && radiusConfig.endsWith('%')) {\n // Percentage of container diagonal\n const percentage = parseFloat(radiusConfig) / 100;\n const diagonal = Math.sqrt(\n containerBounds.width ** 2 + containerBounds.height ** 2\n );\n radius = diagonal * percentage / 2;\n } else {\n radius = typeof radiusConfig === 'number' ? radiusConfig : 500;\n }\n\n // Calculate angle\n let angle: number;\n if (distribution === 'even') {\n angle = (imageIndex / totalImages) * 2 * Math.PI;\n } else {\n angle = Math.random() * 2 * Math.PI;\n }\n\n // Calculate position on circle, centered on container\n const centerX = containerBounds.width / 2;\n const centerY = containerBounds.height / 2;\n\n // Return center position on circle (not top-left)\n const startX = centerX + Math.cos(angle) * radius;\n const startY = centerY + Math.sin(angle) * radius;\n\n return { x: startX, y: startY };\n }\n\n /**\n * Get animation parameters for an image\n */\n getAnimationParams(_imageIndex: number): AnimationParams {\n const duration = this.config.timing.duration;\n const easing = this.config.easing;\n\n return {\n startTransform: '', // Will be computed by caller based on start position\n duration,\n delay: 0,\n easing\n };\n }\n\n /**\n * Build a CSS transform string for the start position\n * Uses pixel-based centering offset for reliable cross-browser behavior\n */\n buildStartTransform(\n startPosition: StartPosition,\n finalPosition: { x: number; y: number },\n finalRotation: number,\n finalScale: number,\n imageWidth?: number,\n imageHeight?: number,\n startRotation?: number,\n startScale?: number\n ): string {\n // Calculate translation from final to start position\n const translateX = startPosition.x - finalPosition.x;\n const translateY = startPosition.y - finalPosition.y;\n\n // Use start rotation if provided, otherwise use final rotation\n const rotation = startRotation !== undefined ? startRotation : finalRotation;\n\n // Use start scale if provided, otherwise use final scale\n const scale = startScale !== undefined ? startScale : finalScale;\n\n // Use pixel offset if dimensions provided\n const centerOffsetX = imageWidth !== undefined ? -imageWidth / 2 : 0;\n const centerOffsetY = imageHeight !== undefined ? -imageHeight / 2 : 0;\n const centerTranslate = imageWidth !== undefined\n ? `translate(${centerOffsetX}px, ${centerOffsetY}px)`\n : `translate(-50%, -50%)`;\n\n if (startPosition.useScale) {\n // For center position: start at center with scale 0\n return `${centerTranslate} translate(${translateX}px, ${translateY}px) rotate(${rotation}deg) scale(0)`;\n }\n\n // Standard entry: translate from edge, with configured start scale\n return `${centerTranslate} translate(${translateX}px, ${translateY}px) rotate(${rotation}deg) scale(${scale})`;\n }\n\n /**\n * Build the final CSS transform string\n * Uses pixel-based centering offset for reliable cross-browser behavior\n */\n buildFinalTransform(rotation: number, scale: number, imageWidth?: number, imageHeight?: number): string {\n // Use pixel offset if dimensions provided, otherwise fall back to percentage\n if (imageWidth !== undefined && imageHeight !== undefined) {\n const offsetX = -imageWidth / 2;\n const offsetY = -imageHeight / 2;\n return `translate(${offsetX}px, ${offsetY}px) rotate(${rotation}deg) scale(${scale})`;\n }\n return `translate(-50%, -50%) rotate(${rotation}deg) scale(${scale})`;\n }\n\n /**\n * Get the transition CSS for entry animation\n * For JS-animated paths, only animate opacity (transform handled by JS)\n */\n getTransitionCSS(): string {\n const duration = this.config.timing.duration;\n const easing = this.config.easing;\n\n // For paths that use JS animation, don't transition transform\n if (this.requiresJSAnimation()) {\n return `opacity ${duration}ms ease-out`;\n }\n\n return `opacity ${duration}ms ease-out, transform ${duration}ms ${easing}`;\n }\n\n /**\n * Check if the current path type requires JavaScript animation\n */\n requiresJSAnimation(): boolean {\n return requiresJSAnimation(this.pathConfig.type);\n }\n\n /**\n * Get the path configuration\n */\n getPathConfig(): EntryPathConfig {\n return this.pathConfig;\n }\n\n /**\n * Get the path type\n */\n getPathType(): EntryPathType {\n return this.pathConfig.type;\n }\n\n /**\n * Get animation timing configuration\n */\n getTiming(): { duration: number } {\n return {\n duration: this.config.timing.duration\n };\n }\n\n /**\n * Get the rotation configuration\n */\n getRotationConfig(): EntryRotationConfig {\n return this.rotationConfig;\n }\n\n /**\n * Get the rotation mode\n */\n getRotationMode(): EntryRotationMode {\n return this.rotationConfig.mode;\n }\n\n /**\n * Calculate the starting rotation for an entry animation\n * @param finalRotation - The final rotation from the layout\n * @returns The starting rotation in degrees\n */\n calculateStartRotation(finalRotation: number): number {\n const mode = this.rotationConfig.mode;\n\n switch (mode) {\n case 'none':\n // No rotation animation - start at final rotation\n return finalRotation;\n\n case 'settle': {\n // Start at a configured rotation and settle to final\n const startConfig = this.rotationConfig.startRotation;\n if (startConfig === undefined) {\n // Default: ±30° random offset from final\n return finalRotation + (Math.random() - 0.5) * 60;\n }\n if (typeof startConfig === 'number') {\n return startConfig;\n }\n // Range: random value between min and max\n const range = startConfig.max - startConfig.min;\n return startConfig.min + Math.random() * range;\n }\n\n case 'spin': {\n // Spin from a rotated position to final\n const spinCount = this.rotationConfig.spinCount ?? 1;\n const direction = this.resolveSpinDirection(finalRotation);\n return finalRotation + (spinCount * 360 * direction);\n }\n\n case 'random':\n // Random starting rotation (±30° from final)\n return finalRotation + (Math.random() - 0.5) * 60;\n\n case 'wobble':\n // Wobble is handled in JS animation, start at final rotation\n return finalRotation;\n\n default:\n return finalRotation;\n }\n }\n\n /**\n * Resolve spin direction based on config\n * @returns 1 for clockwise, -1 for counterclockwise\n */\n private resolveSpinDirection(finalRotation: number): number {\n const direction = this.rotationConfig.direction ?? 'auto';\n\n switch (direction) {\n case 'clockwise':\n return -1; // Negative rotation = clockwise spin to final\n case 'counterclockwise':\n return 1; // Positive rotation = counterclockwise spin to final\n case 'random':\n return Math.random() < 0.5 ? 1 : -1;\n case 'auto':\n default:\n // Auto: choose direction that reduces total rotation distance\n // If final rotation is positive, spin counterclockwise (add positive offset)\n return finalRotation >= 0 ? 1 : -1;\n }\n }\n\n /**\n * Check if the current rotation mode requires JavaScript animation\n * (as opposed to CSS transitions)\n */\n requiresJSRotation(): boolean {\n return this.rotationConfig.mode === 'wobble';\n }\n\n /**\n * Calculate wobble rotation for a given animation progress\n * @param progress - Animation progress from 0 to 1\n * @param finalRotation - The final rotation in degrees\n * @returns The current rotation in degrees\n */\n calculateWobbleRotation(progress: number, finalRotation: number): number {\n if (this.rotationConfig.mode !== 'wobble') {\n return finalRotation;\n }\n\n const wobbleConfig = this.rotationConfig.wobble || {\n amplitude: 15,\n frequency: 3,\n decay: true\n };\n\n const { amplitude, frequency, decay } = wobbleConfig;\n\n // Oscillation using sine wave\n const oscillation = Math.sin(progress * frequency * Math.PI * 2);\n\n // Apply decay if enabled (stronger decay toward end)\n const decayFactor = decay ? Math.pow(1 - progress, 2) : 1;\n\n // Calculate wobble offset\n const wobbleOffset = amplitude * oscillation * decayFactor;\n\n return finalRotation + wobbleOffset;\n }\n\n /**\n * Get the scale configuration\n */\n getScaleConfig(): EntryScaleConfig {\n return this.scaleConfig;\n }\n\n /**\n * Get the scale mode\n */\n getScaleMode(): EntryScaleMode {\n return this.scaleConfig.mode;\n }\n\n /**\n * Calculate the starting scale for an entry animation\n * @param finalScale - The final scale from the layout\n * @returns The starting scale\n */\n calculateStartScale(finalScale: number): number {\n const mode = this.scaleConfig.mode;\n\n switch (mode) {\n case 'none':\n // No scale animation - start at final scale\n return finalScale;\n\n case 'grow': {\n // Start smaller, grow to final\n const startScale = this.scaleConfig.startScale ?? 0.3;\n return startScale * finalScale; // Apply relative to final scale\n }\n\n case 'shrink': {\n // Start larger, shrink to final\n const startScale = this.scaleConfig.startScale ?? 1.5;\n return startScale * finalScale; // Apply relative to final scale\n }\n\n case 'pop':\n // Pop mode uses JS animation, start at final scale\n // (the overshoot/bounce is handled in the animation tick)\n return finalScale;\n\n case 'random': {\n // Random start scale in configured range\n const range = this.scaleConfig.range ?? { min: 0.5, max: 1.0 };\n const randomFactor = range.min + Math.random() * (range.max - range.min);\n return randomFactor * finalScale;\n }\n\n default:\n return finalScale;\n }\n }\n\n /**\n * Check if the current scale mode requires JavaScript animation\n * (as opposed to CSS transitions)\n */\n requiresJSScale(): boolean {\n return this.scaleConfig.mode === 'pop';\n }\n\n /**\n * Calculate pop scale for a given animation progress\n * @param progress - Animation progress from 0 to 1\n * @param finalScale - The final scale value\n * @returns The current scale value with bounce effect\n */\n calculatePopScale(progress: number, finalScale: number): number {\n if (this.scaleConfig.mode !== 'pop') {\n return finalScale;\n }\n\n const popConfig = this.scaleConfig.pop || {\n overshoot: 1.2,\n bounces: 1\n };\n\n const { overshoot, bounces } = popConfig;\n\n // Create keyframes for bounce effect\n // Similar to bounce path but for scale\n const keyframes = this.generateScaleBounceKeyframes(bounces, overshoot);\n\n // Find current segment\n let currentScale = finalScale;\n for (let i = 0; i < keyframes.length; i++) {\n if (progress <= keyframes[i].time) {\n const prevTime = i === 0 ? 0 : keyframes[i - 1].time;\n const prevScale = i === 0 ? finalScale : keyframes[i - 1].scale;\n const segmentProgress = (progress - prevTime) / (keyframes[i].time - prevTime);\n // Smooth easing within segment\n const easedProgress = this.easeOutQuad(segmentProgress);\n currentScale = prevScale + (keyframes[i].scale - prevScale) * easedProgress;\n break;\n }\n }\n\n return currentScale * finalScale;\n }\n\n /**\n * Generate keyframes for scale bounce animation\n */\n private generateScaleBounceKeyframes(\n bounces: number,\n overshoot: number\n ): Array<{ time: number; scale: number }> {\n const keyframes: Array<{ time: number; scale: number }> = [];\n\n // Reach overshoot at 50% of animation\n keyframes.push({ time: 0.5, scale: overshoot });\n\n // Add bounces\n let currentOvershoot = overshoot;\n const bounceDecay = 0.5; // Each bounce is 50% of previous\n const remainingTime = 0.5;\n const bounceTime = remainingTime / (bounces * 2);\n\n let currentTime = 0.5;\n for (let i = 0; i < bounces; i++) {\n // Undershoot (go below 1)\n const undershoot = 1 - (currentOvershoot - 1) * bounceDecay;\n currentTime += bounceTime;\n keyframes.push({ time: currentTime, scale: undershoot });\n\n // Overshoot again (smaller)\n currentOvershoot = 1 + (currentOvershoot - 1) * bounceDecay * bounceDecay;\n currentTime += bounceTime;\n if (i < bounces - 1) {\n keyframes.push({ time: currentTime, scale: currentOvershoot });\n }\n }\n\n // Final settle\n keyframes.push({ time: 1, scale: 1 });\n\n return keyframes;\n }\n\n /**\n * Easing function for smooth transitions\n */\n private easeOutQuad(t: number): number {\n return 1 - (1 - t) * (1 - t);\n }\n}\n","/**\n * IdleAnimationEngine.ts\n * Manages continuous ambient animations for idle images using the Web Animations API.\n *\n * Uses composite: 'add' for transform-based animations (wiggle, pulse, spin) so\n * idle animations layer on top of the base transform without replacing it.\n * Blink uses opacity with no composite.\n */\n\nimport type {\n IdleAnimationConfig,\n IdleWiggleConfig,\n IdlePulseConfig,\n IdleBlinkConfig,\n IdleSpinConfig\n} from '../config/types';\nimport {\n DEFAULT_IDLE_WIGGLE,\n DEFAULT_IDLE_PULSE,\n DEFAULT_IDLE_BLINK,\n DEFAULT_IDLE_SPIN\n} from '../config/defaults';\n\ninterface IdleEntry {\n element: HTMLElement;\n index: number;\n totalImages: number;\n animation: Animation | null;\n blinkAnimation: Animation | null;\n customTeardown: (() => void) | null;\n paused: boolean;\n stopped: boolean;\n startTimer: ReturnType<typeof setTimeout> | null;\n}\n\nexport class IdleAnimationEngine {\n private config: IdleAnimationConfig;\n private entries: Map<HTMLElement, IdleEntry> = new Map();\n private entryDurationMs: number;\n\n // Single rAF loop shared across all 'together'-sync animations.\n // Each frame, all active together-mode animations get the same currentTime.\n private togetherRafId: number | null = null;\n private togetherSpeed: number = 0;\n\n constructor(config: IdleAnimationConfig, entryDurationMs = 600) {\n this.config = config;\n this.entryDurationMs = entryDurationMs;\n }\n\n /**\n * Register an image element for idle animation.\n * Starts animation after entry duration completes.\n */\n register(element: HTMLElement, index: number, totalImages: number, entryDuration?: number): void {\n if (this.entries.has(element)) return;\n\n const delay = entryDuration ?? this.entryDurationMs;\n const startDelay = this.config.startDelay ?? delay;\n\n const entry: IdleEntry = {\n element,\n index,\n totalImages,\n animation: null,\n blinkAnimation: null,\n customTeardown: null,\n paused: false,\n stopped: false,\n startTimer: null\n };\n\n this.entries.set(element, entry);\n\n entry.startTimer = setTimeout(() => {\n entry.startTimer = null;\n if (!entry.stopped && !entry.paused) {\n this._startAnimation(entry);\n }\n }, startDelay);\n }\n\n /**\n * Pause idle animation for a specific image (set to neutral then pause).\n */\n pauseForImage(element: HTMLElement): void {\n const entry = this.entries.get(element);\n if (!entry) return;\n entry.paused = true;\n // Cancel start timer if still pending\n if (entry.startTimer !== null) {\n clearTimeout(entry.startTimer);\n entry.startTimer = null;\n }\n this._pauseEntry(entry);\n }\n\n /**\n * Resume idle animation for a specific image by starting a fresh animation.\n * Always restarts rather than resuming, to avoid Web Animations API\n * quirks with negative-delay animations after pause/cancel.\n */\n resumeForImage(element: HTMLElement): void {\n const entry = this.entries.get(element);\n if (!entry || entry.stopped) return;\n entry.paused = false;\n this._startAnimation(entry);\n }\n\n /**\n * Stop and remove idle animation for a specific image.\n */\n stopForImage(element: HTMLElement): void {\n const entry = this.entries.get(element);\n if (!entry) return;\n entry.stopped = true;\n if (entry.startTimer !== null) {\n clearTimeout(entry.startTimer);\n entry.startTimer = null;\n }\n this._cancelEntry(entry);\n this.entries.delete(element);\n }\n\n pauseAll(): void {\n for (const entry of this.entries.values()) {\n entry.paused = true;\n if (entry.startTimer !== null) {\n clearTimeout(entry.startTimer);\n entry.startTimer = null;\n }\n this._pauseEntry(entry);\n }\n }\n\n resumeAll(): void {\n for (const entry of this.entries.values()) {\n if (!entry.stopped) {\n entry.paused = false;\n this._startAnimation(entry);\n }\n }\n }\n\n stopAll(): void {\n for (const entry of this.entries.values()) {\n entry.stopped = true;\n if (entry.startTimer !== null) {\n clearTimeout(entry.startTimer);\n entry.startTimer = null;\n }\n this._cancelEntry(entry);\n }\n this.entries.clear();\n this._stopTogetherLoop();\n }\n\n // ──────────────────────────────────────────────────────────────────────────\n // Private helpers\n // ──────────────────────────────────────────────────────────────────────────\n\n private _startAnimation(entry: IdleEntry): void {\n const { type } = this.config;\n switch (type) {\n case 'wiggle': this._startWiggle(entry); break;\n case 'pulse': this._startPulse(entry); break;\n case 'blink': this._startBlink(entry); break;\n case 'spin': this._startSpin(entry); break;\n case 'custom': this._startCustom(entry); break;\n default: break;\n }\n }\n\n private _startWiggle(entry: IdleEntry): void {\n const cfg: IdleWiggleConfig = { ...DEFAULT_IDLE_WIGGLE, ...this.config.wiggle };\n\n const keyframes: Keyframe[] = [\n { transform: 'rotate(0deg)', offset: 0 },\n { transform: `rotate(${cfg.maxAngle}deg)`, offset: 0.25 },\n { transform: 'rotate(0deg)', offset: 0.5 },\n { transform: `rotate(${-cfg.maxAngle}deg)`, offset: 0.75 },\n { transform: 'rotate(0deg)', offset: 1 }\n ];\n\n if (cfg.sync === 'together') {\n entry.animation = entry.element.animate(keyframes, {\n duration: cfg.speed, iterations: Infinity, composite: 'add', fill: 'both'\n });\n entry.animation.pause();\n this._startTogetherLoop(cfg.speed);\n } else {\n entry.animation = entry.element.animate(keyframes, {\n duration: cfg.speed,\n delay: -(Math.random() * cfg.speed),\n iterations: Infinity,\n composite: 'add'\n });\n }\n }\n\n private _startPulse(entry: IdleEntry): void {\n const cfg: IdlePulseConfig = { ...DEFAULT_IDLE_PULSE, ...this.config.pulse };\n\n const keyframes: Keyframe[] = [\n { transform: 'scale(1)', offset: 0 },\n { transform: `scale(${cfg.maxScale})`, offset: 0.25 },\n { transform: 'scale(1)', offset: 0.5 },\n { transform: `scale(${cfg.minScale})`, offset: 0.75 },\n { transform: 'scale(1)', offset: 1 }\n ];\n\n if (cfg.sync === 'together') {\n entry.animation = entry.element.animate(keyframes, {\n duration: cfg.speed, iterations: Infinity, composite: 'add', fill: 'both'\n });\n entry.animation.pause();\n this._startTogetherLoop(cfg.speed);\n } else {\n entry.animation = entry.element.animate(keyframes, {\n duration: cfg.speed,\n delay: -(Math.random() * cfg.speed),\n iterations: Infinity,\n composite: 'add'\n });\n }\n }\n\n private _startBlink(entry: IdleEntry): void {\n const cfg: IdleBlinkConfig = { ...DEFAULT_IDLE_BLINK, ...this.config.blink };\n const delay = -(Math.random() * cfg.speed);\n\n // Use the element's current opacity as the \"visible\" state so blink\n // respects the configured default (and hover) opacity.\n const onOpacity = parseFloat(getComputedStyle(entry.element).opacity) || 1;\n\n let keyframes: Keyframe[];\n let options: KeyframeAnimationOptions;\n\n if (cfg.style === 'fade') {\n keyframes = [\n { opacity: onOpacity, offset: 0 },\n { opacity: 0, offset: 0.5 },\n { opacity: onOpacity, offset: 1 }\n ];\n options = {\n duration: cfg.speed,\n delay,\n iterations: Infinity,\n easing: 'ease-in-out'\n };\n } else {\n // snap (default)\n keyframes = [\n { opacity: onOpacity, offset: 0 },\n { opacity: onOpacity, offset: cfg.onRatio },\n { opacity: 0, offset: Math.min(cfg.onRatio + 0.01, 0.99) },\n { opacity: 0, offset: 0.99 },\n { opacity: onOpacity, offset: 1 }\n ];\n options = {\n duration: cfg.speed,\n delay,\n iterations: Infinity\n };\n }\n\n entry.blinkAnimation = entry.element.animate(keyframes, options);\n }\n\n private _startSpin(entry: IdleEntry): void {\n const cfg: IdleSpinConfig = { ...DEFAULT_IDLE_SPIN, ...this.config.spin };\n const endDeg = cfg.direction === 'clockwise' ? 360 : -360;\n\n entry.animation = entry.element.animate(\n [{ transform: 'rotate(0deg)' }, { transform: `rotate(${endDeg}deg)` }],\n {\n duration: cfg.speed,\n iterations: Infinity,\n easing: 'linear',\n composite: 'add'\n }\n );\n }\n\n private _startCustom(entry: IdleEntry): void {\n const fn = this.config.custom;\n if (!fn) return;\n\n const result = fn({ element: entry.element, index: entry.index, totalImages: entry.totalImages });\n\n if (typeof result === 'function') {\n entry.customTeardown = result;\n } else if (result && typeof (result as Animation).play === 'function') {\n entry.animation = result as Animation;\n }\n }\n\n private _startTogetherLoop(speed: number): void {\n this.togetherSpeed = speed;\n if (this.togetherRafId !== null) return; // already ticking\n const tick = () => {\n const t = performance.now() % this.togetherSpeed;\n for (const entry of this.entries.values()) {\n if (!entry.stopped && !entry.paused && entry.animation) {\n entry.animation.currentTime = t;\n }\n }\n this.togetherRafId = requestAnimationFrame(tick);\n };\n this.togetherRafId = requestAnimationFrame(tick);\n }\n\n private _stopTogetherLoop(): void {\n if (this.togetherRafId !== null) {\n cancelAnimationFrame(this.togetherRafId);\n this.togetherRafId = null;\n }\n }\n\n private _pauseEntry(entry: IdleEntry): void {\n // Cancel rather than pause — avoids Web Animations API quirks with\n // negative-delay infinite animations. resumeForImage() always restarts fresh.\n if (entry.animation) {\n entry.animation.cancel();\n entry.animation = null;\n }\n if (entry.blinkAnimation) {\n entry.blinkAnimation.cancel();\n entry.blinkAnimation = null;\n }\n }\n\n private _cancelEntry(entry: IdleEntry): void {\n if (entry.animation) {\n entry.animation.cancel();\n entry.animation = null;\n }\n if (entry.blinkAnimation) {\n entry.blinkAnimation.cancel();\n entry.blinkAnimation = null;\n }\n if (entry.customTeardown) {\n entry.customTeardown();\n entry.customTeardown = null;\n }\n }\n}\n","/**\n * RandomPlacementLayout.ts\n * Generates random overlapping layouts for image cloud\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, ImageConfig } from '../config/types';\n\ninterface RandomLayoutOptions extends Partial<LayoutConfig> {\n fixedHeight?: number;\n}\n\nexport class RandomPlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n\n constructor(config: LayoutConfig, imageConfig: ImageConfig = {}) {\n this.config = config;\n this.imageConfig = imageConfig;\n }\n\n /**\n * Generate random layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides (includes fixedHeight)\n * @returns Array of layout objects with position, rotation, scale\n */\n generate(imageCount: number, containerBounds: ContainerBounds, options: RandomLayoutOptions = {}): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n\n const padding = this.config.spacing.padding;\n // Use fixedHeight if provided, otherwise use default 200\n const baseImageSize = options.fixedHeight ?? 200;\n\n // Get rotation config from image config\n const rotationMode = this.imageConfig.rotation?.mode ?? 'none';\n const minRotation = this.imageConfig.rotation?.range?.min ?? -15;\n const maxRotation = this.imageConfig.rotation?.range?.max ?? 15;\n\n // Get variance config from image config\n const varianceMin = this.imageConfig.sizing?.variance?.min ?? 1.0;\n const varianceMax = this.imageConfig.sizing?.variance?.max ?? 1.0;\n const hasVariance = varianceMin !== 1.0 || varianceMax !== 1.0;\n\n // Calculate safe bounds for center positions (accounting for half image size and padding)\n // Use 16:9 aspect ratio (1.78) as maximum to handle most landscape images\n const estAspectRatio = 1.5; // 3:2 - balanced for mixed portrait/landscape\n const halfWidth = (baseImageSize * estAspectRatio) / 2;\n const halfHeight = baseImageSize / 2;\n\n const maxX = width - padding - halfWidth;\n const maxY = height - padding - halfHeight;\n const minX = padding + halfWidth;\n const minY = padding + halfHeight;\n\n for (let i = 0; i < imageCount; i++) {\n // Random center position within safe bounds\n const x = this.random(minX, maxX);\n const y = this.random(minY, maxY);\n\n // Random rotation within range (only when mode is random)\n const rotation = rotationMode === 'random' ? this.random(minRotation, maxRotation) : 0;\n\n // Random size variance\n const scale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const scaledImageSize = baseImageSize * scale;\n\n const layout: ImageLayout = {\n id: i,\n x,\n y,\n rotation,\n scale,\n baseSize: scaledImageSize\n };\n\n layouts.push(layout);\n }\n\n return layouts;\n }\n\n /**\n * Utility: Generate random number between min and max\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random number in range\n */\n private random(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n }\n}\n","/**\n * RadialPlacementLayout.ts\n * Generates concentric radial layouts for image cloud\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, RadialAlgorithmConfig, ImageConfig } from '../config/types';\nimport { DEFAULT_RADIAL_CONFIG } from '../config/defaults';\n\ninterface RadialLayoutOptions extends Partial<LayoutConfig> {\n fixedHeight?: number;\n}\n\nexport class RadialPlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n\n constructor(config: LayoutConfig, imageConfig: ImageConfig = {}) {\n this.config = config;\n this.imageConfig = imageConfig;\n }\n\n /**\n * Generate radial layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides\n * @returns Array of layout objects with position, rotation, scale\n */\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options: RadialLayoutOptions = {}\n ): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n // Use fixedHeight if provided, otherwise use default 200\n const baseImageSize = options.fixedHeight ?? 200;\n\n // Get rotation config from image config\n const rotationMode = this.imageConfig.rotation?.mode ?? 'none';\n const minRotation = this.imageConfig.rotation?.range?.min ?? -15;\n const maxRotation = this.imageConfig.rotation?.range?.max ?? 15;\n\n // Get variance config from image config\n const varianceMin = this.imageConfig.sizing?.variance?.min ?? 1.0;\n const varianceMax = this.imageConfig.sizing?.variance?.max ?? 1.0;\n const hasVariance = varianceMin !== 1.0 || varianceMax !== 1.0;\n\n // Get scale decay from layout config\n const scaleDecay = this.config.scaleDecay ?? 0;\n\n const radialConfig: RadialAlgorithmConfig = {\n ...DEFAULT_RADIAL_CONFIG,\n ...this.config.radial,\n };\n\n // Use override fixedHeight if provided, else baseImageSize\n const imageSize = options.fixedHeight ?? baseImageSize;\n const cx = width / 2;\n const cy = height / 2;\n\n // Calculate max rings for scale decay calculation\n const estimatedMaxRings = Math.ceil(Math.sqrt(imageCount));\n\n const padding = this.config.spacing.padding ?? 50;\n const maxRadius = Math.max(imageSize * 0.8, Math.min(\n cx - padding - imageSize / 2,\n cy - padding - imageSize / 2\n ));\n\n // Add center image (using center position)\n if (imageCount > 0) {\n // Apply variance to center image\n const varianceScale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const centerSize = imageSize * varianceScale;\n\n layouts.push({\n id: 0,\n x: cx,\n y: cy,\n rotation: rotationMode === 'random' ? this.random(minRotation * 0.33, maxRotation * 0.33) : 0, // Less rotation for center\n scale: varianceScale,\n baseSize: centerSize,\n zIndex: 100 // Center image is highest\n });\n }\n\n let processedCount = 1;\n let currentRing = 1;\n\n while (processedCount < imageCount) {\n // Calculate scale decay for this ring (center is largest, outer rings smaller)\n const normalizedRing = currentRing / estimatedMaxRings;\n const ringScale = scaleDecay > 0\n ? 1 - (normalizedRing * scaleDecay * 0.5) // Max 50% size reduction\n : 1.0;\n\n // Ring settings\n // Scale X more than Y to create horizontal oval shape\n const ringStep = Math.max(imageSize * 0.8, (maxRadius / estimatedMaxRings) * 1.5 / radialConfig.tightness);\n const radiusY = currentRing * ringStep;\n const radiusX = radiusY * 1.5; // Horizontal stretching factor\n\n const circumference = Math.PI * (3 * (radiusX + radiusY) - Math.sqrt((3 * radiusX + radiusY) * (radiusX + 3 * radiusY))); // Ramanujan's approximation\n\n const estimatedItemWidth = this.estimateWidth(imageSize);\n // Increase density by ~40% (1.1 -> 0.7)\n const itemsInRing = Math.floor(circumference / (estimatedItemWidth * 0.7));\n\n if (itemsInRing === 0) {\n currentRing++;\n continue;\n }\n\n const angleStep = (2 * Math.PI) / itemsInRing;\n\n // Add offset of 20 degrees per ring\n const ringOffset = currentRing * (20 * Math.PI / 180);\n\n for (let i = 0; i < itemsInRing && processedCount < imageCount; i++) {\n const angle = (i * angleStep) + ringOffset;\n\n // Apply variance and scale decay\n const varianceScale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const combinedScale = ringScale * varianceScale;\n const scaledImageSize = imageSize * combinedScale;\n\n // Calculate center position of image using elliptical formula (store center, not top-left)\n let x = cx + Math.cos(angle) * radiusX;\n let y = cy + Math.sin(angle) * radiusY;\n\n // Boundary Clamping - clamp center position with conservative estimate\n // Use 16:9 aspect ratio (1.78) as maximum to handle most landscape images\n const estAspectRatio = 1.5; // 3:2 - balanced for mixed portrait/landscape\n const halfWidth = (scaledImageSize * estAspectRatio) / 2;\n const halfHeight = scaledImageSize / 2;\n\n // Clamp X (center position)\n if (x - halfWidth < padding) {\n x = padding + halfWidth;\n } else if (x + halfWidth > width - padding) {\n x = width - padding - halfWidth;\n }\n\n // Clamp Y (center position)\n if (y - halfHeight < padding) {\n y = padding + halfHeight;\n } else if (y + halfHeight > height - padding) {\n y = height - padding - halfHeight;\n }\n\n const rotation = rotationMode === 'random' ? this.random(minRotation, maxRotation) : 0;\n\n layouts.push({\n id: processedCount,\n x,\n y,\n rotation,\n scale: combinedScale,\n baseSize: scaledImageSize,\n zIndex: Math.max(1, 100 - currentRing) // Outer rings have lower z-index\n });\n\n processedCount++;\n }\n\n currentRing++;\n }\n\n return layouts;\n }\n\n /**\n * Estimate image width based on height\n * Assumes landscape aspect ratio (approximately 1.4:1)\n * @param height - Image height\n * @returns Estimated width\n */\n private estimateWidth(height: number): number {\n // Assume landscape aspect ratio approx 4:3 or 16:9 on average\n // Using 1.4 ratio\n return height * 1.4;\n }\n\n /**\n * Utility: Generate random number between min and max\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random number in range\n */\n private random(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n }\n}","/**\n * GridPlacementLayout.ts\n * Generates grid-based layouts with optional stagger and jitter\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, GridAlgorithmConfig, ImageConfig } from '../config/types';\n\ninterface GridLayoutOptions extends Partial<LayoutConfig> {\n fixedHeight?: number;\n}\n\nconst DEFAULT_GRID_CONFIG: GridAlgorithmConfig = {\n columns: 'auto',\n rows: 'auto',\n stagger: 'none',\n jitter: 0,\n overlap: 0,\n fillDirection: 'row',\n alignment: 'center',\n gap: 10,\n overflowOffset: 0.25\n};\n\n// Offset pattern for stacking overflow images within cells\n// Each overflow layer cycles through these directions\n// Corners first, then cardinal directions\nconst OVERFLOW_OFFSET_PATTERN = [\n { x: 1, y: 1 }, // bottom-right\n { x: -1, y: -1 }, // upper-left\n { x: 1, y: -1 }, // upper-right\n { x: -1, y: 1 }, // bottom-left\n { x: -1, y: 0 }, // left\n { x: 1, y: 0 }, // right\n { x: 0, y: -1 }, // up\n { x: 0, y: 1 }, // down\n];\n\nexport class GridPlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n\n constructor(config: LayoutConfig, imageConfig: ImageConfig = {}) {\n this.config = config;\n this.imageConfig = imageConfig;\n }\n\n /**\n * Generate grid layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides (includes fixedHeight)\n * @returns Array of layout objects with position, rotation, scale\n */\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options: GridLayoutOptions = {}\n ): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n\n const gridConfig = { ...DEFAULT_GRID_CONFIG, ...this.config.grid };\n const padding = this.config.spacing.padding;\n // Use fixedHeight if provided, otherwise use base size from config\n const baseImageSize = options.fixedHeight ?? 200;\n\n // Get rotation config from image config\n const rotationMode = this.imageConfig.rotation?.mode ?? 'none';\n\n // Get variance config from image config\n const varianceMin = this.imageConfig.sizing?.variance?.min ?? 1.0;\n const varianceMax = this.imageConfig.sizing?.variance?.max ?? 1.0;\n const hasVariance = varianceMin !== 1.0 || varianceMax !== 1.0;\n\n // Calculate available space\n const availableWidth = width - (2 * padding);\n const availableHeight = height - (2 * padding);\n\n // Calculate grid dimensions\n const { columns, rows } = this.calculateGridDimensions(\n imageCount,\n availableWidth,\n availableHeight,\n baseImageSize,\n gridConfig\n );\n\n // For stagger layouts, we need n+0.5 cells to fit in available space\n // Calculate cell size accounting for this extra half-cell\n const hasRowStagger = gridConfig.stagger === 'row';\n const hasColumnStagger = gridConfig.stagger === 'column';\n\n // Effective columns/rows for sizing: add 0.5 for stagger\n const effectiveColumns = hasRowStagger ? columns + 0.5 : columns;\n const effectiveRows = hasColumnStagger ? rows + 0.5 : rows;\n\n // Calculate cell size to fit effective count in available space\n const cellWidth = (availableWidth - (gridConfig.gap * (columns - 1))) / effectiveColumns;\n const cellHeight = (availableHeight - (gridConfig.gap * (rows - 1))) / effectiveRows;\n\n // Stagger offset is half a cell\n const staggerOffsetX = hasRowStagger ? cellWidth / 2 : 0;\n const staggerOffsetY = hasColumnStagger ? cellHeight / 2 : 0;\n\n // Calculate image size with overlap factor\n // overlap: 0 = fit in cell, 0.5 = 50% larger, 1.0 = 2x cell size\n const overlapMultiplier = 1 + gridConfig.overlap;\n const cellBasedSize = Math.min(cellWidth, cellHeight) * overlapMultiplier;\n\n // Use fixedHeight if provided, otherwise use cell-based calculation\n // For grid layouts, we respect fixedHeight but cap at cell-based size to avoid overflow\n const imageSize = options.fixedHeight\n ? Math.min(options.fixedHeight, cellBasedSize)\n : cellBasedSize;\n\n // Calculate total grid dimensions for alignment (include stagger offset)\n const totalGridWidth = columns * cellWidth + (columns - 1) * gridConfig.gap + staggerOffsetX;\n const totalGridHeight = rows * cellHeight + (rows - 1) * gridConfig.gap + staggerOffsetY;\n\n // Center the grid in the container\n const gridOffsetX = padding + (availableWidth - totalGridWidth) / 2;\n const gridOffsetY = padding + (availableHeight - totalGridHeight) / 2;\n\n // Detect overflow mode: when both dimensions are fixed and we have more images than cells\n const cellCount = columns * rows;\n const hasFixedGrid = gridConfig.columns !== 'auto' && gridConfig.rows !== 'auto';\n const isOverflowMode = hasFixedGrid && imageCount > cellCount;\n\n if (typeof window !== 'undefined') {\n (window as any).__gridOverflowDebug = {\n gridConfigColumns: gridConfig.columns,\n gridConfigRows: gridConfig.rows,\n columns, rows, cellCount,\n hasFixedGrid, imageCount, isOverflowMode\n };\n }\n\n // Track stack depth for each cell (used in overflow mode)\n const cellStackCount: number[] = isOverflowMode ? new Array(cellCount).fill(0) : [];\n\n // Calculate overflow offset in pixels\n const overflowOffsetPx = Math.min(cellWidth, cellHeight) * gridConfig.overflowOffset;\n\n for (let i = 0; i < imageCount; i++) {\n let col: number;\n let row: number;\n let stackLayer = 0; // 0 = base image, 1+ = overflow layers\n\n if (isOverflowMode && i >= cellCount) {\n // Overflow image: determine target cell and stack layer\n const overflowIndex = i - cellCount;\n const targetCell = overflowIndex % cellCount;\n stackLayer = Math.floor(overflowIndex / cellCount) + 1;\n cellStackCount[targetCell]++;\n\n // Get cell coordinates from target cell index\n if (gridConfig.fillDirection === 'row') {\n col = targetCell % columns;\n row = Math.floor(targetCell / columns);\n } else {\n row = targetCell % rows;\n col = Math.floor(targetCell / rows);\n }\n } else {\n // Base image: normal cell assignment\n if (gridConfig.fillDirection === 'row') {\n col = i % columns;\n row = Math.floor(i / columns);\n } else {\n row = i % rows;\n col = Math.floor(i / rows);\n }\n }\n\n // Base cell position (center of cell)\n let cellCenterX = gridOffsetX + col * (cellWidth + gridConfig.gap) + cellWidth / 2;\n let cellCenterY = gridOffsetY + row * (cellHeight + gridConfig.gap) + cellHeight / 2;\n\n // Apply stagger offset\n if (gridConfig.stagger === 'row' && row % 2 === 1) {\n cellCenterX += cellWidth / 2;\n } else if (gridConfig.stagger === 'column' && col % 2 === 1) {\n cellCenterY += cellHeight / 2;\n }\n\n // Apply overflow offset for stacked images\n if (stackLayer > 0) {\n const patternIndex = (stackLayer - 1) % OVERFLOW_OFFSET_PATTERN.length;\n const pattern = OVERFLOW_OFFSET_PATTERN[patternIndex];\n cellCenterX += pattern.x * overflowOffsetPx;\n cellCenterY += pattern.y * overflowOffsetPx;\n }\n\n // Apply jitter (random offset within cell bounds)\n if (gridConfig.jitter > 0) {\n const maxJitterX = (cellWidth / 2) * gridConfig.jitter;\n const maxJitterY = (cellHeight / 2) * gridConfig.jitter;\n cellCenterX += this.random(-maxJitterX, maxJitterX);\n cellCenterY += this.random(-maxJitterY, maxJitterY);\n }\n\n // Store center position directly (not top-left)\n let x = cellCenterX;\n let y = cellCenterY;\n\n // Handle incomplete row alignment (only for non-overflow mode)\n if (!isOverflowMode && gridConfig.fillDirection === 'row') {\n const itemsInLastRow = imageCount % columns || columns;\n const isLastRow = row === Math.floor((imageCount - 1) / columns);\n\n if (isLastRow && itemsInLastRow < columns) {\n const lastRowWidth = itemsInLastRow * cellWidth + (itemsInLastRow - 1) * gridConfig.gap;\n let alignmentOffset = 0;\n\n if (gridConfig.alignment === 'center') {\n alignmentOffset = (totalGridWidth - lastRowWidth) / 2;\n } else if (gridConfig.alignment === 'end') {\n alignmentOffset = totalGridWidth - lastRowWidth;\n }\n\n x += alignmentOffset;\n }\n }\n\n // Apply variance to create non-uniform look\n const varianceScale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const scaledImageSize = imageSize * varianceScale;\n\n // Boundary clamping for center-based positioning\n // Use 1.5 multiplier (3:2 aspect) as reasonable middle ground for mixed portrait/landscape\n const estAspectRatio = 1.5;\n const halfWidth = (scaledImageSize * estAspectRatio) / 2;\n const halfHeight = scaledImageSize / 2;\n const minX = padding + halfWidth;\n const maxX = width - padding - halfWidth;\n const minY = padding + halfHeight;\n const maxY = height - padding - halfHeight;\n\n x = Math.max(minX, Math.min(x, maxX));\n y = Math.max(minY, Math.min(y, maxY));\n\n // Apply rotation when mode is random\n // If jitter > 0, scale rotation by jitter factor; otherwise use full rotation range\n let rotation = 0;\n if (rotationMode === 'random') {\n const minRotation = this.imageConfig.rotation?.range?.min ?? -15;\n const maxRotation = this.imageConfig.rotation?.range?.max ?? 15;\n if (gridConfig.jitter > 0) {\n // Scale rotation by jitter factor for more subtle effect\n rotation = this.random(minRotation * gridConfig.jitter, maxRotation * gridConfig.jitter);\n } else {\n // Full rotation range even without jitter\n rotation = this.random(minRotation, maxRotation);\n }\n }\n\n // Calculate z-index: in overflow mode, overflow images go BELOW base images\n // Base images get higher z-index so they appear on top\n // Overflow images get progressively lower z-index so they flow under\n let zIndex: number;\n if (isOverflowMode && stackLayer > 0) {\n // Overflow images: lower z-index, decreasing with each layer\n // Layer 1 = 50, Layer 2 = 49, etc.\n zIndex = 50 - stackLayer;\n } else {\n // Base images: higher z-index (100+)\n zIndex = isOverflowMode ? 100 + i : i + 1;\n }\n\n layouts.push({\n id: i,\n x,\n y,\n rotation,\n scale: varianceScale,\n baseSize: scaledImageSize,\n zIndex\n });\n }\n\n return layouts;\n }\n\n /**\n * Calculate optimal grid dimensions based on image count and container\n */\n private calculateGridDimensions(\n imageCount: number,\n availableWidth: number,\n availableHeight: number,\n _baseImageSize: number,\n config: GridAlgorithmConfig\n ): { columns: number; rows: number } {\n let columns: number;\n let rows: number;\n\n if (config.columns !== 'auto' && config.rows !== 'auto') {\n columns = config.columns;\n rows = config.rows;\n } else if (config.columns !== 'auto') {\n columns = config.columns;\n rows = Math.ceil(imageCount / columns);\n } else if (config.rows !== 'auto') {\n rows = config.rows;\n columns = Math.ceil(imageCount / rows);\n } else {\n // Auto-calculate: try to fill the space while maintaining reasonable aspect ratio\n const aspectRatio = availableWidth / availableHeight;\n const imageAspectRatio = 1.4; // Assumed landscape\n\n // Calculate ideal columns based on aspect ratio\n columns = Math.max(1, Math.round(Math.sqrt(imageCount * aspectRatio / imageAspectRatio)));\n rows = Math.ceil(imageCount / columns);\n\n // Ensure we don't have too many empty cells\n while (columns > 1 && (columns - 1) * rows >= imageCount) {\n columns--;\n }\n }\n\n return { columns: Math.max(1, columns), rows: Math.max(1, rows) };\n }\n\n /**\n * Utility: Generate random number between min and max\n */\n private random(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n }\n}\n","/**\n * SpiralPlacementLayout.ts\n * Generates spiral layouts (golden, archimedean, logarithmic)\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, SpiralAlgorithmConfig, ImageConfig } from '../config/types';\n\ninterface SpiralLayoutOptions extends Partial<LayoutConfig> {\n fixedHeight?: number;\n}\n\n// Golden angle in radians (~137.5 degrees)\nconst GOLDEN_ANGLE = Math.PI * (3 - Math.sqrt(5));\n\nconst DEFAULT_SPIRAL_CONFIG: SpiralAlgorithmConfig = {\n spiralType: 'golden',\n direction: 'counterclockwise',\n tightness: 1.0,\n scaleDecay: 0,\n startAngle: 0\n};\n\nexport class SpiralPlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n\n constructor(config: LayoutConfig, imageConfig: ImageConfig = {}) {\n this.config = config;\n this.imageConfig = imageConfig;\n }\n\n /**\n * Generate spiral layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides (includes fixedHeight)\n * @returns Array of layout objects with position, rotation, scale\n */\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options: SpiralLayoutOptions = {}\n ): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n\n const spiralConfig = { ...DEFAULT_SPIRAL_CONFIG, ...this.config.spiral };\n const padding = this.config.spacing.padding;\n // Use fixedHeight if provided, otherwise use base size from config\n const baseImageSize = options.fixedHeight ?? 200;\n\n // Get rotation config from image config\n const rotationMode = this.imageConfig.rotation?.mode ?? 'none';\n const minRotation = this.imageConfig.rotation?.range?.min ?? -15;\n const maxRotation = this.imageConfig.rotation?.range?.max ?? 15;\n\n // Get variance config from image config\n const varianceMin = this.imageConfig.sizing?.variance?.min ?? 1.0;\n const varianceMax = this.imageConfig.sizing?.variance?.max ?? 1.0;\n const hasVariance = varianceMin !== 1.0 || varianceMax !== 1.0;\n\n // Get scale decay from layout config (overrides spiral-specific config)\n const scaleDecay = this.config.scaleDecay ?? spiralConfig.scaleDecay;\n\n // Center of the spiral\n const cx = width / 2;\n const cy = height / 2;\n\n // Calculate available radius (distance from center to edge)\n const maxRadius = Math.min(\n cx - padding - baseImageSize / 2,\n cy - padding - baseImageSize / 2\n );\n\n // Direction multiplier (1 for counterclockwise, -1 for clockwise)\n const directionMultiplier = spiralConfig.direction === 'clockwise' ? -1 : 1;\n\n for (let i = 0; i < imageCount; i++) {\n // Calculate angle based on spiral type\n let angle: number;\n let radius: number;\n\n if (spiralConfig.spiralType === 'golden') {\n // Golden spiral: each point separated by golden angle\n // Creates optimal distribution like sunflower seeds\n angle = i * GOLDEN_ANGLE * directionMultiplier + spiralConfig.startAngle;\n // Radius grows with square root of index for even distribution\n radius = this.calculateGoldenRadius(i, imageCount, maxRadius, spiralConfig.tightness);\n } else if (spiralConfig.spiralType === 'archimedean') {\n // Archimedean spiral: r = a + b*θ (constant spacing between arms)\n const theta = i * 0.5 * spiralConfig.tightness;\n angle = theta * directionMultiplier + spiralConfig.startAngle;\n radius = this.calculateArchimedeanRadius(theta, imageCount, maxRadius, spiralConfig.tightness);\n } else {\n // Logarithmic spiral: r = a * e^(b*θ) (self-similar, appears in nature)\n const theta = i * 0.3 * spiralConfig.tightness;\n angle = theta * directionMultiplier + spiralConfig.startAngle;\n radius = this.calculateLogarithmicRadius(theta, imageCount, maxRadius, spiralConfig.tightness);\n }\n\n // Convert polar to cartesian coordinates (store center position)\n const x = cx + Math.cos(angle) * radius;\n const y = cy + Math.sin(angle) * radius;\n\n // Calculate scale based on decay (center images larger) - use scaleDecay from image config\n const normalizedRadius = radius / maxRadius;\n const decayScale = scaleDecay > 0\n ? 1 - (normalizedRadius * scaleDecay * 0.5) // Max 50% size reduction\n : 1.0;\n\n // Apply variance\n const varianceScale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const combinedScale = decayScale * varianceScale;\n\n // Apply scaled image size\n const scaledImageSize = baseImageSize * combinedScale;\n\n // Clamp center positions to keep images within bounds\n // Use 16:9 aspect ratio (1.78) as maximum to handle most landscape images\n const estAspectRatio = 1.5; // 3:2 - balanced for mixed portrait/landscape\n const halfWidth = (scaledImageSize * estAspectRatio) / 2;\n const halfHeight = scaledImageSize / 2;\n const minX = padding + halfWidth;\n const maxX = width - padding - halfWidth;\n const minY = padding + halfHeight;\n const maxY = height - padding - halfHeight;\n\n const clampedX = Math.max(minX, Math.min(x, maxX));\n const clampedY = Math.max(minY, Math.min(y, maxY));\n\n // Rotation based on mode\n let rotation = 0;\n if (rotationMode === 'random') {\n const baseRotation = (angle * 180 / Math.PI) % 360;\n const rotationVariance = this.random(minRotation, maxRotation);\n rotation = spiralConfig.spiralType === 'golden'\n ? rotationVariance // Pure random for golden (more organic)\n : (baseRotation * 0.1 + rotationVariance * 0.9); // Slight directional bias for others\n } else if (rotationMode === 'tangent') {\n // Tangent rotation: align image to spiral curve\n rotation = this.calculateSpiralTangent(angle, radius, spiralConfig);\n }\n\n // Z-index: center images on top\n const zIndex = imageCount - i;\n\n layouts.push({\n id: i,\n x: clampedX,\n y: clampedY,\n rotation,\n scale: combinedScale,\n baseSize: scaledImageSize,\n zIndex\n });\n }\n\n return layouts;\n }\n\n /**\n * Calculate tangent angle for spiral curve at given position\n * This aligns the image along the spiral's direction of travel\n */\n private calculateSpiralTangent(\n angle: number,\n radius: number,\n spiralConfig: SpiralAlgorithmConfig\n ): number {\n // For different spiral types, the tangent calculation varies\n // The tangent angle is the derivative of the spiral equation\n\n let tangentAngle: number;\n\n if (spiralConfig.spiralType === 'golden') {\n // Golden spiral tangent is approximately perpendicular to radial line\n // Plus a small offset based on the golden angle\n tangentAngle = angle + Math.PI / 2;\n } else if (spiralConfig.spiralType === 'archimedean') {\n // Archimedean spiral: dr/dθ = constant = b\n // tan(ψ) = r / (dr/dθ) = r / b\n // For tightness = 1, b ≈ 1\n const b = 1 / spiralConfig.tightness;\n const psi = Math.atan(radius / b);\n tangentAngle = angle + psi;\n } else {\n // Logarithmic spiral: tangent makes constant angle with radial line\n // This angle depends on the growth rate b\n const b = 0.15 / spiralConfig.tightness;\n const psi = Math.atan(1 / b); // Constant pitch angle\n tangentAngle = angle + psi;\n }\n\n // Convert to degrees and normalize\n const degrees = (tangentAngle * 180 / Math.PI) % 360;\n\n // Adjust so 0 degrees means pointing right\n return degrees - 90;\n }\n\n /**\n * Calculate radius for golden spiral (Vogel's model)\n * Creates even distribution like sunflower seeds\n */\n private calculateGoldenRadius(\n index: number,\n totalImages: number,\n maxRadius: number,\n tightness: number\n ): number {\n // Vogel's model: r = c * sqrt(n)\n // Scaling factor to fit within maxRadius\n const scalingFactor = maxRadius / Math.sqrt(totalImages);\n const radius = scalingFactor * Math.sqrt(index) / tightness;\n return Math.min(radius, maxRadius);\n }\n\n /**\n * Calculate radius for Archimedean spiral\n * r = a + b*θ (constant spacing between arms)\n */\n private calculateArchimedeanRadius(\n theta: number,\n totalImages: number,\n maxRadius: number,\n tightness: number\n ): number {\n // Scale so that the last point is at maxRadius\n const maxTheta = totalImages * 0.5 * tightness;\n const normalizedTheta = theta / maxTheta;\n return normalizedTheta * maxRadius;\n }\n\n /**\n * Calculate radius for logarithmic (equiangular) spiral\n * r = a * e^(b*θ)\n */\n private calculateLogarithmicRadius(\n theta: number,\n totalImages: number,\n maxRadius: number,\n tightness: number\n ): number {\n // Parameters for logarithmic spiral\n const a = maxRadius * 0.05; // Starting radius\n const b = 0.15 / tightness; // Growth rate\n\n const radius = a * Math.exp(b * theta);\n\n // Normalize to fit within maxRadius\n const maxTheta = totalImages * 0.3 * tightness;\n const maxComputedRadius = a * Math.exp(b * maxTheta);\n\n return (radius / maxComputedRadius) * maxRadius;\n }\n\n /**\n * Utility: Generate random number between min and max\n */\n private random(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n }\n}\n","/**\n * ClusterPlacementLayout.ts\n * Generates organic cluster layouts with natural groupings\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, ClusterAlgorithmConfig, ImageConfig } from '../config/types';\n\ninterface ClusterCenter {\n x: number;\n y: number;\n spread: number; // Actual spread for this cluster (may vary if density='varied')\n}\n\ninterface ClusterLayoutOptions extends Partial<LayoutConfig> {\n fixedHeight?: number;\n}\n\nconst DEFAULT_CLUSTER_CONFIG: ClusterAlgorithmConfig = {\n clusterCount: 'auto',\n clusterSpread: 150,\n clusterSpacing: 200,\n density: 'uniform',\n overlap: 0.3,\n distribution: 'gaussian'\n};\n\nexport class ClusterPlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n\n constructor(config: LayoutConfig, imageConfig: ImageConfig = {}) {\n this.config = config;\n this.imageConfig = imageConfig;\n }\n\n /**\n * Generate cluster layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides (includes fixedHeight)\n * @returns Array of layout objects with position, rotation, scale\n */\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options: ClusterLayoutOptions = {}\n ): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n\n const clusterConfig = { ...DEFAULT_CLUSTER_CONFIG, ...this.config.cluster };\n const padding = this.config.spacing.padding;\n // Use fixedHeight if provided, otherwise use default 200\n const baseImageSize = options.fixedHeight ?? 200;\n\n // Get rotation config from image config\n const rotationMode = this.imageConfig.rotation?.mode ?? 'none';\n const minRotation = this.imageConfig.rotation?.range?.min ?? -15;\n const maxRotation = this.imageConfig.rotation?.range?.max ?? 15;\n\n // Get variance config from image config\n const varianceMin = this.imageConfig.sizing?.variance?.min ?? 1.0;\n const varianceMax = this.imageConfig.sizing?.variance?.max ?? 1.0;\n const hasVariance = varianceMin !== 1.0 || varianceMax !== 1.0;\n\n // Calculate number of clusters\n const clusterCount = this.calculateClusterCount(\n imageCount,\n clusterConfig.clusterCount,\n width,\n height,\n clusterConfig.clusterSpacing\n );\n\n // Generate cluster centers with spacing constraints\n const clusterCenters = this.generateClusterCenters(\n clusterCount,\n width,\n height,\n padding,\n clusterConfig\n );\n\n // Assign images to clusters (round-robin for even distribution)\n const imagesPerCluster = new Array(clusterCount).fill(0);\n for (let i = 0; i < imageCount; i++) {\n imagesPerCluster[i % clusterCount]++;\n }\n\n let imageIndex = 0;\n\n // Place images in each cluster\n for (let clusterIdx = 0; clusterIdx < clusterCount; clusterIdx++) {\n const cluster = clusterCenters[clusterIdx];\n const imagesInThisCluster = imagesPerCluster[clusterIdx];\n\n for (let i = 0; i < imagesInThisCluster; i++) {\n // Calculate position within cluster\n let offsetX: number;\n let offsetY: number;\n\n if (clusterConfig.distribution === 'gaussian') {\n // Gaussian distribution - most images near center, fewer at edges\n offsetX = this.gaussianRandom() * cluster.spread;\n offsetY = this.gaussianRandom() * cluster.spread;\n } else {\n // Uniform distribution within circle\n const angle = this.random(0, Math.PI * 2);\n const distance = this.random(0, cluster.spread);\n offsetX = Math.cos(angle) * distance;\n offsetY = Math.sin(angle) * distance;\n }\n\n // Apply overlap factor - pulls images closer to center and increases size\n const overlapMultiplier = 1 + clusterConfig.overlap * 0.5;\n const sizeMultiplier = 1 + clusterConfig.overlap * 0.3;\n\n // Reduce offset based on overlap (images cluster tighter)\n offsetX /= overlapMultiplier;\n offsetY /= overlapMultiplier;\n\n // Apply variance\n const varianceScale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const combinedScale = sizeMultiplier * varianceScale;\n\n // Calculate image size with overlap factor and variance\n const imageSize = baseImageSize * combinedScale;\n\n // Store center position (not top-left)\n let x = cluster.x + offsetX;\n let y = cluster.y + offsetY;\n\n // Boundary clamping with center position\n // Use 16:9 aspect ratio (1.78) as maximum to handle most landscape images\n const estAspectRatio = 1.5; // 3:2 - balanced for mixed portrait/landscape\n const halfWidth = (imageSize * estAspectRatio) / 2;\n const halfHeight = imageSize / 2;\n x = Math.max(padding + halfWidth, Math.min(x, width - padding - halfWidth));\n y = Math.max(padding + halfHeight, Math.min(y, height - padding - halfHeight));\n\n // Rotation - more variance for organic feel (only when mode is random)\n const rotation = rotationMode === 'random' ? this.random(minRotation, maxRotation) : 0;\n\n // Z-index: images closer to cluster center are on top\n const distanceFromCenter = Math.sqrt(offsetX * offsetX + offsetY * offsetY);\n const normalizedDistance = distanceFromCenter / cluster.spread;\n const zIndex = Math.round((1 - normalizedDistance) * 50) + 1;\n\n layouts.push({\n id: imageIndex,\n x,\n y,\n rotation,\n scale: combinedScale,\n baseSize: imageSize,\n zIndex\n });\n\n imageIndex++;\n }\n }\n\n return layouts;\n }\n\n /**\n * Calculate optimal number of clusters based on image count and container\n */\n private calculateClusterCount(\n imageCount: number,\n configCount: number | 'auto',\n width: number,\n height: number,\n clusterSpacing: number\n ): number {\n if (configCount !== 'auto') {\n return Math.max(1, Math.min(configCount, imageCount));\n }\n\n // Auto-calculate based on container size and image count\n // Aim for 5-15 images per cluster\n const idealImagesPerCluster = 8;\n const countByImages = Math.max(1, Math.ceil(imageCount / idealImagesPerCluster));\n\n // Also consider container size - how many clusters can fit\n const countBySpace = Math.floor(\n (width / clusterSpacing) * (height / clusterSpacing) * 0.6\n );\n\n return Math.max(1, Math.min(countByImages, countBySpace, 10));\n }\n\n /**\n * Generate cluster center positions with spacing constraints\n */\n private generateClusterCenters(\n count: number,\n width: number,\n height: number,\n padding: number,\n config: ClusterAlgorithmConfig\n ): ClusterCenter[] {\n const centers: ClusterCenter[] = [];\n const maxAttempts = 100;\n\n // Available area for cluster centers\n const minX = padding + config.clusterSpread;\n const maxX = width - padding - config.clusterSpread;\n const minY = padding + config.clusterSpread;\n const maxY = height - padding - config.clusterSpread;\n\n for (let i = 0; i < count; i++) {\n let bestCandidate: ClusterCenter | null = null;\n let bestMinDistance = -1;\n\n // Try multiple random positions and pick the one with best spacing\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const candidate = {\n x: this.random(minX, maxX),\n y: this.random(minY, maxY),\n spread: this.calculateClusterSpread(config)\n };\n\n // Calculate minimum distance to existing clusters\n let minDistance = Infinity;\n for (const existing of centers) {\n const dx = candidate.x - existing.x;\n const dy = candidate.y - existing.y;\n const distance = Math.sqrt(dx * dx + dy * dy);\n minDistance = Math.min(minDistance, distance);\n }\n\n // First cluster or better spacing\n if (centers.length === 0 || minDistance > bestMinDistance) {\n bestCandidate = candidate;\n bestMinDistance = minDistance;\n }\n\n // Good enough spacing found\n if (minDistance >= config.clusterSpacing) {\n break;\n }\n }\n\n if (bestCandidate) {\n centers.push(bestCandidate);\n }\n }\n\n return centers;\n }\n\n /**\n * Calculate spread for a cluster (may vary if density='varied')\n */\n private calculateClusterSpread(config: ClusterAlgorithmConfig): number {\n if (config.density === 'uniform') {\n return config.clusterSpread;\n }\n\n // Varied density: spread varies between 50% and 150% of config value\n return config.clusterSpread * this.random(0.5, 1.5);\n }\n\n /**\n * Generate a random number with approximately Gaussian distribution\n * Using Box-Muller transform\n */\n private gaussianRandom(): number {\n let u = 0, v = 0;\n while (u === 0) u = Math.random();\n while (v === 0) v = Math.random();\n\n const value = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);\n\n // Clamp to reasonable range (-3 to 3 std deviations)\n return Math.max(-3, Math.min(3, value)) / 3;\n }\n\n /**\n * Utility: Generate random number between min and max\n */\n private random(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n }\n}\n","/**\n * WavePlacementLayout.ts\n * Generates wave-based layouts for image cloud\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, ImageConfig } from '../config/types';\nimport { DEFAULT_WAVE_CONFIG } from '../config/defaults';\n\ninterface WaveLayoutOptions extends Partial<LayoutConfig> {\n fixedHeight?: number;\n}\n\nexport class WavePlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n\n constructor(config: LayoutConfig, imageConfig: ImageConfig = {}) {\n this.config = config;\n this.imageConfig = imageConfig;\n }\n\n /**\n * Generate wave layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides\n * @returns Array of layout objects with position, rotation, scale\n */\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options: WaveLayoutOptions = {}\n ): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n // Use fixedHeight if provided, otherwise use default 200\n const baseImageSize = options.fixedHeight ?? 200;\n const padding = this.config.spacing.padding ?? 50;\n\n // Get rotation config from image config\n const rotationMode = this.imageConfig.rotation?.mode ?? 'none';\n const minRotation = this.imageConfig.rotation?.range?.min ?? -15;\n const maxRotation = this.imageConfig.rotation?.range?.max ?? 15;\n\n // Get variance config from image config\n const varianceMin = this.imageConfig.sizing?.variance?.min ?? 1.0;\n const varianceMax = this.imageConfig.sizing?.variance?.max ?? 1.0;\n const hasVariance = varianceMin !== 1.0 || varianceMax !== 1.0;\n\n // Use override fixedHeight if provided, else baseImageSize\n const imageSize = options.fixedHeight ?? baseImageSize;\n\n // Get wave configuration, merging user config with defaults\n const waveConfig = {\n ...DEFAULT_WAVE_CONFIG,\n ...this.config.wave\n };\n\n const { rows, amplitude, frequency, phaseShift, synchronization } = waveConfig;\n\n // Calculate images per row (distribute evenly)\n const imagesPerRow = Math.ceil(imageCount / rows);\n\n // Estimate image width based on height and typical aspect ratio\n const estAspectRatio = 1.5; // 3:2 - balanced for mixed portrait/landscape\n const estImageWidth = imageSize * estAspectRatio;\n const halfImageWidth = estImageWidth / 2;\n\n // Calculate available horizontal space (accounting for image width at edges)\n // This ensures images don't get compressed against the edges\n const startX = padding + halfImageWidth;\n const endX = width - padding - halfImageWidth;\n const availableWidth = endX - startX;\n\n // Distribute images evenly between startX and endX\n const horizontalSpacing = imagesPerRow > 1 ? availableWidth / (imagesPerRow - 1) : 0;\n\n // Calculate vertical distribution to fill the screen\n // Row centerlines need room for amplitude swing above and below\n const minCenterY = padding + amplitude + (imageSize / 2);\n const maxCenterY = height - padding - amplitude - (imageSize / 2);\n const availableHeight = maxCenterY - minCenterY;\n\n // Calculate spacing between row centerlines\n const rowSpacing = rows > 1 ? availableHeight / (rows - 1) : 0;\n\n let imageIndex = 0;\n\n for (let rowIndex = 0; rowIndex < rows && imageIndex < imageCount; rowIndex++) {\n // Calculate base Y position (centerline) for this row - evenly distributed\n const baseY = rows === 1\n ? (minCenterY + maxCenterY) / 2 // Single row: center vertically\n : minCenterY + (rowIndex * rowSpacing);\n\n // Calculate phase offset based on synchronization mode\n let phase = 0;\n if (synchronization === 'offset') {\n phase = rowIndex * phaseShift;\n } else if (synchronization === 'alternating') {\n phase = rowIndex * Math.PI;\n }\n // If 'synchronized', phase remains 0\n\n // Place images along this wave row\n for (let imgInRow = 0; imgInRow < imagesPerRow && imageIndex < imageCount; imgInRow++) {\n // Calculate center position - evenly distributed within available space\n // For single image, center it; otherwise distribute from startX to endX\n const centerX = imagesPerRow === 1\n ? (startX + endX) / 2\n : startX + (imgInRow * horizontalSpacing);\n\n // Calculate wave displacement based on center position\n const waveY = this.calculateWaveY(centerX, width, amplitude, frequency, phase);\n\n // Store center position directly (CSS transform will handle centering)\n const x = centerX;\n const y = baseY + waveY;\n\n // Apply variance\n const varianceScale = hasVariance ? this.random(varianceMin, varianceMax) : 1.0;\n const scaledImageSize = imageSize * varianceScale;\n\n // Calculate rotation based on image.rotation.mode\n let rotation = 0;\n if (rotationMode === 'tangent') {\n // Follow wave tangent - images rotate to align with wave curve\n rotation = this.calculateRotation(centerX, width, amplitude, frequency, phase);\n } else if (rotationMode === 'random') {\n // Random rotation within configured range\n rotation = this.random(minRotation, maxRotation);\n }\n // If mode is 'none', rotation stays 0\n\n // Clamp center positions to keep images within bounds\n // Use 16:9 aspect ratio (1.78) as maximum to handle most landscape images\n const estAspectRatio = 1.5; // 3:2 - balanced for mixed portrait/landscape\n const halfWidth = (scaledImageSize * estAspectRatio) / 2;\n const halfHeight = scaledImageSize / 2;\n const minX = padding + halfWidth;\n const maxX = width - padding - halfWidth;\n const minY = padding + halfHeight;\n const maxY = height - padding - halfHeight;\n\n layouts.push({\n id: imageIndex,\n x: Math.max(minX, Math.min(x, maxX)),\n y: Math.max(minY, Math.min(y, maxY)),\n rotation,\n scale: varianceScale,\n baseSize: scaledImageSize,\n zIndex: imageIndex + 1\n });\n\n imageIndex++;\n }\n }\n\n return layouts;\n }\n\n /**\n * Calculate Y position displacement on wave curve\n * @param x - Horizontal position\n * @param containerWidth - Container width\n * @param amplitude - Wave amplitude\n * @param frequency - Wave frequency\n * @param phase - Phase offset\n * @returns Y displacement from baseline\n */\n private calculateWaveY(\n x: number,\n containerWidth: number,\n amplitude: number,\n frequency: number,\n phase: number\n ): number {\n // Normalize x to [0, 1] range\n const normalizedX = x / containerWidth;\n\n // Calculate wave: y = amplitude * sin(frequency * x * 2π + phase)\n return amplitude * Math.sin(frequency * normalizedX * 2 * Math.PI + phase);\n }\n\n /**\n * Calculate rotation based on wave tangent\n * @param x - Horizontal position\n * @param containerWidth - Container width\n * @param amplitude - Wave amplitude\n * @param frequency - Wave frequency\n * @param phase - Phase offset\n * @returns Rotation angle in degrees\n */\n private calculateRotation(\n x: number,\n containerWidth: number,\n amplitude: number,\n frequency: number,\n phase: number\n ): number {\n // Normalize x to [0, 1] range\n const normalizedX = x / containerWidth;\n\n // Derivative of sine wave: amplitude * frequency * cos(frequency * x * 2π + phase)\n const derivative = amplitude * frequency * 2 * Math.PI * Math.cos(frequency * normalizedX * 2 * Math.PI + phase) / containerWidth;\n\n // Convert derivative to rotation angle in degrees\n return Math.atan(derivative) * (180 / Math.PI);\n }\n\n /**\n * Estimate image width based on height\n /**\n * Utility: Generate random number between min and max\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Random number in range\n */\n private random(min: number, max: number): number {\n return Math.random() * (max - min) + min;\n }\n}\n","/**\n * hexagonGeometry.ts\n * Single source of truth for hexagonal grid math.\n * Used by clipPathGenerator.ts and HoneycombPlacementLayout.ts.\n */\n\n/** Reference height for the canonical hexagon definition (100px) */\nexport const HEXAGON_REF_HEIGHT = 100;\n\n/**\n * Regular flat-top hexagon reference points (refHeight = 100).\n * Circumradius r = 100/√3 so that height = r√3 = 100 exactly.\n * Width = 2r = 200/√3 ≈ 115.47. All 6 vertices are equidistant from center.\n *\n * x = r/2 = 50/√3 ≈ 28.87 (upper/lower inner)\n * x = 3r/2 = 150/√3 ≈ 86.60 (upper/lower outer)\n * x = 2r = 200/√3 ≈ 115.47 (mid right vertex)\n */\nconst _R = 100 / Math.sqrt(3);\nexport const HEXAGON_REF_POINTS: Array<[number, number]> = [\n [_R / 2, 0 ], // upper-left\n [3 * _R / 2, 0 ], // upper-right\n [2 * _R, 50 ], // right ← tiling key vertex\n [3 * _R / 2, 100], // lower-right\n [_R / 2, 100], // lower-left\n [0, 50 ], // left\n];\n\n/**\n * Col-step ratio = x of upper-right vertex / refHeight = (3r/2) / 100 = √3/2 ≈ 0.866.\n * (Previously 0.75 with the non-regular hexagon.)\n */\nexport const HEXAGON_COL_STEP_RATIO = HEXAGON_REF_POINTS[1][0] / HEXAGON_REF_HEIGHT; // √3/2 ≈ 0.866\n\n/** Row-offset ratio = y of right vertex / refHeight = 50/100 = 0.50 (unchanged) */\nexport const HEXAGON_ROW_OFFSET_RATIO = HEXAGON_REF_POINTS[2][1] / HEXAGON_REF_HEIGHT; // 0.50\n\n/**\n * Returns tiling parameters for a hexagon of the given height.\n * Derived from the canonical reference points, not hardcoded constants.\n */\nexport function getHexTilingParams(hexH: number): { colStep: number; rowOffset: number } {\n return {\n colStep: HEXAGON_COL_STEP_RATIO * hexH,\n rowOffset: HEXAGON_ROW_OFFSET_RATIO * hexH,\n };\n}\n\n/**\n * Converts cube coordinates (cx, cy, cz) to pixel center position.\n * @param cx - Cube coord x (cx + cy + cz = 0)\n * @param cy - Cube coord y\n * @param cz - Cube coord z\n * @param originX - Pixel origin X (container center)\n * @param originY - Pixel origin Y (container center)\n * @param hexH - Visual hex height (= imageHeight for height-relative clip path)\n */\nexport function hexCubeToPixel(\n cx: number, cy: number, _cz: number,\n originX: number, originY: number,\n hexH: number\n): { px: number; py: number } {\n const { colStep } = getHexTilingParams(hexH);\n return {\n px: originX + colStep * cx,\n py: originY + hexH * (cy + cx / 2),\n };\n}\n\n/** 6 cube direction vectors for clockwise ring traversal starting from top */\nexport const HEX_RING_DIRECTIONS: Array<[number, number, number]> = [\n [+1, 0, -1],\n [ 0, +1, -1],\n [-1, +1, 0],\n [-1, 0, +1],\n [ 0, -1, +1],\n [+1, -1, 0],\n];\n\n/**\n * Returns ordered cube coordinates for all cells in ring k, clockwise from the top.\n * Ring 0: [(0,0,0)]. Ring k: 6k cells.\n */\nexport function getHexRingCells(ring: number): Array<[number, number, number]> {\n if (ring === 0) return [[0, 0, 0]];\n const cells: Array<[number, number, number]> = [];\n let [cx, cy, cz] = [0, -ring, ring]; // start at top\n for (const [dx, dy, dz] of HEX_RING_DIRECTIONS) {\n for (let step = 0; step < ring; step++) {\n cells.push([cx, cy, cz]);\n cx += dx; cy += dy; cz += dz;\n }\n }\n return cells;\n}\n","/**\n * HoneycombPlacementLayout.ts\n * Places images in hexagonal rings, filling outward clockwise from center-top.\n * Default/hover clip paths are forced to hexagon height-relative by mergeConfig().\n */\n\nimport type { PlacementLayout, ImageLayout, ContainerBounds, LayoutConfig, ImageConfig } from '../config/types';\nimport { DEFAULT_HONEYCOMB_CONFIG } from '../config/defaults';\nimport { getHexRingCells, hexCubeToPixel } from '../utils/hexagonGeometry';\n\nexport class HoneycombPlacementLayout implements PlacementLayout {\n private config: LayoutConfig;\n\n // imageConfig intentionally not stored — honeycomb forces uniform sizing (rotation/variance\n // would break hex tiling). Kept as parameter for interface compatibility.\n constructor(config: LayoutConfig, _imageConfig: ImageConfig = {}) {\n this.config = config;\n }\n\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options: Partial<LayoutConfig> & { fixedHeight?: number } = {}\n ): ImageLayout[] {\n const layouts: ImageLayout[] = [];\n const { width, height } = containerBounds;\n const containerCX = width / 2;\n const containerCY = height / 2;\n\n const baseImageSize = options.fixedHeight ?? 200;\n\n // Merge honeycomb config with defaults\n const honeycombConfig = {\n ...DEFAULT_HONEYCOMB_CONFIG,\n ...this.config.honeycomb\n };\n\n const spacing = honeycombConfig.spacing ?? 0;\n\n // hexH is the pitch (hex height + gap). Since the clip path is height-relative,\n // the hex's visual height equals baseImageSize. We add spacing on top.\n // Note: baseImageSize is already capped to fit the container by LayoutEngine.calculateAdaptiveSize().\n const hexH = baseImageSize + spacing;\n\n let placed = 0;\n let ring = 0;\n\n while (placed < imageCount) {\n const cells = getHexRingCells(ring);\n\n for (const [cx, cy, cz] of cells) {\n if (placed >= imageCount) break;\n\n const { px, py } = hexCubeToPixel(cx, cy, cz, containerCX, containerCY, hexH);\n\n layouts.push({\n id: placed,\n x: px,\n y: py,\n rotation: 0,\n scale: 1.0,\n baseSize: baseImageSize,\n // Inner rings render above outer rings\n zIndex: Math.max(1, 100 - ring)\n });\n\n placed++;\n }\n\n ring++;\n }\n\n return layouts;\n }\n}\n","/**\n * LayoutEngine.ts\n * Generates layouts for image cloud using placement strategies\n *\n * Public API:\n * - generateLayout(imageCount, containerBounds, options)\n * - getOriginalState(imageId)\n * - reset()\n * - updateConfig(newConfig)\n */\n\nimport type { LayoutConfig, ImageLayout, ContainerBounds, PlacementLayout, AdaptiveSizingResult, ImageConfig, FixedModeHeight, ResponsiveBreakpoints } from '../config/types';\nimport { RandomPlacementLayout } from '../layouts/RandomPlacementLayout';\nimport { RadialPlacementLayout } from '../layouts/RadialPlacementLayout';\nimport { GridPlacementLayout } from '../layouts/GridPlacementLayout';\nimport { SpiralPlacementLayout } from '../layouts/SpiralPlacementLayout';\nimport { ClusterPlacementLayout } from '../layouts/ClusterPlacementLayout';\nimport { WavePlacementLayout } from '../layouts/WavePlacementLayout';\nimport { HoneycombPlacementLayout } from '../layouts/HoneycombPlacementLayout';\n\nexport interface LayoutEngineConfig {\n layout: LayoutConfig;\n image: ImageConfig;\n}\n\nexport class LayoutEngine {\n private config: LayoutConfig;\n private imageConfig: ImageConfig;\n private layouts: Map<number, ImageLayout>;\n private placementLayout: PlacementLayout;\n\n constructor(config: LayoutEngineConfig) {\n this.config = config.layout;\n this.imageConfig = config.image;\n\n this.layouts = new Map(); // Store original states by image ID\n\n // Initialize placement layout strategy\n this.placementLayout = this.initLayout();\n }\n\n /**\n * Initialize the appropriate placement layout based on config type\n * @returns Initialized placement layout\n */\n private initLayout(): PlacementLayout {\n switch (this.config.algorithm) {\n case 'radial':\n return new RadialPlacementLayout(this.config, this.imageConfig);\n case 'grid':\n return new GridPlacementLayout(this.config, this.imageConfig);\n case 'spiral':\n return new SpiralPlacementLayout(this.config, this.imageConfig);\n case 'cluster':\n return new ClusterPlacementLayout(this.config, this.imageConfig);\n case 'wave':\n return new WavePlacementLayout(this.config, this.imageConfig);\n case 'honeycomb':\n return new HoneycombPlacementLayout(this.config, this.imageConfig);\n case 'random':\n default:\n return new RandomPlacementLayout(this.config, this.imageConfig);\n }\n }\n\n /**\n * Generate layout positions for images\n * @param imageCount - Number of images to layout\n * @param containerBounds - Container dimensions {width, height}\n * @param options - Optional overrides for configuration (e.g. fixedHeight)\n * @returns Array of layout objects with position, rotation, scale\n */\n generateLayout(imageCount: number, containerBounds: ContainerBounds, options: Partial<LayoutConfig> = {}): ImageLayout[] {\n const layouts = this.placementLayout.generate(imageCount, containerBounds, options);\n\n // Store layouts for state retrieval\n layouts.forEach(layout => {\n this.layouts.set(layout.id, layout);\n });\n\n return layouts;\n }\n\n /**\n * Get the original layout state for an image\n * @param imageId - The image ID (number or string)\n * @returns Original layout state or undefined if not found\n */\n getOriginalState(imageId: number | string): ImageLayout | undefined {\n return this.layouts.get(Number(imageId));\n }\n\n /**\n * Reset all stored layouts\n */\n reset(): void {\n this.layouts.clear();\n }\n\n /**\n * Update config dynamically (useful for responsive changes)\n * @param newConfig - Updated configuration\n */\n updateConfig(newConfig: Partial<LayoutEngineConfig>): void {\n // Update layout config\n if (newConfig.layout) {\n Object.assign(this.config, newConfig.layout);\n\n // Reinitialize placement layout if algorithm changed\n if (newConfig.layout.algorithm && newConfig.layout.algorithm !== this.config.algorithm) {\n this.placementLayout = this.initLayout();\n }\n }\n\n // Update image config\n if (newConfig.image) {\n Object.assign(this.imageConfig, newConfig.image);\n }\n }\n\n /**\n * Get responsive breakpoints from layout config\n */\n private getBreakpoints(): ResponsiveBreakpoints {\n return this.config.responsive ?? {\n mobile: { maxWidth: 767 },\n tablet: { maxWidth: 1199 }\n };\n }\n\n /**\n * Resolve breakpoint name based on viewport width\n */\n resolveBreakpoint(viewportWidth: number): 'mobile' | 'tablet' | 'screen' {\n const breakpoints = this.getBreakpoints();\n\n if (viewportWidth <= breakpoints.mobile.maxWidth) {\n return 'mobile';\n }\n if (viewportWidth <= breakpoints.tablet.maxWidth) {\n return 'tablet';\n }\n return 'screen';\n }\n\n /**\n * Resolve the effective base height based on image config and current viewport\n * @param viewportWidth - Current viewport width\n * @returns Resolved base height or undefined if should auto-calculate (adaptive mode)\n */\n resolveBaseHeight(viewportWidth: number): number | undefined {\n const sizing = this.imageConfig.sizing;\n\n // If mode is adaptive (or not set), return undefined to signal auto-calculation\n if (!sizing || sizing.mode === 'adaptive') {\n return undefined;\n }\n\n // Fixed or responsive mode - use the height property\n const height = sizing.height;\n\n if (height === undefined) {\n return undefined; // No height specified, fall back to adaptive\n }\n\n if (typeof height === 'number') {\n return height;\n }\n\n // Responsive height for fixed mode\n const responsiveHeight = height as FixedModeHeight;\n const breakpoint = this.resolveBreakpoint(viewportWidth);\n\n // Fallback chain: specific breakpoint -> higher breakpoints -> any defined\n if (breakpoint === 'mobile') {\n return responsiveHeight.mobile ?? responsiveHeight.tablet ?? responsiveHeight.screen;\n }\n if (breakpoint === 'tablet') {\n return responsiveHeight.tablet ?? responsiveHeight.screen ?? responsiveHeight.mobile;\n }\n // screen\n return responsiveHeight.screen ?? responsiveHeight.tablet ?? responsiveHeight.mobile;\n }\n\n /**\n * Calculate adaptive image size based on container dimensions and image count\n * @param containerBounds - Container dimensions {width, height}\n * @param imageCount - Number of images to display\n * @param maxHeight - Maximum height constraint (upper bound)\n * @param viewportWidth - Current viewport width for baseHeight resolution\n * @returns Calculated sizing result with height\n */\n calculateAdaptiveSize(\n containerBounds: ContainerBounds,\n imageCount: number,\n maxHeight: number,\n viewportWidth: number\n ): AdaptiveSizingResult {\n const sizing = this.imageConfig.sizing;\n\n // Check if user specified a fixed height in image config\n const userBaseHeight = this.resolveBaseHeight(viewportWidth);\n\n // If user specified baseHeight (fixed/responsive mode), use it directly\n // Don't clamp to maxHeight since user explicitly chose this value\n if (userBaseHeight !== undefined) {\n return { height: userBaseHeight };\n }\n\n // Adaptive mode - auto-calculate based on container and image count\n const minSize = sizing?.minSize ?? 50;\n const maxSize = sizing?.maxSize ?? 400;\n const targetCoverage = this.config.targetCoverage ?? 0.6;\n const densityFactor = this.config.densityFactor ?? 1.0;\n\n const { width, height } = containerBounds;\n\n // Calculate area-based optimal size\n const containerArea = width * height;\n const targetArea = containerArea * targetCoverage;\n const areaPerImage = targetArea / imageCount;\n\n // Calculate height from area assuming 1.4 aspect ratio (landscape images)\n const aspectRatio = 1.4;\n let calculatedHeight = Math.sqrt(areaPerImage / aspectRatio);\n\n // Apply density factor\n calculatedHeight *= densityFactor;\n\n // Clamp to maximum height constraint\n calculatedHeight = Math.min(calculatedHeight, maxHeight);\n\n // Apply min/max constraints\n let finalHeight = this.clamp(calculatedHeight, minSize, maxSize);\n\n // 'minimize' behavior: force fit below minimum if needed (0.05 floor)\n if (finalHeight === minSize && calculatedHeight < minSize) {\n // Minimum floor is 5% of calculated size\n const floor = Math.max(minSize * 0.05, 20);\n finalHeight = Math.max(floor, calculatedHeight);\n }\n\n // Honeycomb: cap so the outermost ring stays within the container.\n // Must apply here (not in generate()) so img.style.height matches tiling pitch.\n if (this.config.algorithm === 'honeycomb') {\n finalHeight = Math.min(finalHeight, this.honeycombMaxImageHeight(imageCount, containerBounds));\n }\n\n return { height: finalHeight };\n }\n\n /**\n * Returns the largest image height at which all honeycomb rings fit within the container.\n * Spacing is 0 for this calculation (user spacing is additive on top of the image height;\n * any non-zero spacing only makes the constraint tighter).\n */\n private honeycombMaxImageHeight(imageCount: number, containerBounds: ContainerBounds): number {\n if (imageCount <= 1) return Infinity;\n\n // Find outermost ring: cells in rings 0..k = 1 + 3k(k+1)\n let maxRing = 0;\n let total = 1;\n while (total < imageCount) {\n maxRing++;\n total += 6 * maxRing;\n }\n\n const padding = this.config.spacing?.padding ?? 50;\n const spacing = this.config.honeycomb?.spacing ?? 0;\n const containerCX = containerBounds.width / 2;\n const containerCY = containerBounds.height / 2;\n\n // colStep = √3/2 ≈ 0.866 * hexH; hex half-width = 1/√3 ≈ 0.577 * imageSize\n const COL_STEP_RATIO = Math.sqrt(3) / 2;\n const HALF_WIDTH_RATIO = 1 / Math.sqrt(3);\n\n // Vertical: containerCY - maxRing*(size+spacing) - size/2 >= padding\n const maxV = (containerCY - padding - spacing * maxRing) / (maxRing + 0.5);\n\n // Horizontal: containerCX - COL_STEP_RATIO*maxRing*(size+spacing) - HALF_WIDTH_RATIO*size >= padding\n const maxH = (containerCX - padding - COL_STEP_RATIO * spacing * maxRing)\n / (COL_STEP_RATIO * maxRing + HALF_WIDTH_RATIO);\n\n return Math.max(10, Math.min(maxV, maxH));\n }\n\n /**\n * Utility: Clamp a value between min and max\n */\n private clamp(value: number, min: number, max: number): number {\n return Math.max(min, Math.min(max, value));\n }\n}","/**\n * Type definitions for Image Gallery Library\n */\n\n// ============================================================================\n// Core Data Types\n// ============================================================================\n\nexport interface ImageLayout {\n id: number;\n x: number;\n y: number;\n rotation: number;\n scale: number;\n baseSize: number;\n zIndex?: number;\n}\n\nexport interface ContainerBounds {\n width: number;\n height: number;\n}\n\nexport interface ResponsiveHeight {\n minWidth: number;\n height: number;\n}\n\nexport interface TransformParams {\n x?: number;\n y?: number;\n rotation?: number;\n scale?: number;\n}\n\n// ============================================================================\n// Loader Configuration\n// ============================================================================\n\n// === Static source types (shape-based, no 'type' discriminant) ===\n\nexport interface StaticUrlsSource {\n urls: string[];\n}\n\nexport interface StaticPathSource {\n path: string;\n files: string[];\n}\n\nexport interface StaticJsonSource {\n json: string;\n}\n\nexport type StaticSource = StaticUrlsSource | StaticPathSource | StaticJsonSource;\n\n// === Google Drive source types (shape-based) ===\n\nexport interface GoogleDriveFolderSource {\n folders: string[];\n recursive?: boolean;\n}\n\nexport interface GoogleDriveFilesSource {\n files: string[];\n}\n\nexport type GoogleDriveSource = GoogleDriveFolderSource | GoogleDriveFilesSource;\n\n// === Loader configs (inner config objects) ===\n\nexport interface StaticLoaderInnerConfig {\n sources: StaticSource[];\n validateUrls?: boolean;\n validationTimeout?: number;\n validationMethod?: 'head' | 'simple' | 'none';\n allowedExtensions?: string[];\n debugLogging?: boolean;\n}\n\nexport interface GoogleDriveLoaderInnerConfig {\n apiKey: string;\n sources: GoogleDriveSource[];\n apiEndpoint?: string;\n allowedExtensions?: string[];\n debugLogging?: boolean;\n}\n\n// === Loader entries (key-based identification) ===\n\nexport interface StaticLoaderEntry {\n static: StaticLoaderInnerConfig;\n}\n\nexport interface GoogleDriveLoaderEntry {\n googleDrive: GoogleDriveLoaderInnerConfig;\n}\n\nexport type LoaderEntry = StaticLoaderEntry | GoogleDriveLoaderEntry;\n\n// === Shared loader config ===\n\nexport interface SharedLoaderConfig {\n validateUrls?: boolean;\n validationTimeout?: number;\n validationMethod?: 'head' | 'simple' | 'none';\n allowedExtensions?: string[];\n}\n\n// === Debug configuration ===\n\nexport interface DebugConfig {\n enabled?: boolean; // General debug logging (was top-level `debug`)\n centers?: boolean; // Center position markers (was `layout.debugCenters`)\n loaders?: boolean; // Loader debug output (was `config.loaders.debugLogging`)\n}\n\n// === Config section ===\n\nexport interface ConfigSection {\n loaders?: SharedLoaderConfig;\n debug?: DebugConfig;\n}\n\n// ============================================================================\n// Image Configuration (sizing and rotation)\n// ============================================================================\n\n/**\n * Sizing mode:\n * - 'fixed': single explicit height for all breakpoints\n * - 'responsive': different heights per breakpoint (mobile/tablet/screen)\n * - 'adaptive': auto-calculates based on container size and image count\n */\nexport type SizingMode = 'fixed' | 'responsive' | 'adaptive';\n\n/**\n * Fixed mode height configuration with responsive breakpoints\n * At least one of mobile, tablet, or screen is required\n */\nexport interface FixedModeHeight {\n mobile?: number; // Height for mobile viewports (< responsive.mobile.maxWidth)\n tablet?: number; // Height for tablet viewports (< responsive.tablet.maxWidth)\n screen?: number; // Height for desktop/large screens (>= responsive.tablet.maxWidth)\n}\n\n/**\n * Legacy responsive base height configuration (kept for backward compatibility)\n */\nexport interface ResponsiveBaseHeight {\n default: number; // Base height for large screens\n tablet?: number; // Height for tablet\n mobile?: number; // Height for mobile\n}\n\n/**\n * Image variance configuration\n * Controls random size variation applied to images\n */\nexport interface ImageVarianceConfig {\n min: number; // 0.25-1 (e.g., 0.8)\n max: number; // 1-1.75 (e.g., 1.2)\n}\n\n/**\n * Image sizing configuration\n */\nexport interface ImageSizingConfig {\n mode: SizingMode; // Required: 'fixed', 'responsive', or 'adaptive'\n height?: number | FixedModeHeight; // Fixed/responsive mode: explicit heights\n minSize?: number; // Adaptive mode only: minimum image height (default: 50)\n maxSize?: number; // Adaptive mode only: maximum image height (default: 400)\n variance?: ImageVarianceConfig; // Size variance (min: 0.25-1, max: 1-1.75)\n}\n\n/**\n * Image rotation mode\n */\nexport type ImageRotationMode = 'none' | 'random' | 'tangent';\n\n/**\n * Image rotation range configuration\n */\nexport interface ImageRotationRange {\n min: number; // Negative degrees (-180 to 0)\n max: number; // Positive degrees (0 to 180)\n}\n\n/**\n * Image rotation configuration\n */\nexport interface ImageRotationConfig {\n mode: ImageRotationMode; // default: 'none'\n range?: ImageRotationRange; // Range for random mode\n}\n\n/**\n * Combined image configuration\n */\nexport interface ImageConfig {\n sizing?: ImageSizingConfig;\n rotation?: ImageRotationConfig;\n}\n\n// ============================================================================\n// Layout Configuration\n// ============================================================================\n\nexport interface AdaptiveSizingResult {\n height: number; // Calculated image height\n}\n\n/**\n * Responsive breakpoints configuration for layout\n * Defines viewport width thresholds for mobile and tablet\n */\nexport interface ResponsiveBreakpoints {\n mobile: { maxWidth: number }; // Default: 767\n tablet: { maxWidth: number }; // Default: 1199\n // screen is implicitly > tablet.maxWidth\n}\n\n// Legacy responsive height uses the existing ResponsiveHeight interface (minWidth, height)\n\nexport interface LayoutSpacingConfig {\n padding: number;\n}\n\n// Legacy interface kept for backward compatibility in LayoutEngine\nexport interface LegacyLayoutRotationConfig {\n enabled: boolean;\n range: {\n min: number;\n max: number;\n };\n}\n\n// ============================================================================\n// Algorithm-Specific Configuration\n// ============================================================================\n\nexport interface GridAlgorithmConfig {\n columns: number | 'auto';\n rows: number | 'auto';\n stagger: 'none' | 'row' | 'column';\n jitter: number;\n overlap: number;\n fillDirection: 'row' | 'column';\n alignment: 'start' | 'center' | 'end';\n gap: number;\n overflowOffset: number; // 0-0.5, percentage of cell size for stacking overflow images (default: 0.25)\n}\n\nexport interface RadialAlgorithmConfig {\n tightness: number; // Ring spacing multiplier (default: 1.0). Higher = more spread, lower = tighter.\n}\n\nexport interface SpiralAlgorithmConfig {\n spiralType: 'golden' | 'archimedean' | 'logarithmic';\n direction: 'clockwise' | 'counterclockwise';\n tightness: number;\n scaleDecay: number;\n startAngle: number;\n}\n\nexport interface ClusterAlgorithmConfig {\n clusterCount: number | 'auto';\n clusterSpread: number;\n clusterSpacing: number;\n density: 'uniform' | 'varied';\n overlap: number;\n distribution: 'gaussian' | 'uniform';\n}\n\nexport interface WaveAlgorithmConfig {\n rows: number;\n amplitude: number;\n frequency: number;\n phaseShift: number;\n synchronization: 'offset' | 'synchronized' | 'alternating';\n // Note: Image rotation along wave is now controlled via image.rotation.mode = 'tangent'\n}\n\nexport type LayoutAlgorithm = 'random' | 'radial' | 'grid' | 'spiral' | 'cluster' | 'wave' | 'honeycomb';\n\nexport interface HoneycombAlgorithmConfig {\n spacing?: number; // extra gap in pixels beyond edge-to-edge (default: 0)\n}\n\nexport interface LayoutConfig {\n algorithm: LayoutAlgorithm;\n spacing: LayoutSpacingConfig;\n scaleDecay?: number; // For Radial/Spiral - progressive size reduction (0-1, default: 0)\n responsive?: ResponsiveBreakpoints; // Viewport width breakpoints (mobile/tablet)\n targetCoverage?: number; // 0-1, for adaptive sizing (default: 0.6)\n densityFactor?: number; // Controls center point spacing (default: 1.0)\n radial?: RadialAlgorithmConfig;\n grid?: GridAlgorithmConfig;\n spiral?: SpiralAlgorithmConfig;\n cluster?: ClusterAlgorithmConfig;\n wave?: WaveAlgorithmConfig;\n honeycomb?: HoneycombAlgorithmConfig;\n}\n\n// ============================================================================\n// Animation Configuration\n// ============================================================================\n\n// Entry Path Types\nexport type EntryPathType = 'linear' | 'arc' | 'bounce' | 'elastic' | 'wave';\n\nexport interface BouncePathConfig {\n overshoot: number; // 0.1-0.3, how far past target (default: 0.15)\n bounces: 1 | 2 | 3; // number of bounces before settling (default: 1)\n decayRatio: number; // 0.3-0.7, each bounce is this % of previous (default: 0.5)\n}\n\nexport type BouncePreset = 'energetic' | 'playful' | 'subtle';\n\nexport interface ElasticPathConfig {\n stiffness: number; // 100-500, higher = faster oscillation (default: 200)\n damping: number; // 10-50, higher = fewer oscillations (default: 20)\n mass: number; // 0.5-3, higher = slower, more momentum (default: 1)\n oscillations: number; // 2-5, visible oscillation count (default: 3)\n}\n\nexport type ElasticPreset = 'gentle' | 'bouncy' | 'wobbly' | 'snappy';\n\nexport interface WavePathConfig {\n amplitude: number; // 20-100px, wave height (default: 40)\n frequency: number; // 1-4, number of complete waves (default: 2)\n decay: boolean; // true = wave diminishes toward target (default: true)\n decayRate: number; // 0.5-1, how fast amplitude decreases (default: 0.8)\n phase: number; // 0-2π, starting phase offset (default: 0)\n}\n\nexport type WavePathPreset = 'gentle' | 'playful' | 'serpentine' | 'flutter';\n\nexport interface EntryPathConfig {\n type: EntryPathType;\n // Preset shortcuts (type-specific)\n bouncePreset?: BouncePreset;\n elasticPreset?: ElasticPreset;\n wavePreset?: WavePathPreset;\n // Type-specific detailed configs (override presets)\n bounce?: Partial<BouncePathConfig>;\n elastic?: Partial<ElasticPathConfig>;\n wave?: Partial<WavePathConfig>;\n}\n\n// Entry Rotation Types\nexport type EntryRotationMode = 'none' | 'settle' | 'spin' | 'wobble' | 'random';\n\nexport interface EntryRotationConfig {\n mode: EntryRotationMode;\n startRotation?: number | { min: number; max: number }; // For 'settle' mode\n spinCount?: number; // For 'spin' mode (default: 1)\n direction?: 'clockwise' | 'counterclockwise' | 'auto' | 'random'; // For 'spin' mode\n wobble?: {\n amplitude: number; // degrees of oscillation (default: 15)\n frequency: number; // oscillations during animation (default: 3)\n decay: boolean; // whether oscillation diminishes (default: true)\n };\n}\n\n// Entry Scale Types\nexport type EntryScaleMode = 'none' | 'grow' | 'shrink' | 'pop' | 'random';\n\nexport interface EntryScalePopConfig {\n overshoot: number; // How much to overshoot final scale (1.1-1.5, default: 1.2)\n bounces: number; // Number of bounces before settling (1-3, default: 1)\n}\n\nexport interface EntryScaleConfig {\n mode: EntryScaleMode;\n startScale?: number; // Fixed start scale for grow/shrink (0.1-4.0)\n range?: { min: number; max: number }; // Random range for 'random' mode\n pop?: EntryScalePopConfig; // Config for 'pop' mode\n}\n\nexport type EntryStartPosition =\n | 'nearest-edge'\n | 'top'\n | 'bottom'\n | 'left'\n | 'right'\n | 'center'\n | 'random-edge'\n | 'circular';\n\nexport interface EntryCircularConfig {\n radius?: number | string; // pixels or percentage like '120%', default: '120%' of container diagonal\n distribution?: 'even' | 'random'; // default: 'even'\n}\n\nexport interface EntryStartConfig {\n position: EntryStartPosition;\n offset?: number; // pixels beyond edge, default: 100\n circular?: EntryCircularConfig;\n}\n\nexport interface EntryTimingConfig {\n duration: number; // default: 600ms\n}\n\nexport interface EntryAnimationConfig {\n start: EntryStartConfig;\n timing: EntryTimingConfig;\n easing: string; // CSS easing, default: 'cubic-bezier(0.25, 1, 0.5, 1)'\n path?: EntryPathConfig; // Animation path type (linear, bounce, elastic, wave)\n rotation?: EntryRotationConfig; // Entry rotation animation\n scale?: EntryScaleConfig; // Entry scale animation\n}\n\nexport interface AnimationEasingConfig {\n default: string;\n bounce: string;\n focus: string;\n}\n\nexport interface AnimationQueueConfig {\n enabled: boolean;\n interval: number;\n}\n\nexport interface AnimationConfig {\n duration: number;\n easing: AnimationEasingConfig;\n queue: AnimationQueueConfig;\n entry?: EntryAnimationConfig;\n idle?: IdleAnimationConfig;\n}\n\n// ============================================================================\n// Idle Animation Configuration\n// ============================================================================\n\nexport type IdleAnimationType = 'wiggle' | 'pulse' | 'blink' | 'spin' | 'custom' | 'none';\nexport type IdleSyncMode = 'together' | 'random';\n\nexport interface IdleWiggleConfig {\n maxAngle: number;\n speed: number;\n sync: IdleSyncMode;\n}\n\nexport interface IdlePulseConfig {\n minScale: number;\n maxScale: number;\n speed: number;\n sync: IdleSyncMode;\n}\n\nexport interface IdleBlinkConfig {\n onRatio: number;\n speed: number;\n style: 'snap' | 'fade';\n}\n\nexport interface IdleSpinConfig {\n speed: number;\n direction: 'clockwise' | 'counterclockwise';\n}\n\nexport interface IdleCustomContext {\n element: HTMLElement;\n index: number;\n totalImages: number;\n}\n\nexport type IdleCustomAnimationFn = (ctx: IdleCustomContext) => Animation | (() => void);\n\nexport interface IdleAnimationConfig {\n type: IdleAnimationType;\n wiggle?: IdleWiggleConfig;\n pulse?: IdlePulseConfig;\n blink?: IdleBlinkConfig;\n spin?: IdleSpinConfig;\n custom?: IdleCustomAnimationFn;\n startDelay?: number;\n}\n\n// ============================================================================\n// Interaction Configuration\n// ============================================================================\n\nexport interface FocusInteractionConfig {\n scalePercent: number; // Percentage of container (0-1 as fraction, 1-100 as percent)\n zIndex: number;\n animationDuration?: number;\n}\n\nexport interface NavigationInteractionConfig {\n keyboard?: boolean;\n swipe?: boolean;\n mouseWheel?: boolean;\n}\n\nexport interface InteractionConfig {\n focus: FocusInteractionConfig;\n navigation?: NavigationInteractionConfig;\n dragging?: boolean;\n}\n\n// ============================================================================\n// UI Configuration\n// ============================================================================\n\nexport interface UIConfig {\n showLoadingSpinner: boolean;\n showImageCounter?: boolean;\n loadingElement?: string | HTMLElement;\n errorElement?: string | HTMLElement;\n counterElement?: string | HTMLElement;\n showNavButtons?: boolean;\n prevButtonElement?: string | HTMLElement;\n nextButtonElement?: string | HTMLElement;\n showFocusOutline?: boolean;\n}\n\n// ============================================================================\n// Main Gallery Configuration\n// ============================================================================\n\nexport interface ImageCloudConfig {\n loaders: LoaderEntry[];\n config: ConfigSection;\n image: ImageConfig;\n layout: LayoutConfig;\n animation: AnimationConfig;\n interaction: InteractionConfig;\n ui: UIConfig;\n styling?: ImageStylingConfig;\n}\n\n// Backwards compatibility alias\nexport type GalleryConfig = ImageCloudConfig;\n\nexport interface ImageCloudOptions {\n container?: string | HTMLElement;\n images?: string[];\n loaders?: LoaderEntry[];\n config?: ConfigSection;\n image?: Partial<ImageConfig>;\n layout?: Partial<LayoutConfig>;\n animation?: Partial<AnimationConfig>;\n interaction?: Partial<InteractionConfig>;\n ui?: Partial<UIConfig>;\n /** @deprecated Use `ui` instead of `rendering.ui` */\n rendering?: { ui?: Partial<UIConfig> };\n styling?: Partial<ImageStylingConfig>;\n}\n\n// Backwards compatibility alias\nexport type ImageGalleryOptions = ImageCloudOptions;\n\n// ============================================================================\n// Legacy Configuration Types (for backward compatibility)\n// ============================================================================\n\nexport interface LegacyLayoutConfig {\n baseImageSize?: number;\n rotationRange?: { min: number; max: number };\n type?: 'random' | 'radial';\n sizeVarianceMin?: number;\n sizeVarianceMax?: number;\n responsiveHeights?: ResponsiveHeight[];\n padding?: number;\n minSpacing?: number;\n minRotation?: number;\n maxRotation?: number;\n}\n\nexport interface LegacyAnimationConfig {\n duration?: number;\n queueInterval?: number;\n easing?: string;\n bounceEasing?: string;\n}\n\nexport interface LegacyZoomConfig {\n focusScale?: number;\n mobileScale?: number; // Deprecated: use scaleTo/scalePercent instead\n focusZIndex?: number;\n}\n\nexport interface LegacyConfig {\n layout?: LegacyLayoutConfig;\n animation?: LegacyAnimationConfig;\n zoom?: LegacyZoomConfig;\n googleDrive?: {\n apiKey?: string;\n apiEndpoint?: string;\n imageExtensions?: string[];\n };\n breakpoints?: {\n mobile?: number;\n tablet?: number;\n desktop?: number;\n };\n isMobile?: () => boolean;\n ui?: {\n showLoadingSpinner?: boolean;\n };\n}\n\nexport interface LegacyImageGalleryOptions {\n containerId?: string;\n loaderType?: 'googleDrive' | 'static';\n folderUrl?: string;\n googleDrive?: {\n apiKey?: string;\n };\n}\n\n// ============================================================================\n// Interface Dependencies\n// ============================================================================\n\nexport interface PlacementLayout {\n generate(\n imageCount: number,\n containerBounds: ContainerBounds,\n options?: Partial<LayoutConfig>\n ): ImageLayout[];\n}\n\n/**\n * ImageFilter interface for filtering images by extension\n * Implemented by the ImageFilter class in loaders/ImageFilter.ts\n */\nexport interface IImageFilter {\n isAllowed(filename: string): boolean;\n getAllowedExtensions(): string[];\n}\n\n/**\n * ImageLoader interface with consistent lifecycle pattern:\n * 1. Constructor - Initialize with required parameters, throw if missing\n * 2. prepare(filter) - Async discovery of images, accepts filter\n * 3. imagesLength() - Return count of images (after prepare)\n * 4. imageURLs() - Return ordered list of URLs (after prepare)\n */\nexport interface ImageLoader {\n /**\n * Async preparation - discovers images and applies filter\n * Succeeds even if 0 images found (gallery handles empty state)\n * @param filter - Filter to apply to discovered images\n */\n prepare(filter: IImageFilter): Promise<void>;\n\n /**\n * Get the number of discovered images\n * @throws Error if called before prepare() completes\n */\n imagesLength(): number;\n\n /**\n * Get the ordered list of image URLs\n * @throws Error if called before prepare() completes\n */\n imageURLs(): string[];\n\n /**\n * Check if the loader has been prepared\n */\n isPrepared(): boolean;\n}\n\nexport interface GoogleDriveFile {\n id: string;\n name: string;\n mimeType: string;\n parents?: string[];\n}\n\nexport interface GoogleDriveResponse {\n files: GoogleDriveFile[];\n nextPageToken?: string;\n}\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];\n};\n\n// ============================================================================\n// Image Styling Configuration\n// ============================================================================\n\nexport type ShadowPreset = 'none' | 'sm' | 'md' | 'lg' | 'glow';\n\nexport type BorderStyle =\n | 'solid' // Most common - continuous line\n | 'dashed' // Series of dashes\n | 'dotted' // Series of dots\n | 'double' // Two parallel lines\n | 'none' // No border\n | 'groove' // 3D carved into page\n | 'ridge' // 3D raised from page\n | 'inset' // 3D embedded look\n | 'outset' // 3D raised look\n | 'hidden'; // Same as none (for table border conflict resolution)\n\nexport interface BorderConfig {\n width?: number; // pixels, default: 0\n color?: string; // CSS color, default: '#000'\n radius?: number; // pixels, default: 8\n style?: BorderStyle; // default: 'solid'\n}\n\nexport interface DropShadowConfig {\n x: number;\n y: number;\n blur: number;\n color: string;\n}\n\nexport interface FilterConfig {\n grayscale?: number; // 0-1\n blur?: number; // pixels\n brightness?: number; // multiplier (1 = normal)\n contrast?: number; // multiplier\n saturate?: number; // multiplier\n opacity?: number; // 0-1\n sepia?: number; // 0-1\n hueRotate?: number; // degrees\n invert?: number; // 0-1\n dropShadow?: DropShadowConfig | string;\n}\n\nexport interface OutlineConfig {\n width?: number; // pixels\n color?: string; // CSS color\n style?: BorderStyle; // reuses BorderStyle type\n offset?: number; // pixels\n}\n\nexport type ClipPathShape =\n | 'circle'\n | 'square'\n | 'triangle'\n | 'pentagon'\n | 'hexagon'\n | 'octagon'\n | 'diamond';\n\nexport type ClipPathMode = 'percent' | 'height-relative';\n\nexport interface ClipPathConfig {\n shape: ClipPathShape;\n mode?: ClipPathMode; // defaults to 'percent'\n}\n\nexport interface ImageStyleState {\n // CSS class names (space-separated string or array)\n className?: string | string[];\n\n // Border (shorthand applies to all sides)\n border?: BorderConfig;\n borderTop?: Partial<BorderConfig>;\n borderRight?: Partial<BorderConfig>;\n borderBottom?: Partial<BorderConfig>;\n borderLeft?: Partial<BorderConfig>;\n\n // Per-corner border radius (overrides border.radius for specific corners)\n borderRadiusTopLeft?: number;\n borderRadiusTopRight?: number;\n borderRadiusBottomRight?: number;\n borderRadiusBottomLeft?: number;\n\n // Shadow (preset name or custom CSS string)\n shadow?: ShadowPreset | string;\n\n // Filters\n filter?: FilterConfig;\n\n // Other properties\n opacity?: number; // 0-1\n cursor?: string; // CSS cursor value\n outline?: OutlineConfig;\n objectFit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';\n aspectRatio?: string; // e.g., '16/9', '1/1'\n clipPath?: ClipPathShape | string | ClipPathConfig; // Predefined shape, custom clip-path string, or config object\n}\n\nexport interface ImageStylingConfig {\n default?: ImageStyleState;\n hover?: Partial<ImageStyleState>; // inherits from default\n focused?: Partial<ImageStyleState>; // inherits from default\n}\n\n// ============================================================================\n// Focus Animation Types (Cross-Animation Support)\n// ============================================================================\n\n/**\n * State machine states for zoom/focus animations\n */\nexport enum ZoomState {\n IDLE = 'idle', // No focus, no animations\n FOCUSING = 'focusing', // Single image animating in\n FOCUSED = 'focused', // Stable focused state\n UNFOCUSING = 'unfocusing', // Single image animating out\n CROSS_ANIMATING = 'cross_animating' // Two images: one out, one in\n}\n\n/**\n * Handle for a cancellable animation using Web Animations API\n */\nexport interface AnimationHandle {\n id: string;\n element: HTMLElement;\n animation: Animation;\n fromState: TransformParams;\n toState: TransformParams;\n startTime: number;\n duration: number;\n}\n\n/**\n * Snapshot of an element's current transform state\n * Used for capturing position mid-animation\n */\nexport interface AnimationSnapshot {\n x: number;\n y: number;\n rotation: number;\n scale: number;\n}\n\n/**\n * Tracks an image that is currently animating\n */\nexport interface AnimatingImage {\n element: HTMLElement;\n originalState: ImageLayout;\n animationHandle: AnimationHandle;\n direction: 'in' | 'out';\n originalWidth?: number;\n originalHeight?: number;\n}","/**\n * Maps predefined shape names to CSS clip-path values.\n * Supports both percentage-based (responsive) and height-relative (aspect-ratio aware) modes.\n */\n\nimport type { ClipPathShape } from '../config/types';\nimport { HEXAGON_REF_HEIGHT, HEXAGON_REF_POINTS } from './hexagonGeometry';\n\nconst CLIP_PATH_SHAPES: Record<ClipPathShape, string> = {\n // Geometric shapes - uses percentages for responsive sizing\n circle: 'circle(50%)',\n square: 'polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)',\n triangle: 'polygon(50% 0%, 100% 100%, 0% 100%)',\n pentagon: 'polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%)',\n hexagon: 'polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%)',\n octagon: 'polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%)',\n diamond: 'polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)'\n};\n\n/**\n * Height-relative shapes: normalized at a reference height of 100px\n * Points are in pixels relative to the reference height\n * When applied, coordinates scale based on actual image height\n */\ninterface HeightRelativeShape {\n refHeight: number;\n points: Array<[number, number]>; // [x, y] coordinates\n}\n\nconst CLIP_PATH_SHAPES_HEIGHT_RELATIVE: Record<ClipPathShape, HeightRelativeShape> = {\n // Circle - uses radius in pixels (refHeight of 100px = 50px radius)\n circle: {\n refHeight: 100,\n points: [] // Special case: handled separately\n },\n // Square - maintains perfect aspect ratio (always 1:1)\n square: {\n refHeight: 100,\n points: [[0, 0], [100, 0], [100, 100], [0, 100]]\n },\n // Triangle - isosceles triangle\n triangle: {\n refHeight: 100,\n points: [[50, 0], [100, 100], [0, 100]]\n },\n // Pentagon - regular pentagon\n pentagon: {\n refHeight: 100,\n points: [[50, 0], [100, 38], [82, 100], [18, 100], [0, 38]]\n },\n // Hexagon - regular hexagon (reference points imported from hexagonGeometry)\n hexagon: {\n refHeight: HEXAGON_REF_HEIGHT,\n points: HEXAGON_REF_POINTS\n },\n // Octagon - regular octagon\n octagon: {\n refHeight: 100,\n points: [[30, 0], [70, 0], [100, 30], [100, 70], [70, 100], [30, 100], [0, 70], [0, 30]]\n },\n // Diamond - 45-degree rotated square\n diamond: {\n refHeight: 100,\n points: [[50, 0], [100, 50], [50, 100], [0, 50]]\n }\n};\n\n/**\n * Resolves a shape name or custom clip-path string to a valid CSS clip-path value.\n * @param shape - Predefined shape name or custom clip-path string\n * @returns Valid CSS clip-path value\n */\nexport function getClipPath(shape: ClipPathShape | string | undefined): string | undefined {\n if (!shape) return undefined;\n\n // Check if it's a predefined shape\n if (shape in CLIP_PATH_SHAPES) {\n return CLIP_PATH_SHAPES[shape as ClipPathShape];\n }\n\n // Treat as custom clip-path string (e.g., 'polygon(...)' or 'inset(...)')\n return shape;\n}\n\n/**\n * Returns available predefined shape names for UI/documentation.\n */\nexport function getAvailableShapes(): ClipPathShape[] {\n return Object.keys(CLIP_PATH_SHAPES) as ClipPathShape[];\n}\n\n/**\n * Calculates height-relative clip-path string for a given shape and image dimensions.\n * Scales the reference shape definition by (imageHeight / refHeight) and centers it horizontally.\n * @param shape - Predefined shape name\n * @param imageHeight - Actual image height in pixels\n * @param imageWidth - Actual image width in pixels (used to center the shape horizontally)\n * @returns CSS clip-path value (e.g., 'circle(50px)' or 'polygon(...)')\n */\nexport function calculateHeightRelativeClipPath(shape: ClipPathShape, imageHeight: number, imageWidth?: number): string {\n const shapeDef = CLIP_PATH_SHAPES_HEIGHT_RELATIVE[shape];\n if (!shapeDef) return '';\n\n const scale = imageHeight / shapeDef.refHeight;\n\n // Special case: circle uses circle() function with radius\n if (shape === 'circle') {\n const radius = Math.round(50 * scale * 100) / 100; // Round to 2 decimals\n return `circle(${radius}px)`;\n }\n\n // Calculate offsets to center the shape's bounding box within the image.\n // Compute the bounding box from the actual points (handles non-square shapes\n // like the regular hexagon whose width ≠ refHeight).\n const xs = shapeDef.points.map(([x]) => x);\n const ys = shapeDef.points.map(([, y]) => y);\n const shapeCenterX = ((Math.min(...xs) + Math.max(...xs)) / 2) * scale;\n const shapeCenterY = ((Math.min(...ys) + Math.max(...ys)) / 2) * scale;\n\n // Fallback imageWidth: use the scaled bounding box width when unknown\n const scaledBBoxWidth = (Math.max(...xs) - Math.min(...xs)) * scale;\n\n // Image's center\n const imageCenterX = (imageWidth ?? scaledBBoxWidth) / 2;\n const imageCenterY = imageHeight / 2;\n\n // Offsets to move shape center to image center\n const horizontalOffset = imageCenterX - shapeCenterX;\n const verticalOffset = imageCenterY - shapeCenterY;\n\n // For polygon shapes, scale all points and format as polygon()\n const scaledPoints = shapeDef.points.map(([x, y]) => {\n const scaledX = Math.round((x * scale + horizontalOffset) * 100) / 100;\n const scaledY = Math.round((y * scale + verticalOffset) * 100) / 100;\n return `${scaledX}px ${scaledY}px`;\n });\n\n return `polygon(${scaledPoints.join(', ')})`;\n}\n","/**\n * Style utilities for image styling configuration\n */\n\nimport type { ImageStyleState, FilterConfig, BorderConfig, ShadowPreset, DropShadowConfig, ClipPathConfig, ClipPathShape } from '../config/types';\nimport { SHADOW_PRESETS } from '../config/defaults';\nimport { getClipPath, calculateHeightRelativeClipPath } from './clipPathGenerator';\n\n/**\n * Check if a value is a known shadow preset\n */\nfunction isShadowPreset(value: string): value is ShadowPreset {\n return value in SHADOW_PRESETS;\n}\n\n/**\n * Resolve shadow value - converts preset names to CSS values,\n * passes custom CSS strings through directly\n */\nexport function resolveShadow(shadow: ShadowPreset | string | undefined): string {\n if (!shadow) return SHADOW_PRESETS.md;\n if (isShadowPreset(shadow)) return SHADOW_PRESETS[shadow];\n return shadow; // Custom CSS string\n}\n\n/**\n * Build CSS filter string from FilterConfig\n */\nexport function buildFilterString(filter: FilterConfig | undefined): string {\n if (!filter) return '';\n\n const parts: string[] = [];\n\n if (filter.grayscale !== undefined) {\n parts.push(`grayscale(${filter.grayscale})`);\n }\n if (filter.blur !== undefined) {\n parts.push(`blur(${filter.blur}px)`);\n }\n if (filter.brightness !== undefined) {\n parts.push(`brightness(${filter.brightness})`);\n }\n if (filter.contrast !== undefined) {\n parts.push(`contrast(${filter.contrast})`);\n }\n if (filter.saturate !== undefined) {\n parts.push(`saturate(${filter.saturate})`);\n }\n if (filter.opacity !== undefined) {\n parts.push(`opacity(${filter.opacity})`);\n }\n if (filter.sepia !== undefined) {\n parts.push(`sepia(${filter.sepia})`);\n }\n if (filter.hueRotate !== undefined) {\n parts.push(`hue-rotate(${filter.hueRotate}deg)`);\n }\n if (filter.invert !== undefined) {\n parts.push(`invert(${filter.invert})`);\n }\n if (filter.dropShadow !== undefined) {\n if (typeof filter.dropShadow === 'string') {\n parts.push(`drop-shadow(${filter.dropShadow})`);\n } else {\n const ds = filter.dropShadow as DropShadowConfig;\n parts.push(`drop-shadow(${ds.x}px ${ds.y}px ${ds.blur}px ${ds.color})`);\n }\n }\n\n return parts.join(' ');\n}\n\n/**\n * Build border CSS for a single side\n */\nfunction buildSingleBorder(config: BorderConfig | undefined): string {\n if (!config || config.style === 'none' || config.width === 0) {\n return 'none';\n }\n const width = config.width ?? 0;\n const style = config.style ?? 'solid';\n const color = config.color ?? '#000000';\n return `${width}px ${style} ${color}`;\n}\n\n/**\n * CSS properties object type for style application\n */\nexport interface StyleProperties {\n borderRadius?: string;\n borderTopLeftRadius?: string;\n borderTopRightRadius?: string;\n borderBottomRightRadius?: string;\n borderBottomLeftRadius?: string;\n border?: string;\n borderTop?: string;\n borderRight?: string;\n borderBottom?: string;\n borderLeft?: string;\n boxShadow?: string;\n filter?: string;\n opacity?: string;\n cursor?: string;\n outline?: string;\n outlineOffset?: string;\n objectFit?: string;\n aspectRatio?: string;\n clipPath?: string;\n overflow?: string;\n}\n\n/**\n * Build complete style properties object from ImageStyleState\n * @param state - Image style state configuration\n * @param imageHeight - Optional image height for height-relative clip-path calculations\n * @param imageWidth - Optional image width for centering height-relative clip-path shapes\n */\nexport function buildStyleProperties(state: ImageStyleState | undefined, imageHeight?: number, imageWidth?: number): StyleProperties {\n if (!state) return {};\n\n const styles: StyleProperties = {};\n\n // Border radius - check for per-corner overrides first\n const hasPerCornerRadius = state.borderRadiusTopLeft !== undefined ||\n state.borderRadiusTopRight !== undefined ||\n state.borderRadiusBottomRight !== undefined ||\n state.borderRadiusBottomLeft !== undefined;\n\n if (hasPerCornerRadius) {\n // Use per-corner radius with fallback to base radius\n const baseRadius = state.border?.radius ?? 0;\n if (state.borderRadiusTopLeft !== undefined) {\n styles.borderTopLeftRadius = `${state.borderRadiusTopLeft}px`;\n } else if (baseRadius) {\n styles.borderTopLeftRadius = `${baseRadius}px`;\n }\n if (state.borderRadiusTopRight !== undefined) {\n styles.borderTopRightRadius = `${state.borderRadiusTopRight}px`;\n } else if (baseRadius) {\n styles.borderTopRightRadius = `${baseRadius}px`;\n }\n if (state.borderRadiusBottomRight !== undefined) {\n styles.borderBottomRightRadius = `${state.borderRadiusBottomRight}px`;\n } else if (baseRadius) {\n styles.borderBottomRightRadius = `${baseRadius}px`;\n }\n if (state.borderRadiusBottomLeft !== undefined) {\n styles.borderBottomLeftRadius = `${state.borderRadiusBottomLeft}px`;\n } else if (baseRadius) {\n styles.borderBottomLeftRadius = `${baseRadius}px`;\n }\n } else if (state.border?.radius !== undefined) {\n styles.borderRadius = `${state.border.radius}px`;\n }\n\n // Check if any per-side border is defined\n const hasPerSideBorder = state.borderTop || state.borderRight || state.borderBottom || state.borderLeft;\n\n if (hasPerSideBorder) {\n // Merge base border with per-side overrides\n const baseBorder = state.border || {};\n\n const topBorder = { ...baseBorder, ...state.borderTop };\n const rightBorder = { ...baseBorder, ...state.borderRight };\n const bottomBorder = { ...baseBorder, ...state.borderBottom };\n const leftBorder = { ...baseBorder, ...state.borderLeft };\n\n styles.borderTop = buildSingleBorder(topBorder);\n styles.borderRight = buildSingleBorder(rightBorder);\n styles.borderBottom = buildSingleBorder(bottomBorder);\n styles.borderLeft = buildSingleBorder(leftBorder);\n } else if (state.border) {\n // Apply uniform border\n styles.border = buildSingleBorder(state.border);\n }\n\n // Shadow\n if (state.shadow !== undefined) {\n styles.boxShadow = resolveShadow(state.shadow);\n }\n\n // Filter - always set to ensure hover filters are properly cleared on mouseleave\n const filterStr = buildFilterString(state.filter);\n styles.filter = filterStr || 'none';\n\n // Opacity\n if (state.opacity !== undefined) {\n styles.opacity = String(state.opacity);\n }\n\n // Cursor\n if (state.cursor !== undefined) {\n styles.cursor = state.cursor;\n }\n\n // Outline\n if (state.outline && state.outline.style !== 'none' && (state.outline.width ?? 0) > 0) {\n const width = state.outline.width ?? 0;\n const style = state.outline.style ?? 'solid';\n const color = state.outline.color ?? '#000000';\n styles.outline = `${width}px ${style} ${color}`;\n if (state.outline.offset !== undefined) {\n styles.outlineOffset = `${state.outline.offset}px`;\n }\n }\n\n // Object fit\n if (state.objectFit !== undefined) {\n styles.objectFit = state.objectFit;\n }\n\n // Aspect ratio\n if (state.aspectRatio !== undefined) {\n styles.aspectRatio = state.aspectRatio;\n }\n\n // Clip path (cropping)\n if (state.clipPath !== undefined) {\n let clipPathValue: string | undefined;\n\n // Check if clipPath is a config object with height-relative mode\n const isConfig = typeof state.clipPath === 'object' && state.clipPath !== null && 'shape' in state.clipPath;\n const config = isConfig ? (state.clipPath as ClipPathConfig) : undefined;\n\n if (config?.mode === 'height-relative' && imageHeight) {\n // Use height-relative calculation if mode is specified and imageHeight is available\n clipPathValue = calculateHeightRelativeClipPath(config.shape, imageHeight, imageWidth);\n } else {\n // Fall back to standard clip-path resolution\n const clipPathInput = isConfig && config ? config.shape : state.clipPath;\n clipPathValue = getClipPath(clipPathInput as ClipPathShape | string);\n }\n\n if (clipPathValue) {\n // When 'none' is specified, use 'unset' to clear any inherited clip-path\n if (clipPathValue === 'none') {\n styles.clipPath = 'unset';\n } else {\n styles.clipPath = clipPathValue;\n styles.overflow = 'hidden'; // Ensure clean boundaries\n }\n }\n }\n\n return styles;\n}\n\n/**\n * Apply style properties to an HTML element\n */\nexport function applyStylesToElement(element: HTMLElement, styles: StyleProperties): void {\n if (styles.borderRadius !== undefined) element.style.borderRadius = styles.borderRadius;\n if (styles.borderTopLeftRadius !== undefined) element.style.borderTopLeftRadius = styles.borderTopLeftRadius;\n if (styles.borderTopRightRadius !== undefined) element.style.borderTopRightRadius = styles.borderTopRightRadius;\n if (styles.borderBottomRightRadius !== undefined) element.style.borderBottomRightRadius = styles.borderBottomRightRadius;\n if (styles.borderBottomLeftRadius !== undefined) element.style.borderBottomLeftRadius = styles.borderBottomLeftRadius;\n if (styles.border !== undefined) element.style.border = styles.border;\n if (styles.borderTop !== undefined) element.style.borderTop = styles.borderTop;\n if (styles.borderRight !== undefined) element.style.borderRight = styles.borderRight;\n if (styles.borderBottom !== undefined) element.style.borderBottom = styles.borderBottom;\n if (styles.borderLeft !== undefined) element.style.borderLeft = styles.borderLeft;\n if (styles.boxShadow !== undefined) element.style.boxShadow = styles.boxShadow;\n if (styles.filter !== undefined) element.style.filter = styles.filter;\n if (styles.opacity !== undefined) element.style.opacity = styles.opacity;\n if (styles.cursor !== undefined) element.style.cursor = styles.cursor;\n if (styles.outline !== undefined) element.style.outline = styles.outline;\n if (styles.outlineOffset !== undefined) element.style.outlineOffset = styles.outlineOffset;\n if (styles.objectFit !== undefined) element.style.objectFit = styles.objectFit;\n if (styles.aspectRatio !== undefined) element.style.aspectRatio = styles.aspectRatio;\n if (styles.clipPath !== undefined) element.style.clipPath = styles.clipPath;\n if (styles.overflow !== undefined) element.style.overflow = styles.overflow;\n}\n\n/**\n * Build and apply style properties for a given state with image dimensions\n * This is useful for height-relative clip-path calculations which depend on image height and width\n * @param element - HTML element to apply styles to\n * @param state - Image style state configuration\n * @param imageHeight - Optional image height for height-relative clip-path calculations\n * @param imageWidth - Optional image width for centering height-relative clip-path shapes\n */\nexport function applyStylesToElementWithState(element: HTMLElement, state: ImageStyleState | undefined, imageHeight?: number, imageWidth?: number): void {\n const styles = buildStyleProperties(state, imageHeight, imageWidth);\n applyStylesToElement(element, styles);\n}\n\n/**\n * Resolve className to a space-separated string\n */\nexport function resolveClassName(className: string | string[] | undefined): string {\n if (!className) return '';\n if (Array.isArray(className)) return className.join(' ');\n return className;\n}\n\n/**\n * Apply className to element (additive with existing classes)\n */\nexport function applyClassNameToElement(element: HTMLElement, className: string | string[] | undefined): void {\n const resolved = resolveClassName(className);\n if (resolved) {\n resolved.split(' ').forEach(cls => {\n if (cls.trim()) element.classList.add(cls.trim());\n });\n }\n}\n\n/**\n * Remove className from element\n */\nexport function removeClassNameFromElement(element: HTMLElement, className: string | string[] | undefined): void {\n const resolved = resolveClassName(className);\n if (resolved) {\n resolved.split(' ').forEach(cls => {\n if (cls.trim()) element.classList.remove(cls.trim());\n });\n }\n}\n","/**\n * ZoomEngine.ts\n * Manages zoom/focus behavior for image cloud with cross-animation support\n *\n * Public API:\n * - focusImage(imageElement, containerBounds, originalState)\n * - unfocusImage()\n * - getCurrentFocus()\n * - swapFocus(newImageElement, containerBounds, originalState)\n * - isFocused(imageElement)\n * - isAnimating()\n * - getState()\n * - reset()\n */\n\nimport type {\n FocusInteractionConfig,\n ContainerBounds,\n ImageLayout,\n TransformParams,\n ImageStylingConfig,\n AnimationHandle,\n AnimatingImage\n} from '../config/types';\nimport { ZoomState } from '../config/types';\nimport { AnimationEngine } from './AnimationEngine';\nimport { applyClassNameToElement, removeClassNameFromElement, buildStyleProperties } from '../utils/styleUtils';\n\ninterface FocusData {\n element: HTMLElement;\n originalState: ImageLayout;\n focusTransform: TransformParams;\n originalZIndex: string;\n originalWidth: number;\n originalHeight: number;\n focusWidth: number;\n focusHeight: number;\n}\n\n// Z-index constants for layering during animations\nconst Z_INDEX = {\n DEFAULT: '',\n UNFOCUSING: 999,\n FOCUSING: 1000,\n FOCUSED: 1000\n};\n\nexport class ZoomEngine {\n private config: FocusInteractionConfig;\n private animationEngine: AnimationEngine;\n\n // State machine\n private state: ZoomState = ZoomState.IDLE;\n private currentFocus: HTMLElement | null = null;\n private focusData: FocusData | null = null;\n\n // Animation tracking for cross-animation\n private outgoing: AnimatingImage | null = null;\n private incoming: AnimatingImage | null = null;\n\n // Generation counter to handle concurrent calls\n private focusGeneration: number = 0;\n\n // Styling\n private styling?: ImageStylingConfig;\n private focusedClassName: string | string[] | undefined;\n\n // Callback fired when an unfocus animation fully completes\n private onUnfocusComplete: ((element: HTMLElement) => void) | null = null;\n\n constructor(config: FocusInteractionConfig, animationEngine: AnimationEngine, styling?: ImageStylingConfig) {\n this.config = config;\n this.animationEngine = animationEngine;\n this.styling = styling;\n\n // Store focused class name for on-demand class management\n this.focusedClassName = styling?.focused?.className;\n }\n\n /**\n * Set callback to be fired when an unfocus animation fully completes.\n */\n setOnUnfocusCompleteCallback(callback: ((element: HTMLElement) => void) | null): void {\n this.onUnfocusComplete = callback;\n }\n\n /**\n * Get current state machine state\n */\n getState(): ZoomState {\n return this.state;\n }\n\n /**\n * Check if any animation is in progress\n */\n isAnimating(): boolean {\n return this.state !== ZoomState.IDLE && this.state !== ZoomState.FOCUSED;\n }\n\n /**\n * Normalize scalePercent value\n */\n private normalizeScalePercent(value: number): number {\n return value > 1 ? value / 100 : value;\n }\n\n /**\n * Calculate target dimensions for focused image\n * Returns actual pixel dimensions instead of scale factor for sharper rendering\n */\n private calculateFocusDimensions(imageWidth: number, imageHeight: number, containerBounds: ContainerBounds): { width: number; height: number } {\n const normalizedPercent = this.normalizeScalePercent(this.config.scalePercent);\n const targetHeight = containerBounds.height * normalizedPercent;\n const aspectRatio = imageWidth / imageHeight;\n\n let focusHeight = targetHeight;\n let focusWidth = focusHeight * aspectRatio;\n\n const maxWidth = containerBounds.width * normalizedPercent;\n if (focusWidth > maxWidth) {\n focusWidth = maxWidth;\n focusHeight = focusWidth / aspectRatio;\n }\n\n return { width: focusWidth, height: focusHeight };\n }\n\n /**\n * Calculate the transform needed to center an image (position only, no scale)\n * Scale is handled by animating actual dimensions for sharper rendering\n */\n private calculateFocusTransform(\n containerBounds: ContainerBounds,\n originalState: ImageLayout\n ): TransformParams {\n const centerX = containerBounds.width / 2;\n const centerY = containerBounds.height / 2;\n\n const targetX = centerX - originalState.x;\n const targetY = centerY - originalState.y;\n\n return {\n x: targetX,\n y: targetY,\n rotation: 0,\n scale: 1 // No scale transform - dimensions are animated instead\n };\n }\n\n /**\n * Build transform string for dimension-based zoom (no scale in transform)\n */\n private buildDimensionZoomTransform(params: TransformParams): string {\n const transforms: string[] = ['translate(-50%, -50%)'];\n\n if (params.x !== undefined || params.y !== undefined) {\n const x = params.x ?? 0;\n const y = params.y ?? 0;\n transforms.push(`translate(${x}px, ${y}px)`);\n }\n\n if (params.rotation !== undefined) {\n transforms.push(`rotate(${params.rotation}deg)`);\n }\n\n // Note: scale is intentionally omitted - we animate width/height instead\n\n return transforms.join(' ');\n }\n\n /**\n * Create a Web Animation that animates both transform (position) and dimensions\n * This provides sharper zoom by re-rendering at target size instead of scaling pixels\n */\n private animateWithDimensions(\n element: HTMLElement,\n fromTransform: TransformParams,\n toTransform: TransformParams,\n fromWidth: number,\n fromHeight: number,\n toWidth: number,\n toHeight: number,\n duration: number\n ): Animation {\n const fromTransformStr = this.buildDimensionZoomTransform(fromTransform);\n const toTransformStr = this.buildDimensionZoomTransform(toTransform);\n\n // Clear any CSS transitions to avoid conflicts\n element.style.transition = 'none';\n\n // Create Web Animation with both transform and dimensions\n const animation = element.animate(\n [\n {\n transform: fromTransformStr,\n width: `${fromWidth}px`,\n height: `${fromHeight}px`\n },\n {\n transform: toTransformStr,\n width: `${toWidth}px`,\n height: `${toHeight}px`\n }\n ],\n {\n duration: duration,\n easing: 'cubic-bezier(0.4, 0, 0.2, 1)',\n fill: 'forwards'\n }\n );\n\n return animation;\n }\n\n /**\n * Apply focused styling to an element\n * Applies all focused styling properties, classes, and z-index\n */\n private applyFocusedStyling(element: HTMLElement, zIndex: number): void {\n element.style.zIndex = String(zIndex);\n element.classList.add('fbn-ic-focused');\n applyClassNameToElement(element, this.focusedClassName);\n\n // Apply all styling properties (opacity, filters, borders, shadows, etc.)\n // for the focused state\n if (this.styling?.focused) {\n const styles = buildStyleProperties(this.styling.focused, element.offsetHeight, element.offsetWidth);\n // Apply styles directly, skipping clip-path and overflow (updated in startClipPathAnimation)\n if (styles.borderRadius !== undefined) element.style.borderRadius = styles.borderRadius;\n if (styles.borderTopLeftRadius !== undefined) element.style.borderTopLeftRadius = styles.borderTopLeftRadius;\n if (styles.borderTopRightRadius !== undefined) element.style.borderTopRightRadius = styles.borderTopRightRadius;\n if (styles.borderBottomRightRadius !== undefined) element.style.borderBottomRightRadius = styles.borderBottomRightRadius;\n if (styles.borderBottomLeftRadius !== undefined) element.style.borderBottomLeftRadius = styles.borderBottomLeftRadius;\n if (styles.border !== undefined) element.style.border = styles.border;\n if (styles.borderTop !== undefined) element.style.borderTop = styles.borderTop;\n if (styles.borderRight !== undefined) element.style.borderRight = styles.borderRight;\n if (styles.borderBottom !== undefined) element.style.borderBottom = styles.borderBottom;\n if (styles.borderLeft !== undefined) element.style.borderLeft = styles.borderLeft;\n if (styles.boxShadow !== undefined) element.style.boxShadow = styles.boxShadow;\n if (styles.filter !== undefined) element.style.filter = styles.filter;\n if (styles.opacity !== undefined) element.style.opacity = styles.opacity;\n if (styles.cursor !== undefined) element.style.cursor = styles.cursor;\n if (styles.outline !== undefined) element.style.outline = styles.outline;\n if (styles.outlineOffset !== undefined) element.style.outlineOffset = styles.outlineOffset;\n if (styles.objectFit !== undefined) element.style.objectFit = styles.objectFit;\n if (styles.aspectRatio !== undefined) element.style.aspectRatio = styles.aspectRatio;\n }\n }\n\n /**\n * Remove focused styling from an element\n * Restores default styling properties, removes classes, and resets z-index\n */\n private removeFocusedStyling(element: HTMLElement, originalZIndex: string): void {\n element.style.zIndex = originalZIndex;\n element.classList.remove('fbn-ic-focused');\n removeClassNameFromElement(element, this.focusedClassName);\n\n // Restore default styling properties (opacity, filters, borders, shadows, etc.)\n if (this.styling?.default) {\n const styles = buildStyleProperties(this.styling.default, element.offsetHeight, element.offsetWidth);\n // Apply styles directly, skipping clip-path and overflow (updated in startClipPathAnimation)\n if (styles.borderRadius !== undefined) element.style.borderRadius = styles.borderRadius;\n if (styles.borderTopLeftRadius !== undefined) element.style.borderTopLeftRadius = styles.borderTopLeftRadius;\n if (styles.borderTopRightRadius !== undefined) element.style.borderTopRightRadius = styles.borderTopRightRadius;\n if (styles.borderBottomRightRadius !== undefined) element.style.borderBottomRightRadius = styles.borderBottomRightRadius;\n if (styles.borderBottomLeftRadius !== undefined) element.style.borderBottomLeftRadius = styles.borderBottomLeftRadius;\n if (styles.border !== undefined) element.style.border = styles.border;\n if (styles.borderTop !== undefined) element.style.borderTop = styles.borderTop;\n if (styles.borderRight !== undefined) element.style.borderRight = styles.borderRight;\n if (styles.borderBottom !== undefined) element.style.borderBottom = styles.borderBottom;\n if (styles.borderLeft !== undefined) element.style.borderLeft = styles.borderLeft;\n if (styles.boxShadow !== undefined) element.style.boxShadow = styles.boxShadow;\n if (styles.filter !== undefined) element.style.filter = styles.filter;\n if (styles.opacity !== undefined) element.style.opacity = styles.opacity;\n if (styles.cursor !== undefined) element.style.cursor = styles.cursor;\n if (styles.outline !== undefined) element.style.outline = styles.outline;\n if (styles.outlineOffset !== undefined) element.style.outlineOffset = styles.outlineOffset;\n if (styles.objectFit !== undefined) element.style.objectFit = styles.objectFit;\n if (styles.aspectRatio !== undefined) element.style.aspectRatio = styles.aspectRatio;\n }\n }\n\n /**\n * Continuously update clip-path during animation based on current element dimensions\n * This ensures clip-path changes smoothly as width/height animate\n */\n private startClipPathAnimation(element: HTMLElement, handle: AnimationHandle, isToFocused: boolean): void {\n // Determine which styling config to use\n // If focused is explicitly defined but has no clipPath, don't fall back to default clipPath\n let styleConfig: any = isToFocused\n ? (this.styling?.focused ?? this.styling?.default)\n : this.styling?.default;\n\n // If focused config is explicitly set but missing clipPath, explicitly set it to undefined\n // to prevent inheriting default clipPath\n if (isToFocused && this.styling?.focused && this.styling.focused.clipPath === undefined) {\n styleConfig = { ...styleConfig, clipPath: undefined };\n }\n\n const updateClipPath = () => {\n // Use actual animated element dimensions (both width and height are being animated)\n const currentHeight = element.offsetHeight;\n const currentWidth = element.offsetWidth;\n\n // Build style properties with current dimensions to get updated clip-path\n const styles = buildStyleProperties(styleConfig, currentHeight, currentWidth);\n\n // Apply clip-path - clear it if not defined\n if (styles.clipPath !== undefined) {\n element.style.clipPath = styles.clipPath;\n } else {\n // No clip-path defined - clear any inherited clip-path\n element.style.clipPath = 'unset';\n }\n if (styles.overflow !== undefined) {\n element.style.overflow = styles.overflow;\n }\n\n // Continue updating if animation is still running\n if (handle.animation.playState === 'running') {\n requestAnimationFrame(updateClipPath);\n }\n };\n\n // Start the update loop\n requestAnimationFrame(updateClipPath);\n }\n\n /**\n * Start focus animation for an image using dimension-based zoom\n * Animates actual width/height for sharper rendering instead of transform scale\n * @param fromTransform - Optional starting transform (for mid-animation reversals)\n * @param fromDimensions - Optional starting dimensions (for mid-animation reversals)\n */\n private startFocusAnimation(\n element: HTMLElement,\n containerBounds: ContainerBounds,\n originalState: ImageLayout,\n fromTransform?: TransformParams,\n fromDimensions?: { width: number; height: number }\n ): AnimatingImage {\n const originalZIndex = element.style.zIndex || '';\n const originalWidth = element.offsetWidth;\n const originalHeight = element.offsetHeight;\n\n // Calculate target dimensions and position\n const focusDimensions = this.calculateFocusDimensions(originalWidth, originalHeight, containerBounds);\n const focusTransform = this.calculateFocusTransform(containerBounds, originalState);\n\n // Cancel any existing animation\n this.animationEngine.cancelAllAnimations(element);\n\n // Get animation duration\n const duration = this.config.animationDuration ?? 600;\n\n // Apply focused styling (classes and z-index only)\n this.applyFocusedStyling(element, Z_INDEX.FOCUSING);\n\n // Start animation from provided state or original position\n const startTransform: TransformParams = fromTransform ?? {\n x: 0,\n y: 0,\n rotation: originalState.rotation,\n scale: 1 // No scale - using dimensions\n };\n\n const startWidth = fromDimensions?.width ?? originalWidth;\n const startHeight = fromDimensions?.height ?? originalHeight;\n\n // Create dimension-based animation\n const animation = this.animateWithDimensions(\n element,\n startTransform,\n focusTransform,\n startWidth,\n startHeight,\n focusDimensions.width,\n focusDimensions.height,\n duration\n );\n\n // Create animation handle\n const handle: AnimationHandle = {\n id: `focus-${Date.now()}`,\n element,\n animation,\n fromState: startTransform,\n toState: focusTransform,\n startTime: performance.now(),\n duration\n };\n\n // Store focus data for later (including dimensions for unfocus)\n this.focusData = {\n element,\n originalState,\n focusTransform,\n originalZIndex,\n originalWidth,\n originalHeight,\n focusWidth: focusDimensions.width,\n focusHeight: focusDimensions.height\n };\n\n // Start continuous clip-path animation to follow dimension changes\n this.startClipPathAnimation(element, handle, true);\n\n return {\n element,\n originalState,\n animationHandle: handle,\n direction: 'in' as const,\n originalWidth,\n originalHeight\n };\n }\n\n /**\n * Start unfocus animation for an image using dimension-based zoom\n * Animates back to original dimensions for consistent behavior\n * @param fromDimensions - Optional starting dimensions (for mid-animation reversals)\n */\n private startUnfocusAnimation(\n element: HTMLElement,\n originalState: ImageLayout,\n fromTransform?: TransformParams,\n fromDimensions?: { width: number; height: number }\n ): AnimatingImage {\n // Set z-index for unfocusing (below incoming)\n element.style.zIndex = String(Z_INDEX.UNFOCUSING);\n\n // Cancel any existing animation\n this.animationEngine.cancelAllAnimations(element);\n\n // Get animation duration\n const duration = this.config.animationDuration ?? 600;\n\n // Remove focused classes but keep z-index high during animation\n // (z-index will be reset after animation completes)\n element.classList.remove('fbn-ic-focused');\n removeClassNameFromElement(element, this.focusedClassName);\n\n // Start from current focused state (or provided state for interrupted animations)\n const startTransform = fromTransform ?? this.focusData?.focusTransform ?? { x: 0, y: 0, rotation: 0, scale: 1 };\n const startWidth = fromDimensions?.width ?? this.focusData?.focusWidth ?? element.offsetWidth;\n const startHeight = fromDimensions?.height ?? this.focusData?.focusHeight ?? element.offsetHeight;\n\n // Target is original position and dimensions\n const toState: TransformParams = {\n x: 0,\n y: 0,\n rotation: originalState.rotation,\n scale: 1 // No scale - using dimensions\n };\n\n const targetWidth = this.focusData?.originalWidth ?? element.offsetWidth;\n const targetHeight = this.focusData?.originalHeight ?? element.offsetHeight;\n\n // Create dimension-based animation\n const animation = this.animateWithDimensions(\n element,\n startTransform,\n toState,\n startWidth,\n startHeight,\n targetWidth,\n targetHeight,\n duration\n );\n\n // Create animation handle\n const handle: AnimationHandle = {\n id: `unfocus-${Date.now()}`,\n element,\n animation,\n fromState: startTransform,\n toState: toState,\n startTime: performance.now(),\n duration\n };\n\n // Start continuous clip-path animation to follow dimension changes back to default\n this.startClipPathAnimation(element, handle, false);\n\n return {\n element,\n originalState,\n animationHandle: handle,\n direction: 'out' as const,\n originalWidth: targetWidth,\n originalHeight: targetHeight\n };\n }\n\n /**\n * Capture the current visual state of an element mid-animation, BEFORE cancelling.\n *\n * The computed matrix.e/f include the -50%/-50% centering offset resolved to pixels.\n * buildDimensionZoomTransform prepends its own translate(-50%,-50%), so passing raw\n * matrix.e/f doubles the centering and produces the wrong starting position.\n *\n * This method extracts the PURE positional offset (pureX = matrix.e + 0.5*midWidth)\n * and commits width/height/transform to inline styles before the animation is cancelled,\n * preventing any visual snap.\n *\n * Must be called while the animation is still running (offsetWidth reflects animated size).\n * Caller is responsible for calling animationEngine.cancelAllAnimations() afterwards.\n */\n private captureMidAnimationState(element: HTMLElement): {\n transform: TransformParams;\n dimensions: { width: number; height: number };\n } {\n const computed = getComputedStyle(element);\n const matrix = new DOMMatrix(computed.transform);\n const midWidth = element.offsetWidth;\n const midHeight = element.offsetHeight;\n\n // Remove the -50%/-50% centering that is baked into matrix.e/f\n const pureX = matrix.e + midWidth * 0.5;\n const pureY = matrix.f + midHeight * 0.5;\n const rotation = Math.atan2(matrix.b, matrix.a) * (180 / Math.PI);\n\n // Commit current visual state to inline styles so cancelling the animation\n // does not cause the element to snap to a different size/position\n element.style.width = `${midWidth}px`;\n element.style.height = `${midHeight}px`;\n element.style.transform = `translate(-50%, -50%) translate(${pureX}px, ${pureY}px) rotate(${rotation}deg)`;\n element.style.transition = 'none';\n\n return {\n transform: { x: pureX, y: pureY, rotation, scale: 1 },\n dimensions: { width: midWidth, height: midHeight }\n };\n }\n\n /**\n * Handle animation completion\n */\n private async waitForAnimation(handle: AnimationHandle): Promise<void> {\n try {\n await handle.animation.finished;\n } catch {\n // Animation was cancelled - this is expected during interruption\n }\n }\n\n /**\n * Reset an element instantly to its original position and dimensions (no animation)\n */\n private resetElementInstantly(\n element: HTMLElement,\n originalState: ImageLayout,\n originalZIndex: string,\n originalWidth?: number,\n originalHeight?: number\n ): void {\n // Cancel any active animation (including completed animations with fill: 'forwards')\n this.animationEngine.cancelAllAnimations(element);\n\n // Build transform string for original position (no scale - using dimensions)\n const transforms = ['translate(-50%, -50%)'];\n transforms.push('translate(0px, 0px)');\n transforms.push(`rotate(${originalState.rotation}deg)`);\n // No scale in transform - dimensions handle sizing\n\n element.style.transition = 'none';\n element.style.transform = transforms.join(' ');\n\n // Restore original dimensions if provided\n if (originalWidth !== undefined && originalHeight !== undefined) {\n element.style.width = `${originalWidth}px`;\n element.style.height = `${originalHeight}px`;\n }\n\n // Remove focused styling (clip-path will be updated when animating)\n this.removeFocusedStyling(element, originalZIndex);\n }\n\n /**\n * Focus (zoom) an image to center of container\n * Implements cross-animation when swapping focus\n */\n async focusImage(\n imageElement: HTMLElement,\n containerBounds: ContainerBounds,\n originalState: ImageLayout\n ): Promise<void> {\n // Same image clicked while already focused - unfocus it\n if (this.currentFocus === imageElement && this.state === ZoomState.FOCUSED) {\n return this.unfocusImage();\n }\n\n // Same image clicked while it's animating in - reverse to unfocus\n if (this.incoming?.element === imageElement && this.state === ZoomState.FOCUSING) {\n // Capture mid-animation state BEFORE cancelling (offsetWidth reflects animated size;\n // pure positional offset strips the -50% centering already baked into matrix.e/f)\n const { transform: fromTransform, dimensions: fromDimensions } =\n this.captureMidAnimationState(imageElement);\n this.animationEngine.cancelAllAnimations(imageElement);\n\n this.outgoing = this.startUnfocusAnimation(\n imageElement,\n this.incoming.originalState,\n fromTransform,\n fromDimensions\n );\n this.incoming = null;\n this.state = ZoomState.UNFOCUSING;\n\n await this.waitForAnimation(this.outgoing.animationHandle);\n this.removeFocusedStyling(this.outgoing.element, this.focusData?.originalZIndex || '');\n this.outgoing = null;\n this.currentFocus = null;\n this.focusData = null;\n this.state = ZoomState.IDLE;\n return;\n }\n\n // Increment generation to invalidate any previous in-flight calls\n const myGeneration = ++this.focusGeneration;\n\n switch (this.state) {\n case ZoomState.IDLE:\n // Simple focus - no current focus\n this.state = ZoomState.FOCUSING;\n this.incoming = this.startFocusAnimation(imageElement, containerBounds, originalState);\n\n await this.waitForAnimation(this.incoming.animationHandle);\n\n // Check if we're still the active generation\n if (this.focusGeneration !== myGeneration) return;\n\n this.currentFocus = imageElement;\n this.incoming = null;\n this.state = ZoomState.FOCUSED;\n break;\n\n case ZoomState.FOCUSED:\n // Cross-animation: unfocus current while focusing new\n this.state = ZoomState.CROSS_ANIMATING;\n\n // Start outgoing animation for currently focused image\n if (this.currentFocus && this.focusData) {\n this.outgoing = this.startUnfocusAnimation(\n this.currentFocus,\n this.focusData.originalState\n );\n }\n\n // Start incoming animation for new image\n this.incoming = this.startFocusAnimation(imageElement, containerBounds, originalState);\n\n // Wait for both animations\n await Promise.all([\n this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),\n this.waitForAnimation(this.incoming.animationHandle)\n ]);\n\n // Check if we're still the active generation\n if (this.focusGeneration !== myGeneration) {\n return;\n }\n\n // Cleanup outgoing\n if (this.outgoing) {\n const completedOutgoing = this.outgoing.element;\n this.removeFocusedStyling(completedOutgoing, this.outgoing.originalState.zIndex?.toString() || '');\n this.outgoing = null;\n this.onUnfocusComplete?.(completedOutgoing);\n }\n\n this.currentFocus = imageElement;\n this.incoming = null;\n this.state = ZoomState.FOCUSED;\n break;\n\n case ZoomState.FOCUSING:\n // New image clicked while another is focusing\n // Cancel the incoming and start new focus\n if (this.incoming) {\n this.animationEngine.cancelAnimation(this.incoming.animationHandle, false);\n this.resetElementInstantly(\n this.incoming.element,\n this.incoming.originalState,\n this.focusData?.originalZIndex || '',\n this.focusData?.originalWidth,\n this.focusData?.originalHeight\n );\n this.incoming = null;\n }\n\n // Start focusing the new image\n this.incoming = this.startFocusAnimation(imageElement, containerBounds, originalState);\n\n await this.waitForAnimation(this.incoming.animationHandle);\n\n // Check if we're still the active generation\n if (this.focusGeneration !== myGeneration) return;\n\n this.currentFocus = imageElement;\n this.incoming = null;\n this.state = ZoomState.FOCUSED;\n break;\n\n case ZoomState.UNFOCUSING:\n // New image clicked while current is unfocusing\n // Let outgoing continue, start cross-animation\n this.state = ZoomState.CROSS_ANIMATING;\n this.incoming = this.startFocusAnimation(imageElement, containerBounds, originalState);\n\n await Promise.all([\n this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),\n this.waitForAnimation(this.incoming.animationHandle)\n ]);\n\n // Check if we're still the active generation\n if (this.focusGeneration !== myGeneration) return;\n\n if (this.outgoing) {\n const completedOutgoing = this.outgoing.element;\n this.removeFocusedStyling(completedOutgoing, this.outgoing.originalState.zIndex?.toString() || '');\n this.outgoing = null;\n this.onUnfocusComplete?.(completedOutgoing);\n }\n\n this.currentFocus = imageElement;\n this.incoming = null;\n this.state = ZoomState.FOCUSED;\n break;\n\n case ZoomState.CROSS_ANIMATING:\n // Handle click during cross-animation\n\n // If clicking the same image that's animating in, ignore (it's already targeting focus)\n if (this.incoming?.element === imageElement) {\n return;\n }\n\n // If clicking the same image that's animating out, let it become the new focus target\n // (reverse direction - it should animate back to focused)\n if (this.outgoing?.element === imageElement) {\n // Capture mid-animation state for outgoing before cancelling\n const { transform: fromTransform, dimensions: fromDimensions } =\n this.captureMidAnimationState(imageElement);\n this.animationEngine.cancelAllAnimations(imageElement);\n\n // Redirect current incoming to become outgoing\n if (this.incoming) {\n const { transform: incomingFrom, dimensions: incomingFromDimensions } =\n this.captureMidAnimationState(this.incoming.element);\n this.animationEngine.cancelAllAnimations(this.incoming.element);\n this.outgoing = this.startUnfocusAnimation(\n this.incoming.element,\n this.incoming.originalState,\n incomingFrom,\n incomingFromDimensions\n );\n } else {\n this.outgoing = null;\n }\n\n // Start new incoming for the clicked (formerly outgoing) image\n // Use fromTransform and fromDimensions to continue from current state\n this.incoming = this.startFocusAnimation(imageElement, containerBounds, originalState, fromTransform, fromDimensions);\n\n await Promise.all([\n this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),\n this.waitForAnimation(this.incoming.animationHandle)\n ]);\n\n if (this.focusGeneration !== myGeneration) return;\n\n if (this.outgoing) {\n const completedOutgoing = this.outgoing.element;\n this.removeFocusedStyling(completedOutgoing, this.outgoing.originalState.zIndex?.toString() || '');\n this.outgoing = null;\n this.onUnfocusComplete?.(completedOutgoing);\n }\n\n this.currentFocus = imageElement;\n this.incoming = null;\n this.state = ZoomState.FOCUSED;\n return;\n }\n\n // Third different image clicked during cross-animation\n // 1. Reset outgoing instantly\n // 2. Redirect incoming to become outgoing\n // 3. Start new incoming\n\n // Reset outgoing instantly\n if (this.outgoing) {\n this.animationEngine.cancelAnimation(this.outgoing.animationHandle, false);\n this.resetElementInstantly(\n this.outgoing.element,\n this.outgoing.originalState,\n this.outgoing.originalState.zIndex?.toString() || '',\n this.outgoing.originalWidth,\n this.outgoing.originalHeight\n );\n this.outgoing = null;\n }\n\n // Redirect incoming to outgoing (animate from current position back to original)\n if (this.incoming) {\n const { transform: fromTransform, dimensions: fromDimensions } =\n this.captureMidAnimationState(this.incoming.element);\n this.animationEngine.cancelAllAnimations(this.incoming.element);\n\n this.outgoing = this.startUnfocusAnimation(\n this.incoming.element,\n this.incoming.originalState,\n fromTransform,\n fromDimensions\n );\n }\n\n // Start new incoming\n this.incoming = this.startFocusAnimation(imageElement, containerBounds, originalState);\n\n // Wait for both\n await Promise.all([\n this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),\n this.waitForAnimation(this.incoming.animationHandle)\n ]);\n\n // Check if we're still the active generation\n if (this.focusGeneration !== myGeneration) return;\n\n if (this.outgoing) {\n const completedOutgoing = this.outgoing.element;\n this.removeFocusedStyling(completedOutgoing, this.outgoing.originalState.zIndex?.toString() || '');\n this.outgoing = null;\n this.onUnfocusComplete?.(completedOutgoing);\n }\n\n this.currentFocus = imageElement;\n this.incoming = null;\n this.state = ZoomState.FOCUSED;\n break;\n }\n }\n\n /**\n * Unfocus current image, returning it to original position\n */\n async unfocusImage(): Promise<void> {\n // Already animating out - ignore duplicate requests (e.g. ESC pressed twice)\n if (this.state === ZoomState.UNFOCUSING) {\n return;\n }\n\n // Increment generation to invalidate any previous in-flight calls\n const myGeneration = ++this.focusGeneration;\n\n if (!this.currentFocus || !this.focusData) {\n // Handle case where we're in FOCUSING state\n if (this.incoming && this.state === ZoomState.FOCUSING) {\n const { transform: fromTransform, dimensions: fromDimensions } =\n this.captureMidAnimationState(this.incoming.element);\n this.animationEngine.cancelAllAnimations(this.incoming.element);\n\n this.outgoing = this.startUnfocusAnimation(\n this.incoming.element,\n this.incoming.originalState,\n fromTransform,\n fromDimensions\n );\n this.incoming = null;\n this.state = ZoomState.UNFOCUSING;\n\n await this.waitForAnimation(this.outgoing.animationHandle);\n\n if (this.focusGeneration !== myGeneration) return;\n\n const completedFocusing = this.outgoing.element;\n this.removeFocusedStyling(completedFocusing, this.focusData?.originalZIndex || '');\n this.outgoing = null;\n this.focusData = null;\n this.state = ZoomState.IDLE;\n this.onUnfocusComplete?.(completedFocusing);\n }\n return;\n }\n\n // Handle cross-animation cancellation (ESC during cross-animation)\n if (this.state === ZoomState.CROSS_ANIMATING) {\n // Cancel incoming and animate it back\n if (this.incoming) {\n const { transform: fromTransform, dimensions: fromDimensions } =\n this.captureMidAnimationState(this.incoming.element);\n this.animationEngine.cancelAllAnimations(this.incoming.element);\n\n // Start unfocus for incoming from its current position\n const incomingUnfocus = this.startUnfocusAnimation(\n this.incoming.element,\n this.incoming.originalState,\n fromTransform,\n fromDimensions\n );\n\n // Wait for both outgoing and the redirected incoming\n await Promise.all([\n this.outgoing ? this.waitForAnimation(this.outgoing.animationHandle) : Promise.resolve(),\n this.waitForAnimation(incomingUnfocus.animationHandle)\n ]);\n\n if (this.focusGeneration !== myGeneration) return;\n\n // Cleanup\n let outgoingElement: HTMLElement | null = null;\n if (this.outgoing) {\n outgoingElement = this.outgoing.element;\n this.removeFocusedStyling(outgoingElement, this.outgoing.originalState.zIndex?.toString() || '');\n }\n const incomingUnfocusElement = incomingUnfocus.element;\n this.removeFocusedStyling(incomingUnfocusElement, this.incoming.originalState.zIndex?.toString() || '');\n\n this.outgoing = null;\n this.incoming = null;\n this.currentFocus = null;\n this.focusData = null;\n this.state = ZoomState.IDLE;\n\n if (outgoingElement) this.onUnfocusComplete?.(outgoingElement);\n this.onUnfocusComplete?.(incomingUnfocusElement);\n return;\n }\n }\n\n // Normal unfocus from FOCUSED state\n this.state = ZoomState.UNFOCUSING;\n const element = this.currentFocus;\n const originalState = this.focusData.originalState;\n const originalZIndex = this.focusData.originalZIndex;\n\n this.outgoing = this.startUnfocusAnimation(element, originalState);\n\n await this.waitForAnimation(this.outgoing.animationHandle);\n\n if (this.focusGeneration !== myGeneration) return;\n\n this.removeFocusedStyling(element, originalZIndex);\n this.outgoing = null;\n this.currentFocus = null;\n this.focusData = null;\n this.state = ZoomState.IDLE;\n this.onUnfocusComplete?.(element);\n }\n\n /**\n * Swap focus from current image to a new one (alias for focusImage with cross-animation)\n */\n async swapFocus(\n newImageElement: HTMLElement,\n containerBounds: ContainerBounds,\n originalState: ImageLayout\n ): Promise<void> {\n return this.focusImage(newImageElement, containerBounds, originalState);\n }\n\n /**\n * Get currently focused image element\n */\n getCurrentFocus(): HTMLElement | null {\n return this.currentFocus;\n }\n\n /**\n * Check if an image is currently focused (stable state)\n */\n isFocused(imageElement: HTMLElement): boolean {\n return this.currentFocus === imageElement && this.state === ZoomState.FOCUSED;\n }\n\n /**\n * Check if an image is the target of current focus animation\n */\n isTargetingFocus(imageElement: HTMLElement): boolean {\n return this.incoming?.element === imageElement;\n }\n\n /**\n * Check if an image is involved in any focus/animation state\n * Returns true if the image is focused, animating in, or animating out\n * Useful for hover state management - don't apply hover to animating images\n */\n isInvolved(imageElement: HTMLElement): boolean {\n return (\n this.currentFocus === imageElement ||\n this.incoming?.element === imageElement ||\n this.outgoing?.element === imageElement\n );\n }\n\n /**\n * Apply a temporary horizontal drag offset to the focused image\n * Used during swipe gestures for visual feedback\n */\n setDragOffset(offset: number): void {\n if (!this.currentFocus || !this.focusData || this.state !== ZoomState.FOCUSED) return;\n\n const element = this.currentFocus;\n const focusTransform = this.focusData.focusTransform;\n\n // Build transform with additional horizontal offset\n const transforms: string[] = ['translate(-50%, -50%)'];\n const x = (focusTransform.x ?? 0) + offset;\n const y = focusTransform.y ?? 0;\n transforms.push(`translate(${x}px, ${y}px)`);\n if (focusTransform.rotation !== undefined) {\n transforms.push(`rotate(${focusTransform.rotation}deg)`);\n }\n\n element.style.transition = 'none';\n element.style.transform = transforms.join(' ');\n }\n\n /**\n * Clear the drag offset, optionally animating back to center\n * @param animate - If true, animate back to center; if false, snap instantly\n * @param duration - Animation duration in ms (default 150)\n */\n clearDragOffset(animate: boolean, duration: number = 150): void {\n if (!this.currentFocus || !this.focusData || this.state !== ZoomState.FOCUSED) return;\n\n const element = this.currentFocus;\n const focusTransform = this.focusData.focusTransform;\n\n // Build the centered transform (no offset)\n const transforms: string[] = ['translate(-50%, -50%)'];\n const x = focusTransform.x ?? 0;\n const y = focusTransform.y ?? 0;\n transforms.push(`translate(${x}px, ${y}px)`);\n if (focusTransform.rotation !== undefined) {\n transforms.push(`rotate(${focusTransform.rotation}deg)`);\n }\n const centeredTransform = transforms.join(' ');\n\n if (animate) {\n element.style.transition = `transform ${duration}ms ease-out`;\n element.style.transform = centeredTransform;\n // Clear transition after animation completes\n setTimeout(() => {\n if (this.currentFocus === element) {\n element.style.transition = 'none';\n }\n }, duration);\n } else {\n element.style.transition = 'none';\n element.style.transform = centeredTransform;\n }\n }\n\n /**\n * Reset zoom state (cancels all animations)\n */\n reset(): void {\n // Cancel any active animations\n if (this.outgoing) {\n this.animationEngine.cancelAnimation(this.outgoing.animationHandle, false);\n this.resetElementInstantly(\n this.outgoing.element,\n this.outgoing.originalState,\n this.outgoing.originalState.zIndex?.toString() || '',\n this.outgoing.originalWidth,\n this.outgoing.originalHeight\n );\n }\n\n if (this.incoming) {\n this.animationEngine.cancelAnimation(this.incoming.animationHandle, false);\n this.resetElementInstantly(\n this.incoming.element,\n this.incoming.originalState,\n this.focusData?.originalZIndex || '',\n this.focusData?.originalWidth,\n this.focusData?.originalHeight\n );\n }\n\n if (this.currentFocus && this.focusData) {\n this.resetElementInstantly(\n this.currentFocus,\n this.focusData.originalState,\n this.focusData.originalZIndex,\n this.focusData.originalWidth,\n this.focusData.originalHeight\n );\n }\n\n this.state = ZoomState.IDLE;\n this.currentFocus = null;\n this.focusData = null;\n this.outgoing = null;\n this.incoming = null;\n }\n}\n","/**\n * SwipeEngine.ts\n * Handles touch swipe gestures for navigating between focused images\n *\n * Public API:\n * - enable() - Start listening for touch events\n * - disable() - Stop listening (when no image focused)\n * - destroy() - Clean up all event listeners\n * - setDragCallback(callback) - Set callback for drag offset updates\n */\n\n// Constants for swipe detection\nconst SWIPE_THRESHOLD_PX = 50;\nconst SWIPE_VELOCITY_THRESHOLD = 0.5; // px/ms\nconst SWIPE_MIN_DISTANCE_FOR_VELOCITY = 20;\nconst DRAG_DAMPING = 0.3;\nconst SNAP_BACK_DURATION_MS = 150;\nconst HORIZONTAL_ANGLE_THRESHOLD_DEG = 30;\n\ninterface SwipeCallbacks {\n onNext: () => void;\n onPrev: () => void;\n onDragOffset: (offset: number) => void;\n onDragEnd: (navigated: boolean) => void;\n}\n\ninterface TouchState {\n startX: number;\n startY: number;\n startTime: number;\n currentX: number;\n isDragging: boolean;\n isHorizontalSwipe: boolean | null; // null = not determined yet\n}\n\nexport class SwipeEngine {\n private container: HTMLElement;\n private callbacks: SwipeCallbacks;\n private enabled: boolean = false;\n private touchState: TouchState | null = null;\n\n // Track recent touch activity to prevent click-outside from unfocusing\n private recentTouchTimestamp: number = 0;\n private static readonly TOUCH_CLICK_DELAY = 300; // ms to ignore clicks after touch\n\n // Bound event handlers for proper cleanup\n private boundTouchStart: (e: TouchEvent) => void;\n private boundTouchMove: (e: TouchEvent) => void;\n private boundTouchEnd: (e: TouchEvent) => void;\n private boundTouchCancel: (e: TouchEvent) => void;\n\n constructor(container: HTMLElement, callbacks: SwipeCallbacks) {\n this.container = container;\n this.callbacks = callbacks;\n\n // Bind handlers\n this.boundTouchStart = this.handleTouchStart.bind(this);\n this.boundTouchMove = this.handleTouchMove.bind(this);\n this.boundTouchEnd = this.handleTouchEnd.bind(this);\n this.boundTouchCancel = this.handleTouchCancel.bind(this);\n }\n\n /**\n * Start listening for touch events\n */\n enable(): void {\n if (this.enabled) return;\n this.enabled = true;\n\n // Set touch-action to allow vertical scroll but let JS handle horizontal\n this.container.style.touchAction = 'pan-y';\n\n this.container.addEventListener('touchstart', this.boundTouchStart, { passive: false });\n this.container.addEventListener('touchmove', this.boundTouchMove, { passive: false });\n this.container.addEventListener('touchend', this.boundTouchEnd, { passive: true });\n this.container.addEventListener('touchcancel', this.boundTouchCancel, { passive: true });\n }\n\n /**\n * Stop listening for touch events\n */\n disable(): void {\n if (!this.enabled) return;\n this.enabled = false;\n\n // Restore default touch-action\n this.container.style.touchAction = '';\n\n this.container.removeEventListener('touchstart', this.boundTouchStart);\n this.container.removeEventListener('touchmove', this.boundTouchMove);\n this.container.removeEventListener('touchend', this.boundTouchEnd);\n this.container.removeEventListener('touchcancel', this.boundTouchCancel);\n\n // Reset any in-progress drag\n if (this.touchState?.isDragging) {\n this.callbacks.onDragEnd(false);\n }\n this.touchState = null;\n }\n\n /**\n * Clean up all event listeners\n */\n destroy(): void {\n this.disable();\n }\n\n /**\n * Check if a touch interaction happened recently\n * Used to prevent click-outside from unfocusing immediately after touch\n */\n hadRecentTouch(): boolean {\n return Date.now() - this.recentTouchTimestamp < SwipeEngine.TOUCH_CLICK_DELAY;\n }\n\n private handleTouchStart(e: TouchEvent): void {\n if (e.touches.length !== 1) return;\n\n // Mark recent touch to prevent click-outside unfocus\n this.recentTouchTimestamp = Date.now();\n\n const touch = e.touches[0];\n this.touchState = {\n startX: touch.clientX,\n startY: touch.clientY,\n startTime: performance.now(),\n currentX: touch.clientX,\n isDragging: false,\n isHorizontalSwipe: null\n };\n }\n\n private handleTouchMove(e: TouchEvent): void {\n if (!this.touchState || e.touches.length !== 1) return;\n\n const touch = e.touches[0];\n const deltaX = touch.clientX - this.touchState.startX;\n const deltaY = touch.clientY - this.touchState.startY;\n\n // Determine swipe direction on first significant movement\n if (this.touchState.isHorizontalSwipe === null) {\n const totalDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n if (totalDistance > 10) {\n // Calculate angle from horizontal\n const angleRad = Math.atan2(Math.abs(deltaY), Math.abs(deltaX));\n const angleDeg = angleRad * (180 / Math.PI);\n this.touchState.isHorizontalSwipe = angleDeg <= HORIZONTAL_ANGLE_THRESHOLD_DEG;\n }\n }\n\n // If determined to be vertical, don't capture\n if (this.touchState.isHorizontalSwipe === false) {\n return;\n }\n\n // If horizontal swipe, prevent default and track drag\n if (this.touchState.isHorizontalSwipe === true) {\n e.preventDefault();\n this.touchState.isDragging = true;\n this.touchState.currentX = touch.clientX;\n\n // Apply damped offset to focused image\n const dampedOffset = deltaX * DRAG_DAMPING;\n this.callbacks.onDragOffset(dampedOffset);\n }\n }\n\n private handleTouchEnd(_e: TouchEvent): void {\n if (!this.touchState) return;\n\n // Update timestamp to prevent click-outside unfocus\n this.recentTouchTimestamp = Date.now();\n\n const deltaX = this.touchState.currentX - this.touchState.startX;\n const deltaTime = performance.now() - this.touchState.startTime;\n const velocity = Math.abs(deltaX) / deltaTime;\n const absDistance = Math.abs(deltaX);\n\n let navigated = false;\n\n // Only process if it was a horizontal swipe\n if (this.touchState.isHorizontalSwipe === true && this.touchState.isDragging) {\n // Check if swipe threshold met (distance or velocity)\n const thresholdMet =\n absDistance >= SWIPE_THRESHOLD_PX ||\n (velocity >= SWIPE_VELOCITY_THRESHOLD && absDistance >= SWIPE_MIN_DISTANCE_FOR_VELOCITY);\n\n if (thresholdMet) {\n navigated = true;\n if (deltaX < 0) {\n // Swipe left -> next image\n this.callbacks.onNext();\n } else {\n // Swipe right -> previous image\n this.callbacks.onPrev();\n }\n }\n }\n\n // Notify drag end (handles snap-back if not navigated)\n if (this.touchState.isDragging) {\n this.callbacks.onDragEnd(navigated);\n }\n\n this.touchState = null;\n }\n\n private handleTouchCancel(_e: TouchEvent): void {\n if (this.touchState?.isDragging) {\n this.callbacks.onDragEnd(false);\n }\n this.touchState = null;\n }\n}\n\nexport { SNAP_BACK_DURATION_MS };\n","/**\n * GoogleDriveLoader.ts\n * Loads images from a public Google Drive folder\n *\n * Public API:\n * - prepare(filter) - Async discovery of images\n * - imagesLength() - Get count of discovered images\n * - imageURLs() - Get ordered list of image URLs\n * - isPrepared() - Check if loader has been prepared\n *\n * Helper methods (for advanced usage):\n * - extractFolderId(folderUrl)\n * - manualImageUrls(imageIds)\n */\n\nimport type { ImageLoader, IImageFilter, GoogleDriveResponse, GoogleDriveLoaderInnerConfig, GoogleDriveSource } from '../config/types';\n\nexport class GoogleDriveLoader implements ImageLoader {\n private apiKey: string;\n private apiEndpoint: string;\n private debugLogging: boolean;\n private sources: GoogleDriveSource[];\n\n // State for new interface\n private _prepared: boolean = false;\n private _discoveredUrls: string[] = [];\n\n constructor(config: GoogleDriveLoaderInnerConfig) {\n this.apiKey = config.apiKey ?? '';\n this.apiEndpoint = config.apiEndpoint ?? 'https://www.googleapis.com/drive/v3/files';\n this.debugLogging = config.debugLogging ?? false;\n this.sources = config.sources ?? [];\n\n // Validate that we have sources configured\n if (!this.sources || this.sources.length === 0) {\n throw new Error('GoogleDriveLoader requires at least one source to be configured');\n }\n }\n\n /**\n * Prepare the loader by discovering all images from configured sources\n * @param filter - Filter to apply to discovered images\n */\n async prepare(filter: IImageFilter): Promise<void> {\n this._discoveredUrls = [];\n\n for (const source of this.sources) {\n if ('folders' in source) {\n for (const folderUrl of source.folders) {\n const recursive = source.recursive !== undefined ? source.recursive : true;\n const urls = await this.loadFromFolder(folderUrl, filter, recursive);\n this._discoveredUrls.push(...urls);\n }\n } else if ('files' in source) {\n const urls = await this.loadFiles(source.files, filter);\n this._discoveredUrls.push(...urls);\n }\n }\n\n this._prepared = true;\n }\n\n /**\n * Get the number of discovered images\n * @throws Error if called before prepare()\n */\n imagesLength(): number {\n if (!this._prepared) {\n throw new Error('GoogleDriveLoader.imagesLength() called before prepare()');\n }\n return this._discoveredUrls.length;\n }\n\n /**\n * Get the ordered list of image URLs\n * @throws Error if called before prepare()\n */\n imageURLs(): string[] {\n if (!this._prepared) {\n throw new Error('GoogleDriveLoader.imageURLs() called before prepare()');\n }\n return [...this._discoveredUrls];\n }\n\n /**\n * Check if the loader has been prepared\n */\n isPrepared(): boolean {\n return this._prepared;\n }\n\n /**\n * Extract folder ID from various Google Drive URL formats\n * @param folderUrl - Google Drive folder URL\n * @returns Folder ID or null if invalid\n */\n extractFolderId(folderUrl: string): string | null {\n // Handle various URL formats:\n // https://drive.google.com/drive/folders/FOLDER_ID\n // https://drive.google.com/drive/folders/FOLDER_ID?usp=sharing\n\n const patterns = [\n /\\/folders\\/([a-zA-Z0-9_-]+)/, // Standard format\n /id=([a-zA-Z0-9_-]+)/ // Alternative format\n ];\n\n for (const pattern of patterns) {\n const match = folderUrl.match(pattern);\n if (match && match[1]) {\n return match[1];\n }\n }\n\n return null;\n }\n\n /**\n * Load images from a Google Drive folder\n * @param folderUrl - Google Drive folder URL\n * @param filter - Filter to apply to discovered images\n * @param recursive - Whether to include images from subfolders\n * @returns Promise resolving to array of image URLs\n */\n private async loadFromFolder(folderUrl: string, filter: IImageFilter, recursive: boolean = true): Promise<string[]> {\n const folderId = this.extractFolderId(folderUrl);\n\n if (!folderId) {\n throw new Error('Invalid Google Drive folder URL. Please check the URL format.');\n }\n\n // If no API key is configured, use direct link method\n if (!this.apiKey || this.apiKey === 'YOUR_API_KEY_HERE') {\n return this.loadImagesDirectly(folderId, filter);\n }\n\n try {\n if (recursive) {\n return await this.loadImagesRecursively(folderId, filter);\n } else {\n return await this.loadImagesFromSingleFolder(folderId, filter);\n }\n } catch (error) {\n console.error('Error loading from Google Drive API:', error);\n // Fallback to direct link method\n return this.loadImagesDirectly(folderId, filter);\n }\n }\n\n /**\n * Load images from a single folder (non-recursive)\n * @param folderId - Google Drive folder ID\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of image URLs\n */\n private async loadImagesFromSingleFolder(folderId: string, filter: IImageFilter): Promise<string[]> {\n const imageUrls: string[] = [];\n\n // Query for all files in this folder\n const query = `'${folderId}' in parents and trashed=false`;\n const fields = 'files(id,name,mimeType,thumbnailLink)';\n const url = `${this.apiEndpoint}?q=${encodeURIComponent(query)}&fields=${fields}&key=${this.apiKey}`;\n\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`API request failed: ${response.status} ${response.statusText}`);\n }\n\n const data: GoogleDriveResponse = await response.json();\n\n // Filter for valid image files only using the provided filter\n const validFiles = data.files.filter(file =>\n file.mimeType.startsWith('image/') && filter.isAllowed(file.name)\n );\n\n this.log(`Found ${validFiles.length} images in folder ${folderId} (non-recursive)`);\n\n // Add image URLs\n validFiles.forEach(file => {\n imageUrls.push(`https://lh3.googleusercontent.com/d/${file.id}=s1600`);\n this.log(`Added file: ${file.name}`);\n });\n\n return imageUrls;\n }\n\n /**\n * Load specific files by their URLs or IDs\n * @param fileUrls - Array of Google Drive file URLs or IDs\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of image URLs\n */\n private async loadFiles(fileUrls: string[], filter: IImageFilter): Promise<string[]> {\n const imageUrls: string[] = [];\n\n for (const fileUrl of fileUrls) {\n const fileId = this.extractFileId(fileUrl);\n\n if (!fileId) {\n this.log(`Skipping invalid file URL: ${fileUrl}`);\n continue;\n }\n\n // Validate it's an image file\n if (this.apiKey && this.apiKey !== 'YOUR_API_KEY_HERE') {\n try {\n // Get file metadata to verify it's an image\n const metadataUrl = `${this.apiEndpoint}/${fileId}?fields=name,mimeType&key=${this.apiKey}`;\n const response = await fetch(metadataUrl);\n\n if (response.ok) {\n const metadata = await response.json();\n if (metadata.mimeType.startsWith('image/') && filter.isAllowed(metadata.name)) {\n imageUrls.push(`https://lh3.googleusercontent.com/d/${fileId}=s1600`);\n this.log(`Added file: ${metadata.name}`);\n } else {\n this.log(`Skipping non-image file: ${metadata.name} (${metadata.mimeType})`);\n }\n } else {\n this.log(`Failed to fetch metadata for file ${fileId}: ${response.status}`);\n }\n } catch (error) {\n this.log(`Error fetching metadata for file ${fileId}:`, error);\n }\n } else {\n // Without API key, assume it's valid and add it\n imageUrls.push(`https://lh3.googleusercontent.com/d/${fileId}=s1600`);\n }\n }\n\n return imageUrls;\n }\n\n /**\n * Extract file ID from Google Drive file URL\n * @param fileUrl - Google Drive file URL or file ID\n * @returns File ID or null if invalid\n */\n private extractFileId(fileUrl: string): string | null {\n // Handle various URL formats:\n // https://drive.google.com/file/d/FILE_ID/view\n // https://drive.google.com/open?id=FILE_ID\n // FILE_ID (raw ID)\n\n // If it looks like a raw ID (no slashes or protocol), return it\n if (!/[/:.]/.test(fileUrl)) {\n return fileUrl;\n }\n\n const patterns = [\n /\\/file\\/d\\/([a-zA-Z0-9_-]+)/, // Standard file format\n /\\/open\\?id=([a-zA-Z0-9_-]+)/, // Alternative format\n /id=([a-zA-Z0-9_-]+)/ // Generic id parameter\n ];\n\n for (const pattern of patterns) {\n const match = fileUrl.match(pattern);\n if (match && match[1]) {\n return match[1];\n }\n }\n\n return null;\n }\n\n /**\n * Recursively load images from a folder and all its subfolders\n * @param folderId - Google Drive folder ID\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of image URLs\n */\n private async loadImagesRecursively(folderId: string, filter: IImageFilter): Promise<string[]> {\n const imageUrls: string[] = [];\n\n // Query for all files in this folder\n const query = `'${folderId}' in parents and trashed=false`;\n // Request thumbnailLink for PDFs\n const fields = 'files(id,name,mimeType,thumbnailLink)';\n const url = `${this.apiEndpoint}?q=${encodeURIComponent(query)}&fields=${fields}&key=${this.apiKey}`;\n\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`API request failed: ${response.status} ${response.statusText}`);\n }\n\n const data: GoogleDriveResponse = await response.json();\n\n // Separate images and folders using the provided filter\n const validFiles = data.files.filter(file =>\n file.mimeType.startsWith('image/') && filter.isAllowed(file.name)\n );\n\n const subfolders = data.files.filter(file =>\n file.mimeType === 'application/vnd.google-apps.folder'\n );\n\n this.log(`Found ${data.files.length} total items in folder ${folderId}`);\n // Log details of all files to see what we are missing\n data.files.forEach(f => this.log(` - File: ${f.name} (${f.mimeType})`));\n\n this.log(`- ${validFiles.length} valid files (images only)`);\n this.log(`- ${subfolders.length} subfolders`);\n\n // Add image URLs from this folder\n validFiles.forEach(file => {\n // Use the reliable thumbnail/preview endpoint for both Images and PDFs\n // This works for public folders and handles file format conversion automatically\n // 'sz=w1000' requests a high-quality preview (1000px width)\n // detailed explanation:\n // 1. \"drive.google.com\" is blocked by ad-blockers (net::ERR_BLOCKED_BY_CLIENT)\n // 2. The API's \"thumbnailLink\" is a signed URL that can expire or fail 403.\n // 3. \"lh3.googleusercontent.com/d/{ID}\" is the permanent CDN link structure.\n // It bypasses the domain block AND the signing issues.\n imageUrls.push(`https://lh3.googleusercontent.com/d/${file.id}=s1600`);\n\n this.log(`Added file: ${file.name}`);\n });\n\n // Recursively process subfolders\n for (const folder of subfolders) {\n this.log(`Loading images from subfolder: ${folder.name}`);\n const subfolderImages = await this.loadImagesRecursively(folder.id, filter);\n imageUrls.push(...subfolderImages);\n }\n\n return imageUrls;\n }\n\n /**\n * Direct loading method (no API key required, but less reliable)\n * Uses embedded folder view to scrape image IDs\n * @param folderId - Google Drive folder ID\n * @param filter - Filter to apply (not used in fallback mode)\n * @returns Promise resolving to array of image URLs\n */\n private async loadImagesDirectly(folderId: string, _filter: IImageFilter): Promise<string[]> {\n // For now, we'll return a method that requires the user to manually provide image IDs\n // or we construct URLs based on a known pattern\n\n // This is a fallback - in production, you'd want to use the API\n // For demo purposes, we can try to fetch the folder page and extract image IDs\n\n try {\n // Attempt to fetch folder page (CORS may block this)\n const folderUrl = `https://drive.google.com/embeddedfolderview?id=${folderId}`;\n const response = await fetch(folderUrl, { mode: 'cors' });\n\n if (!response.ok) {\n throw new Error('Cannot access folder directly (CORS or permissions issue)');\n }\n\n const html = await response.text();\n\n // Try to extract image IDs from HTML (this is fragile and may break)\n const imageIdPattern = /\\/file\\/d\\/([a-zA-Z0-9_-]+)/g;\n const matches = [...html.matchAll(imageIdPattern)];\n const imageIds = [...new Set(matches.map(m => m[1]))];\n\n const imageUrls = imageIds.map(id =>\n `https://drive.google.com/uc?export=view&id=${id}`\n );\n\n return imageUrls;\n\n } catch (error) {\n console.error('Direct loading failed:', error);\n throw new Error(\n 'Unable to load images. Please ensure:\\n' +\n '1. The folder is shared publicly (Anyone with the link can view)\\n' +\n '2. The folder contains image files\\n' +\n '3. Consider adding a Google Drive API key in config.js for better reliability'\n );\n }\n }\n\n /**\n * Manually add image URLs (for testing or when auto-loading fails)\n * @param imageIds - Array of Google Drive file IDs\n * @returns Array of direct image URLs\n */\n manualImageUrls(imageIds: string[]): string[] {\n return imageIds.map(id => `https://drive.google.com/uc?export=view&id=${id}`);\n }\n\n /**\n * Debug logging helper\n * @param args - Arguments to log\n */\n private log(...args: unknown[]): void {\n if (this.debugLogging && typeof console !== 'undefined') {\n console.log(...args);\n }\n }\n}\n","/**\n * StaticImageLoader.ts\n * Loads images from predefined URL sources and local paths\n * Compatible with ImageCloud's loader interface\n *\n * Public API:\n * - prepare(filter) - Async discovery of images\n * - imagesLength() - Get count of discovered images\n * - imageURLs() - Get ordered list of image URLs\n * - isPrepared() - Check if loader has been prepared\n */\n\nimport type { ImageLoader, IImageFilter, StaticSource, StaticLoaderInnerConfig } from '../config/types';\n\nexport class StaticImageLoader implements ImageLoader {\n private validateUrls: boolean;\n private validationTimeout: number;\n private validationMethod: 'head' | 'simple' | 'none';\n private sources: StaticSource[];\n private debugLogging: boolean;\n\n // State for new interface\n private _prepared: boolean = false;\n private _discoveredUrls: string[] = [];\n\n constructor(config: StaticLoaderInnerConfig) {\n this.validateUrls = config.validateUrls !== false;\n this.validationTimeout = config.validationTimeout ?? 5000;\n this.validationMethod = config.validationMethod ?? 'head';\n this.debugLogging = config.debugLogging ?? false;\n this.sources = config.sources ?? [];\n\n // Validate that we have sources configured\n if (!this.sources || this.sources.length === 0) {\n throw new Error('StaticImageLoader requires at least one source to be configured');\n }\n\n this.log('StaticImageLoader initialized with config:', config);\n }\n\n /**\n * Prepare the loader by discovering all images from configured sources\n * @param filter - Filter to apply to discovered images\n */\n async prepare(filter: IImageFilter): Promise<void> {\n this._discoveredUrls = [];\n\n this.log(`Processing ${this.sources.length} source(s)`);\n\n // Process sources sequentially to preserve order\n for (const source of this.sources) {\n try {\n const urls = await this.processSource(source, filter);\n this._discoveredUrls.push(...urls);\n } catch (error) {\n console.warn('Failed to process source:', source, error);\n // Continue processing other sources\n }\n }\n\n this._prepared = true;\n this.log(`Successfully loaded ${this._discoveredUrls.length} image(s)`);\n }\n\n /**\n * Get the number of discovered images\n * @throws Error if called before prepare()\n */\n imagesLength(): number {\n if (!this._prepared) {\n throw new Error('StaticImageLoader.imagesLength() called before prepare()');\n }\n return this._discoveredUrls.length;\n }\n\n /**\n * Get the ordered list of image URLs\n * @throws Error if called before prepare()\n */\n imageURLs(): string[] {\n if (!this._prepared) {\n throw new Error('StaticImageLoader.imageURLs() called before prepare()');\n }\n return [...this._discoveredUrls];\n }\n\n /**\n * Check if the loader has been prepared\n */\n isPrepared(): boolean {\n return this._prepared;\n }\n\n /**\n * Process a single source object using shape-based detection\n * @param source - Source configuration detected by key presence\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of valid URLs from this source\n */\n private async processSource(source: StaticSource, filter: IImageFilter): Promise<string[]> {\n if (!source) {\n console.warn('Invalid source object:', source);\n return [];\n }\n\n if ('urls' in source) {\n return await this.processUrls(source.urls, filter);\n } else if ('path' in source) {\n return await this.processPath(source.path, source.files, filter);\n } else if ('json' in source) {\n return await this.processJson(source.json, filter);\n } else {\n console.warn('Unknown source shape:', source);\n return [];\n }\n }\n\n /**\n * Process a list of direct URLs\n * @param urls - Array of image URLs\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of validated URLs\n */\n private async processUrls(urls: string[], filter: IImageFilter): Promise<string[]> {\n if (!Array.isArray(urls)) {\n console.warn('URLs must be an array:', urls);\n return [];\n }\n\n const validUrls: string[] = [];\n\n for (const url of urls) {\n // Apply filter based on URL filename\n const filename = url.split('/').pop() || url;\n if (!filter.isAllowed(filename)) {\n this.log(`Skipping filtered URL: ${url}`);\n continue;\n }\n\n if (this.validateUrls) {\n const isValid = await this.validateUrl(url);\n if (isValid) {\n validUrls.push(url);\n } else {\n console.warn(`Skipping invalid/missing URL: ${url}`);\n }\n } else {\n // No validation - add all URLs\n validUrls.push(url);\n }\n }\n\n return validUrls;\n }\n\n /**\n * Process a path-based source\n * @param basePath - Base path (relative or absolute)\n * @param files - Array of filenames\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of validated URLs\n */\n private async processPath(basePath: string, files: string[], filter: IImageFilter): Promise<string[]> {\n\n if (!Array.isArray(files)) {\n console.warn('files must be an array:', files);\n return [];\n }\n\n const validUrls: string[] = [];\n\n for (const file of files) {\n // Apply filter based on filename\n if (!filter.isAllowed(file)) {\n this.log(`Skipping filtered file: ${file}`);\n continue;\n }\n\n const url = this.constructUrl(basePath, file);\n\n if (this.validateUrls) {\n const isValid = await this.validateUrl(url);\n if (isValid) {\n validUrls.push(url);\n } else {\n console.warn(`Skipping invalid/missing file: ${url}`);\n }\n } else {\n // No validation - add all URLs\n validUrls.push(url);\n }\n }\n\n return validUrls;\n }\n\n /**\n * Process a JSON endpoint source\n * Fetches a JSON endpoint that returns { images: string[] }\n * @param url - JSON endpoint URL\n * @param filter - Filter to apply to discovered images\n * @returns Promise resolving to array of validated URLs\n */\n private async processJson(url: string, filter: IImageFilter): Promise<string[]> {\n\n this.log(`Fetching JSON endpoint: ${url}`);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 10000);\n\n try {\n const response = await fetch(url, { signal: controller.signal });\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status} fetching ${url}`);\n }\n\n const data = await response.json();\n\n if (!data || !Array.isArray(data.images)) {\n throw new Error(`JSON source must return JSON with shape { \"images\": [\"url1\", \"url2\", ...] }`);\n }\n\n this.log(`JSON endpoint returned ${data.images.length} image(s)`);\n\n // Process the URLs through the standard URL processing pipeline\n return await this.processUrls(data.images, filter);\n } catch (error) {\n clearTimeout(timeoutId);\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Timeout fetching JSON endpoint: ${url}`);\n }\n throw error;\n }\n }\n\n /**\n * Validate a single URL using HEAD request\n * @param url - URL to validate\n * @returns Promise resolving to true if valid and accessible\n */\n private async validateUrl(url: string): Promise<boolean> {\n if (this.validationMethod === 'none') {\n return true;\n }\n\n if (this.validationMethod === 'simple') {\n // Basic URL format check\n try {\n if (typeof window !== 'undefined') {\n new URL(url, window.location.origin);\n } else {\n new URL(url);\n }\n return true;\n } catch {\n return false;\n }\n }\n\n // validationMethod === 'head' (default)\n // For cross-origin URLs, we can't validate due to CORS\n // So we only validate same-origin URLs\n if (typeof window === 'undefined') {\n return true; // In non-browser environment, assume valid\n }\n\n const isSameOrigin = url.startsWith(window.location.origin) ||\n url.startsWith('/');\n\n if (!isSameOrigin) {\n // Cross-origin URL - assume valid, can't validate due to CORS\n this.log(`Skipping validation for cross-origin URL: ${url}`);\n return true;\n }\n\n // Same-origin URL - validate with HEAD request\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.validationTimeout);\n\n const response = await fetch(url, {\n method: 'HEAD',\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return true;\n } else {\n this.log(`Validation failed for ${url}: HTTP ${response.status}`);\n return false;\n }\n\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n this.log(`Validation timeout for ${url}`);\n } else {\n this.log(`Validation failed for ${url}:`, error.message);\n }\n }\n return false;\n }\n }\n\n /**\n * Construct full URL from basePath and filename\n * @param basePath - Base path (relative or absolute)\n * @param filename - Filename to append\n * @returns Complete URL\n */\n private constructUrl(basePath: string, filename: string): string {\n // Remove trailing slash from basePath\n const cleanBase = basePath.replace(/\\/$/, '');\n\n // Check if basePath is absolute URL\n if (this.isAbsoluteUrl(basePath)) {\n return `${cleanBase}/${filename}`;\n }\n\n // Relative path - prepend current origin\n if (typeof window === 'undefined') {\n return `${cleanBase}/${filename}`; // In non-browser environment, return as-is\n }\n\n const origin = window.location.origin;\n // Ensure basePath starts with /\n const normalizedPath = basePath.startsWith('/') ? basePath : '/' + basePath;\n const cleanPath = normalizedPath.replace(/\\/$/, '');\n\n return `${origin}${cleanPath}/${filename}`;\n }\n\n /**\n * Check if URL is absolute (contains protocol)\n * @param url - URL to check\n * @returns True if absolute URL\n */\n private isAbsoluteUrl(url: string): boolean {\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Debug logging helper\n * @param args - Arguments to log\n */\n private log(...args: unknown[]): void {\n if (this.debugLogging && typeof console !== 'undefined') {\n console.log(...args);\n }\n }\n}\n","/**\n * CompositeLoader.ts\n * Combines multiple image loaders and loads them in parallel\n *\n * Public API:\n * - prepare(filter) - Async discovery of images from all loaders in parallel\n * - imagesLength() - Get combined count of discovered images\n * - imageURLs() - Get combined ordered list of image URLs\n * - isPrepared() - Check if loader has been prepared\n */\n\nimport type { ImageLoader, IImageFilter } from '../config/types';\n\nexport interface CompositeLoaderConfig {\n loaders: ImageLoader[];\n debugLogging?: boolean;\n}\n\nexport class CompositeLoader implements ImageLoader {\n private loaders: ImageLoader[];\n private debugLogging: boolean;\n\n // State for interface\n private _prepared: boolean = false;\n private _discoveredUrls: string[] = [];\n\n constructor(config: CompositeLoaderConfig) {\n this.loaders = config.loaders;\n this.debugLogging = config.debugLogging ?? false;\n\n // Validate that we have at least one loader\n if (!this.loaders || this.loaders.length === 0) {\n throw new Error('CompositeLoader requires at least one loader to be configured');\n }\n\n this.log(`CompositeLoader initialized with ${this.loaders.length} loader(s)`);\n }\n\n /**\n * Prepare all loaders in parallel and combine their results\n * @param filter - Filter to apply to discovered images\n */\n async prepare(filter: IImageFilter): Promise<void> {\n this._discoveredUrls = [];\n\n this.log(`Preparing ${this.loaders.length} loader(s) in parallel`);\n\n // Prepare all loaders in parallel\n const preparePromises = this.loaders.map((loader, index) => {\n return loader.prepare(filter).then(() => {\n this.log(`Loader ${index} prepared with ${loader.imagesLength()} images`);\n }).catch(error => {\n console.warn(`Loader ${index} failed to prepare:`, error);\n // Continue with other loaders even if one fails\n });\n });\n\n await Promise.all(preparePromises);\n\n // Combine URLs from all prepared loaders (preserves order of loaders array)\n for (const loader of this.loaders) {\n if (loader.isPrepared()) {\n const urls = loader.imageURLs();\n this._discoveredUrls.push(...urls);\n }\n }\n\n this._prepared = true;\n this.log(`CompositeLoader prepared with ${this._discoveredUrls.length} total images`);\n }\n\n /**\n * Get the combined number of discovered images\n * @throws Error if called before prepare()\n */\n imagesLength(): number {\n if (!this._prepared) {\n throw new Error('CompositeLoader.imagesLength() called before prepare()');\n }\n return this._discoveredUrls.length;\n }\n\n /**\n * Get the combined ordered list of image URLs\n * @throws Error if called before prepare()\n */\n imageURLs(): string[] {\n if (!this._prepared) {\n throw new Error('CompositeLoader.imageURLs() called before prepare()');\n }\n return [...this._discoveredUrls];\n }\n\n /**\n * Check if the loader has been prepared\n */\n isPrepared(): boolean {\n return this._prepared;\n }\n\n /**\n * Debug logging helper\n * @param args - Arguments to log\n */\n private log(...args: unknown[]): void {\n if (this.debugLogging && typeof console !== 'undefined') {\n console.log('[CompositeLoader]', ...args);\n }\n }\n}\n","/**\n * ImageFilter.ts\n * Filters images by extension, designed for future extensibility\n * (e.g., size filters, date filters, etc.)\n */\n\nexport class ImageFilter {\n private allowedExtensions: string[];\n\n /**\n * Create a new ImageFilter\n * @param extensions - Array of allowed file extensions (without dots)\n * Defaults to common image formats if not provided\n */\n constructor(extensions?: string[]) {\n this.allowedExtensions = extensions || [\n 'jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'\n ];\n }\n\n /**\n * Check if a filename has an allowed extension\n * @param filename - The filename to check (can include path or query string)\n * @returns True if the file extension is allowed\n */\n isAllowed(filename: string): boolean {\n // Remove query string if present (for URLs like image.jpg?w=800)\n const withoutQuery = filename.split('?')[0];\n const extension = withoutQuery.split('.').pop()?.toLowerCase();\n return extension ? this.allowedExtensions.includes(extension) : false;\n }\n\n /**\n * Get the list of allowed extensions\n * @returns Array of allowed extensions\n */\n getAllowedExtensions(): string[] {\n return [...this.allowedExtensions];\n }\n\n // Future expansion methods:\n // isAllowedSize(sizeBytes: number): boolean\n // isAllowedDate(date: Date): boolean\n // isAllowedDimensions(width: number, height: number): boolean\n}\n","/**\n * Minimal functional CSS required for the library to work.\n * Injected automatically - no external CSS file needed.\n */\nexport const FUNCTIONAL_CSS = `\n.fbn-ic-gallery {\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n perspective: 1000px;\n}\n\n.fbn-ic-image {\n position: absolute;\n cursor: pointer;\n transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1),\n box-shadow 0.6s cubic-bezier(0.4, 0, 0.2, 1),\n filter 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n border 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n outline 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n z-index 0s 0.6s;\n will-change: transform;\n user-select: none;\n backface-visibility: hidden;\n -webkit-backface-visibility: hidden;\n}\n\n.fbn-ic-image.fbn-ic-focused {\n z-index: 1000;\n transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1),\n box-shadow 0.6s cubic-bezier(0.4, 0, 0.2, 1),\n filter 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n border 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n outline 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n z-index 0s 0s;\n will-change: auto;\n}\n\n.fbn-ic-counter {\n position: fixed;\n bottom: 24px;\n left: 50%;\n transform: translateX(-50%);\n z-index: 10001;\n pointer-events: none;\n}\n\n.fbn-ic-gallery:focus,\n.fbn-ic-gallery.fbn-ic-has-focus {\n outline: 2px solid rgba(147, 197, 253, 0.8);\n outline-offset: -4px;\n}\n.fbn-ic-gallery.fbn-ic-suppress-outline:focus {\n outline: none;\n}\n.fbn-ic-gallery.fbn-ic-suppress-outline.fbn-ic-has-focus {\n outline: 2px solid rgba(99, 102, 241, 0.6);\n outline-offset: -4px;\n}\n\n.fbn-ic-nav-btn {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n z-index: 10001;\n cursor: pointer;\n border: none;\n background: none;\n padding: 0;\n line-height: 1;\n}\n.fbn-ic-nav-btn-prev {\n left: 12px;\n}\n.fbn-ic-nav-btn-next {\n right: 12px;\n}\n\n.fbn-ic-hidden {\n display: none !important;\n}\n`;\n\n/**\n * Inject functional styles into document head.\n * Idempotent - safe to call multiple times.\n */\nexport function injectFunctionalStyles(): void {\n if (typeof document === 'undefined') return;\n const id = 'fbn-ic-functional-styles';\n if (document.getElementById(id)) return;\n const style = document.createElement('style');\n style.id = id;\n style.textContent = FUNCTIONAL_CSS;\n document.head.appendChild(style);\n}\n","/**\n * ImageCloud.ts\n * Main application class\n * Manages initialization and coordination of the interactive image cloud\n */\n\nimport type { ImageCloudOptions, ImageCloudConfig, ImageLayout, ContainerBounds, ImageLoader, EntryAnimationConfig, LoaderEntry, SharedLoaderConfig, StaticLoaderInnerConfig, GoogleDriveLoaderInnerConfig } from './config/types';\nimport { mergeConfig, DEFAULT_CONFIG } from './config/defaults';\nimport { AnimationEngine } from './engines/AnimationEngine';\nimport { EntryAnimationEngine } from './engines/EntryAnimationEngine';\nimport { IdleAnimationEngine } from './engines/IdleAnimationEngine';\nimport { LayoutEngine } from './engines/LayoutEngine';\nimport { ZoomEngine } from './engines/ZoomEngine';\nimport { SwipeEngine, SNAP_BACK_DURATION_MS } from './engines/SwipeEngine';\nimport { animatePath } from './engines/PathAnimator';\nimport { GoogleDriveLoader } from './loaders/GoogleDriveLoader';\nimport { StaticImageLoader } from './loaders/StaticImageLoader';\nimport { CompositeLoader } from './loaders/CompositeLoader';\nimport { ImageFilter } from './loaders/ImageFilter';\nimport { buildStyleProperties, applyStylesToElementWithState, applyClassNameToElement, removeClassNameFromElement, StyleProperties } from './utils/styleUtils';\nimport { injectFunctionalStyles } from './styles/functionalStyles';\n\nexport class ImageCloud {\n private containerId: string | null;\n private containerRef: HTMLElement | null;\n\n // Internal state\n private fullConfig: ImageCloudConfig;\n private imagesLoaded: boolean;\n private imageElements: HTMLImageElement[];\n private imageLayouts: ImageLayout[];\n private currentImageHeight: number;\n private currentFocusIndex: number | null;\n private hoveredImage: { element: HTMLImageElement; layout: ImageLayout } | null;\n private resizeTimeout: number | null;\n private displayQueue: HTMLImageElement[];\n private queueInterval: number | null;\n private loadGeneration: number;\n\n // Precomputed styling\n private defaultStyles: StyleProperties;\n private defaultClassName: string | string[] | undefined;\n private hoverClassName: string | string[] | undefined;\n\n // Modules\n private animationEngine: AnimationEngine;\n private entryAnimationEngine: EntryAnimationEngine;\n private idleAnimationEngine: IdleAnimationEngine | null;\n private layoutEngine: LayoutEngine;\n private zoomEngine: ZoomEngine;\n private swipeEngine: SwipeEngine | null;\n private imageLoader: ImageLoader;\n private imageFilter: ImageFilter;\n\n // DOM Elements\n private containerEl: HTMLElement | null;\n private loadingEl: HTMLElement | null;\n private errorEl: HTMLElement | null;\n private loadingElAutoCreated: boolean;\n private errorElAutoCreated: boolean;\n private counterEl: HTMLElement | null;\n private counterElAutoCreated: boolean;\n private prevButtonEl: HTMLElement | null;\n private nextButtonEl: HTMLElement | null;\n private prevButtonElAutoCreated: boolean;\n private nextButtonElAutoCreated: boolean;\n\n constructor(options: ImageCloudOptions = {}) {\n this.fullConfig = mergeConfig(options);\n\n // Container can be a string ID or an HTMLElement reference\n if (options.container instanceof HTMLElement) {\n this.containerRef = options.container;\n this.containerId = null;\n } else {\n this.containerRef = null;\n this.containerId = options.container || 'imageCloud';\n }\n\n // Internal state\n this.imagesLoaded = false;\n this.imageElements = [];\n this.imageLayouts = [];\n this.currentImageHeight = 225;\n this.currentFocusIndex = null;\n this.hoveredImage = null;\n this.resizeTimeout = null;\n this.displayQueue = [];\n this.queueInterval = null;\n this.loadGeneration = 0;\n this.loadingElAutoCreated = false;\n this.errorElAutoCreated = false;\n this.counterEl = null;\n this.counterElAutoCreated = false;\n this.prevButtonEl = null;\n this.nextButtonEl = null;\n this.prevButtonElAutoCreated = false;\n this.nextButtonElAutoCreated = false;\n\n // Initialize engines with new config structure\n this.animationEngine = new AnimationEngine(this.fullConfig.animation);\n this.layoutEngine = new LayoutEngine({\n layout: this.fullConfig.layout,\n image: this.fullConfig.image\n });\n this.zoomEngine = new ZoomEngine(this.fullConfig.interaction.focus, this.animationEngine, this.fullConfig.styling);\n\n // Precompute styling properties\n this.defaultStyles = buildStyleProperties(this.fullConfig.styling?.default);\n this.defaultClassName = this.fullConfig.styling?.default?.className;\n this.hoverClassName = this.fullConfig.styling?.hover?.className;\n\n // Initialize entry animation engine with layout-aware defaults\n const entryConfig = this.fullConfig.animation.entry || DEFAULT_CONFIG.animation.entry!;\n this.entryAnimationEngine = new EntryAnimationEngine(\n entryConfig as EntryAnimationConfig,\n this.fullConfig.layout.algorithm\n );\n\n // Initialize idle animation engine if configured\n const idleConfig = this.fullConfig.animation.idle;\n if (idleConfig && idleConfig.type !== 'none') {\n this.idleAnimationEngine = new IdleAnimationEngine(\n idleConfig,\n (entryConfig as EntryAnimationConfig).timing?.duration ?? 600\n );\n } else {\n this.idleAnimationEngine = null;\n }\n\n // Wire unfocus complete callback to resume idle animations and re-apply hover styles\n this.zoomEngine.setOnUnfocusCompleteCallback((el) => {\n this.idleAnimationEngine?.resumeForImage(el as HTMLImageElement);\n // If the cursor is still over this image, mouseenter won't re-fire — re-apply hover styles.\n // Defer to next frame so the browser updates :hover after the animation finishes.\n const img = el as HTMLImageElement;\n requestAnimationFrame(() => {\n if (img.matches(':hover') && this.fullConfig.styling?.hover) {\n const idx = this.imageElements.indexOf(img);\n if (idx !== -1) {\n const imageHeight = img.offsetHeight;\n const cachedWidth = (img as any).cachedRenderedWidth;\n applyStylesToElementWithState(img, this.fullConfig.styling.hover, imageHeight, cachedWidth);\n applyClassNameToElement(img, this.hoverClassName);\n this.hoveredImage = { element: img, layout: this.imageLayouts[idx] };\n }\n }\n });\n });\n\n // SwipeEngine will be initialized after container is available\n this.swipeEngine = null;\n\n // Initialize image filter with configured extensions\n this.imageFilter = this.createImageFilter();\n\n // Initialize image loader based on type\n this.imageLoader = this.createLoader();\n\n // DOM Elements (will be fetched on init)\n this.containerEl = null;\n this.loadingEl = null;\n this.errorEl = null;\n }\n\n /**\n * Create image filter based on shared loader config\n */\n private createImageFilter(): ImageFilter {\n const extensions = this.fullConfig.config.loaders?.allowedExtensions;\n return new ImageFilter(extensions);\n }\n\n /**\n * Create appropriate image loader based on config\n * Processes loaders array, merges shared config, wraps in CompositeLoader if needed\n */\n private createLoader(): ImageLoader {\n const entries = this.fullConfig.loaders;\n const shared = this.fullConfig.config.loaders ?? {};\n\n if (!entries || entries.length === 0) {\n throw new Error('No loaders configured. Provide `images`, `loaders`, or both.\\n Example: imageCloud({ container: \\'id\\', images: [\\'https://...\\'] })');\n }\n\n const childLoaders = entries.map(entry => this.createLoaderFromEntry(entry, shared));\n\n if (childLoaders.length === 1) {\n return childLoaders[0];\n }\n\n return new CompositeLoader({\n loaders: childLoaders,\n debugLogging: this.fullConfig.config.debug?.loaders\n });\n }\n\n /**\n * Create a single loader from a LoaderEntry, merging shared config\n */\n private createLoaderFromEntry(entry: LoaderEntry, shared: SharedLoaderConfig): ImageLoader {\n if ('static' in entry) {\n const inner = entry.static;\n const merged: StaticLoaderInnerConfig = {\n ...inner,\n validateUrls: inner.validateUrls ?? shared.validateUrls,\n validationTimeout: inner.validationTimeout ?? shared.validationTimeout,\n validationMethod: inner.validationMethod ?? shared.validationMethod,\n allowedExtensions: inner.allowedExtensions ?? shared.allowedExtensions,\n debugLogging: inner.debugLogging ?? this.fullConfig.config.debug?.loaders\n };\n return new StaticImageLoader(merged);\n } else if ('googleDrive' in entry) {\n const inner = entry.googleDrive;\n const merged: GoogleDriveLoaderInnerConfig = {\n ...inner,\n allowedExtensions: inner.allowedExtensions ?? shared.allowedExtensions,\n debugLogging: inner.debugLogging ?? this.fullConfig.config.debug?.loaders\n };\n return new GoogleDriveLoader(merged);\n } else {\n throw new Error(`Unknown loader entry: ${JSON.stringify(entry)}`);\n }\n }\n\n /**\n * Initialize the gallery\n */\n async init(): Promise<void> {\n try {\n // Inject functional styles (idempotent)\n injectFunctionalStyles();\n\n // 1. Setup DOM\n if (this.containerRef) {\n this.containerEl = this.containerRef;\n } else {\n this.containerEl = document.getElementById(this.containerId!);\n if (!this.containerEl) {\n throw new Error(`Container \"#${this.containerId}\" not found. Ensure an element with id=\"${this.containerId}\" exists in the DOM before calling imageCloud().`);\n }\n }\n\n // Add gallery class for CSS scoping\n this.containerEl.classList.add('fbn-ic-gallery');\n this.containerEl.setAttribute('tabindex', '0');\n\n // Initialize swipe engine for touch navigation (guarded by config flag)\n if (this.fullConfig.interaction.navigation?.swipe !== false) {\n this.swipeEngine = new SwipeEngine(this.containerEl, {\n onNext: () => this.navigateToNextImage(),\n onPrev: () => this.navigateToPreviousImage(),\n onDragOffset: (offset) => this.zoomEngine.setDragOffset(offset),\n onDragEnd: (navigated) => {\n if (!navigated) {\n // Snap back to center with animation\n this.zoomEngine.clearDragOffset(true, SNAP_BACK_DURATION_MS);\n } else {\n // Clear offset immediately (navigation handles transition)\n this.zoomEngine.clearDragOffset(false);\n }\n }\n });\n }\n\n // Create or bind UI elements\n this.setupUI();\n\n // 2. Setup Event Listeners\n this.setupEventListeners();\n\n // 3. Load Images\n this.logDebug('ImageCloud initialized');\n await this.loadImages();\n\n } catch (error) {\n console.error(`ImageCloud initialization failed (container: \"${this.containerId ?? 'element'}\"):`, error);\n if (this.errorEl && error instanceof Error) {\n this.showError('Gallery failed to initialize: ' + error.message);\n }\n }\n }\n\n private setupUI(): void {\n const uiConfig = this.fullConfig.ui;\n\n // Manage focus outline: suppress browser ring by default, restore when showFocusOutline: true\n if (!uiConfig.showFocusOutline) {\n this.containerEl?.classList.add('fbn-ic-suppress-outline');\n } else {\n this.containerEl?.classList.remove('fbn-ic-suppress-outline');\n }\n\n // Loading element\n if (uiConfig.showLoadingSpinner) {\n if (uiConfig.loadingElement) {\n this.loadingEl = this.resolveElement(uiConfig.loadingElement);\n this.loadingElAutoCreated = false;\n } else {\n this.loadingEl = this.createDefaultLoadingElement();\n this.loadingElAutoCreated = true;\n }\n }\n\n // Error element\n if (uiConfig.errorElement) {\n this.errorEl = this.resolveElement(uiConfig.errorElement);\n this.errorElAutoCreated = false;\n } else {\n this.errorEl = this.createDefaultErrorElement();\n this.errorElAutoCreated = true;\n }\n\n // Counter element\n if (uiConfig.showImageCounter) {\n if (uiConfig.counterElement) {\n this.counterEl = this.resolveElement(uiConfig.counterElement);\n this.counterElAutoCreated = false;\n } else {\n this.counterEl = this.createDefaultCounterElement();\n this.counterElAutoCreated = true;\n }\n }\n\n // Nav button elements\n if (uiConfig.showNavButtons) {\n if (uiConfig.prevButtonElement) {\n this.prevButtonEl = this.resolveElement(uiConfig.prevButtonElement);\n this.prevButtonElAutoCreated = false;\n } else {\n this.prevButtonEl = this.createDefaultPrevButtonElement();\n this.prevButtonElAutoCreated = true;\n }\n if (uiConfig.nextButtonElement) {\n this.nextButtonEl = this.resolveElement(uiConfig.nextButtonElement);\n this.nextButtonElAutoCreated = false;\n } else {\n this.nextButtonEl = this.createDefaultNextButtonElement();\n this.nextButtonElAutoCreated = true;\n }\n this.prevButtonEl?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.navigateToPreviousImage();\n });\n this.nextButtonEl?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.navigateToNextImage();\n });\n }\n }\n\n private resolveElement(ref: string | HTMLElement): HTMLElement | null {\n if (ref instanceof HTMLElement) return ref;\n return document.getElementById(ref);\n }\n\n private createDefaultLoadingElement(): HTMLElement {\n const el = document.createElement('div');\n el.className = 'fbn-ic-loading fbn-ic-hidden';\n const spinner = document.createElement('div');\n spinner.className = 'fbn-ic-spinner';\n el.appendChild(spinner);\n const text = document.createElement('p');\n text.textContent = 'Loading images...';\n el.appendChild(text);\n this.containerEl!.appendChild(el);\n return el;\n }\n\n private createDefaultErrorElement(): HTMLElement {\n const el = document.createElement('div');\n el.className = 'fbn-ic-error fbn-ic-hidden';\n this.containerEl!.appendChild(el);\n return el;\n }\n\n private createDefaultCounterElement(): HTMLElement {\n const el = document.createElement('div');\n el.className = 'fbn-ic-counter fbn-ic-hidden';\n this.containerEl!.appendChild(el);\n return el;\n }\n\n private createDefaultPrevButtonElement(): HTMLElement {\n const el = document.createElement('button');\n el.className = 'fbn-ic-nav-btn fbn-ic-nav-btn-prev fbn-ic-hidden';\n el.textContent = '‹';\n el.setAttribute('aria-label', 'Previous image');\n el.setAttribute('tabindex', '-1');\n this.containerEl!.appendChild(el);\n return el;\n }\n\n private createDefaultNextButtonElement(): HTMLElement {\n const el = document.createElement('button');\n el.className = 'fbn-ic-nav-btn fbn-ic-nav-btn-next fbn-ic-hidden';\n el.textContent = '›';\n el.setAttribute('aria-label', 'Next image');\n el.setAttribute('tabindex', '-1');\n this.containerEl!.appendChild(el);\n return el;\n }\n\n private setupEventListeners(): void {\n // Keyboard navigation — scoped to container, guarded by config flag\n if (this.fullConfig.interaction.navigation?.keyboard !== false) {\n this.containerEl!.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n this.zoomEngine.unfocusImage();\n this.currentFocusIndex = null;\n this.swipeEngine?.disable();\n this.hideCounter();\n this.hideNavButtons();\n this.hideFocusIndicator();\n } else if (e.key === 'ArrowRight') {\n this.navigateToNextImage();\n } else if (e.key === 'ArrowLeft') {\n this.navigateToPreviousImage();\n } else if ((e.key === 'Enter' || e.key === ' ') && this.hoveredImage) {\n this.handleImageClick(this.hoveredImage.element, this.hoveredImage.layout);\n e.preventDefault();\n }\n });\n }\n\n document.addEventListener('click', (e: MouseEvent) => {\n // Ignore clicks that follow touch events (prevents unfocus during swipe)\n if (this.swipeEngine?.hadRecentTouch()) {\n return;\n }\n if (!(e.target as HTMLElement).closest('.fbn-ic-image') &&\n !(e.target as HTMLElement).closest('.fbn-ic-nav-btn')) {\n this.zoomEngine.unfocusImage();\n this.currentFocusIndex = null;\n this.swipeEngine?.disable();\n this.hideCounter();\n this.hideNavButtons();\n this.hideFocusIndicator();\n }\n });\n\n // Resize handler\n window.addEventListener('resize', () => this.handleResize());\n }\n\n /**\n * Navigate to the next image (Right arrow)\n */\n private navigateToNextImage(): void {\n if (this.currentFocusIndex === null || this.imageElements.length === 0) return;\n\n const nextId = (this.currentFocusIndex + 1) % this.imageLayouts.length;\n const nextElement = this.imageElements.find(\n el => el.dataset.imageId === String(nextId)\n );\n if (!nextElement) return;\n\n const layout = this.imageLayouts[nextId];\n if (!layout) return;\n\n this.currentFocusIndex = nextId;\n this.handleImageClick(nextElement, layout);\n this.updateCounter(nextId);\n this.showNavButtons();\n this.showFocusIndicator();\n }\n\n /**\n * Navigate to the previous image (Left arrow)\n */\n private navigateToPreviousImage(): void {\n if (this.currentFocusIndex === null || this.imageElements.length === 0) return;\n\n const prevId = (this.currentFocusIndex - 1 + this.imageLayouts.length) % this.imageLayouts.length;\n const prevElement = this.imageElements.find(\n el => el.dataset.imageId === String(prevId)\n );\n if (!prevElement) return;\n\n const layout = this.imageLayouts[prevId];\n if (!layout) return;\n\n this.currentFocusIndex = prevId;\n this.handleImageClick(prevElement, layout);\n this.updateCounter(prevId);\n this.showNavButtons();\n this.showFocusIndicator();\n }\n\n /**\n * Navigate to a specific image by index\n */\n private handleResize(): void {\n if (!this.imagesLoaded) return;\n\n if (this.resizeTimeout !== null) {\n clearTimeout(this.resizeTimeout);\n }\n\n this.resizeTimeout = window.setTimeout(() => {\n const newHeight = this.getImageHeight();\n\n if (newHeight !== this.currentImageHeight) {\n this.logDebug(`Window resized to new breakpoint (height: ${newHeight}px). Reloading images...`);\n // Reload images with new breakpoint\n this.loadImages();\n } else {\n this.logDebug('Window resized (no breakpoint change)');\n }\n }, 500);\n }\n\n private getImageHeight(): number {\n const width = window.innerWidth;\n const responsive = this.fullConfig.layout.responsive;\n\n // Get sizing config for adaptive mode defaults\n const sizing = this.fullConfig.image.sizing;\n const maxSize = sizing?.maxSize ?? 400;\n\n // Use responsive breakpoints to determine max height\n // These serve as upper bounds for the adaptive sizing\n if (!responsive) {\n // Fallback defaults if responsive not configured\n if (width <= 767) return Math.min(100, maxSize);\n if (width <= 1199) return Math.min(180, maxSize);\n return Math.min(225, maxSize);\n }\n\n if (width <= responsive.mobile.maxWidth) {\n return Math.min(100, maxSize); // Mobile\n }\n if (width <= responsive.tablet.maxWidth) {\n return Math.min(180, maxSize); // Tablet\n }\n return Math.min(225, maxSize); // Screen (desktop)\n }\n\n /**\n * Get container bounds for layout calculations\n */\n private getContainerBounds(): { width: number; height: number } {\n if (!this.containerEl) {\n return { width: window.innerWidth, height: window.innerHeight * 0.7 };\n }\n return {\n width: this.containerEl.offsetWidth,\n height: this.containerEl.offsetHeight || window.innerHeight * 0.7\n };\n }\n\n /**\n * Load images using the unified loader interface\n */\n private async loadImages(): Promise<void> {\n try {\n this.showLoading(true);\n this.hideError();\n this.clearImageCloud();\n\n // Prepare the loader (show spinner during this)\n await this.imageLoader.prepare(this.imageFilter);\n\n // Get image count and URLs from loader\n const imageCount = this.imageLoader.imagesLength();\n let imageUrls = this.imageLoader.imageURLs();\n\n if (imageCount === 0) {\n this.showError('No images found.');\n this.showLoading(false);\n return;\n }\n\n // Calculate adaptive sizing based on container and image count\n const containerBounds = this.getContainerBounds();\n const responsiveHeight = this.getImageHeight();\n const viewportWidth = window.innerWidth;\n\n this.logDebug(`Adaptive sizing input: container=${containerBounds.width}x${containerBounds.height}px, images=${imageCount}, responsiveMax=${responsiveHeight}px`);\n\n const sizingResult = this.layoutEngine.calculateAdaptiveSize(\n containerBounds,\n imageCount,\n responsiveHeight,\n viewportWidth\n );\n\n this.logDebug(`Adaptive sizing result: height=${sizingResult.height}px`);\n\n await this.createImageCloud(imageUrls, sizingResult.height);\n\n this.showLoading(false);\n this.imagesLoaded = true;\n\n } catch (error) {\n console.error('Error loading images:', error);\n if (error instanceof Error) {\n this.showError(error.message || 'Failed to load images.');\n }\n this.showLoading(false);\n }\n }\n\n /**\n * Helper for debug logging\n */\n private logDebug(...args: unknown[]): void {\n if (this.fullConfig.config.debug?.enabled && typeof console !== 'undefined') {\n console.log(...args);\n }\n }\n\n private async createImageCloud(imageUrls: string[], imageHeight: number): Promise<void> {\n if (!this.containerEl) return;\n\n const containerBounds = this.getContainerBounds();\n this.currentImageHeight = imageHeight;\n\n // Capture current generation to detect stale callbacks\n const currentGeneration = this.loadGeneration;\n\n // Generate layout\n const layouts = this.layoutEngine.generateLayout(imageUrls.length, containerBounds, { fixedHeight: imageHeight } as any);\n this.imageLayouts = layouts;\n\n this.displayQueue = [];\n let processedCount = 0;\n\n // Helper to display a single image with animation\n const displayImage = (img: HTMLImageElement) => {\n if (!this.containerEl) return;\n\n this.containerEl.appendChild(img);\n this.imageElements.push(img);\n\n requestAnimationFrame(() => {\n void img.offsetWidth; // Force reflow\n // Use configured default opacity, or 1 if not specified\n img.style.opacity = this.defaultStyles.opacity ?? '1';\n\n // Check if we need JS animation for path type, rotation, or scale\n const needsJSAnimation = img.dataset.startX &&\n (this.entryAnimationEngine.requiresJSAnimation() ||\n this.entryAnimationEngine.requiresJSRotation() ||\n this.entryAnimationEngine.requiresJSScale() ||\n img.dataset.startRotation !== img.dataset.rotation ||\n img.dataset.startScale !== img.dataset.scale);\n\n if (needsJSAnimation) {\n // Use animatePath for bounce, elastic, wave paths or rotation/scale animation\n const startPosition = {\n x: parseFloat(img.dataset.startX!),\n y: parseFloat(img.dataset.startY!)\n };\n const endPosition = {\n x: parseFloat(img.dataset.endX!),\n y: parseFloat(img.dataset.endY!)\n };\n const imageWidth = parseFloat(img.dataset.imageWidth!);\n const imageHeight = parseFloat(img.dataset.imageHeight!);\n const rotation = parseFloat(img.dataset.rotation!);\n const scale = parseFloat(img.dataset.scale!);\n const startRotation = img.dataset.startRotation\n ? parseFloat(img.dataset.startRotation)\n : rotation;\n const startScale = img.dataset.startScale\n ? parseFloat(img.dataset.startScale)\n : scale;\n const timing = this.entryAnimationEngine.getTiming();\n\n animatePath({\n element: img,\n startPosition,\n endPosition,\n pathConfig: this.entryAnimationEngine.getPathConfig(),\n duration: timing.duration,\n imageWidth,\n imageHeight,\n rotation,\n scale,\n rotationConfig: this.entryAnimationEngine.getRotationConfig(),\n startRotation,\n scaleConfig: this.entryAnimationEngine.getScaleConfig(),\n startScale\n });\n } else {\n // Use CSS transition for linear/arc paths without rotation animation\n const finalTransform = img.dataset.finalTransform || '';\n img.style.transform = finalTransform;\n }\n\n // Debug: log final state for first few images\n const imgIndex = parseInt(img.dataset.imageId || '0');\n if (this.fullConfig.config.debug?.enabled && imgIndex < 3) {\n const finalTransform = img.dataset.finalTransform || '';\n console.log(`Image ${imgIndex} final state:`, {\n left: img.style.left,\n top: img.style.top,\n width: img.style.width,\n height: img.style.height,\n computedWidth: img.offsetWidth,\n computedHeight: img.offsetHeight,\n transform: finalTransform,\n pathType: this.entryAnimationEngine.getPathType()\n });\n }\n\n // Register with idle animation engine (starts after entry completes)\n if (this.idleAnimationEngine) {\n const entryDuration = this.entryAnimationEngine.getTiming().duration;\n this.idleAnimationEngine.register(img, imgIndex, this.imageElements.length, entryDuration);\n }\n });\n\n processedCount++;\n };\n\n const startQueueProcessing = () => {\n this.logDebug('Starting queue processing, enabled:', this.fullConfig.animation.queue.enabled);\n\n // If queue is disabled, display all images immediately\n if (!this.fullConfig.animation.queue.enabled) {\n while (this.displayQueue.length > 0) {\n const img = this.displayQueue.shift();\n if (img) {\n displayImage(img);\n }\n }\n return;\n }\n\n // Queue is enabled - stagger images with interval\n // Clear any existing interval before creating new one\n if (this.queueInterval !== null) {\n clearInterval(this.queueInterval);\n }\n this.queueInterval = window.setInterval(() => {\n // Check if this interval is still valid (generation hasn't changed)\n if (currentGeneration !== this.loadGeneration) {\n if (this.queueInterval !== null) {\n clearInterval(this.queueInterval);\n this.queueInterval = null;\n }\n return;\n }\n\n if (this.displayQueue.length > 0) {\n const img = this.displayQueue.shift();\n if (img) {\n displayImage(img);\n }\n }\n\n if (processedCount >= imageUrls.length && this.displayQueue.length === 0) {\n if (this.queueInterval !== null) {\n clearInterval(this.queueInterval);\n this.queueInterval = null;\n }\n }\n }, this.fullConfig.animation.queue.interval);\n };\n\n // Visibility Check\n if ('IntersectionObserver' in window && this.containerEl) {\n const observer = new IntersectionObserver((entries) => {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n startQueueProcessing();\n observer.disconnect();\n }\n });\n }, { threshold: 0.1, rootMargin: '50px' });\n observer.observe(this.containerEl);\n } else {\n startQueueProcessing();\n }\n\n // Debug: Draw center markers if debug.centers is enabled\n if (this.fullConfig.config.debug?.centers && this.containerEl) {\n // Remove any existing debug markers\n this.containerEl.querySelectorAll('.fbn-ic-debug-center').forEach(el => el.remove());\n\n layouts.forEach((layout, index) => {\n const marker = document.createElement('div');\n marker.className = 'fbn-ic-debug-center';\n marker.style.position = 'absolute';\n marker.style.width = '12px';\n marker.style.height = '12px';\n marker.style.borderRadius = '50%';\n marker.style.backgroundColor = 'red';\n marker.style.border = '2px solid yellow';\n marker.style.zIndex = '9999';\n marker.style.pointerEvents = 'none';\n // Center position: layout.x and layout.y now store the center position directly\n const centerX = layout.x;\n const centerY = layout.y;\n marker.style.left = `${centerX - 6}px`; // Offset by half marker size\n marker.style.top = `${centerY - 6}px`;\n marker.title = `Image ${index}: center (${Math.round(centerX)}, ${Math.round(centerY)})`;\n this.containerEl!.appendChild(marker);\n });\n }\n\n // Create elements\n imageUrls.forEach((url, index) => {\n const img = document.createElement('img');\n // NOTE: img.src is set AFTER onload handler to ensure handler catches cached images\n img.referrerPolicy = 'no-referrer';\n img.classList.add('fbn-ic-image');\n if (this.fullConfig.interaction.dragging === false) {\n img.draggable = false;\n }\n img.dataset.imageId = String(index);\n img.dataset.createdFlag = 'true'; // Debug flag\n\n const layout = layouts[index];\n img.style.position = 'absolute';\n img.style.width = 'auto';\n img.style.height = `${imageHeight}px`;\n img.style.left = `${layout.x}px`;\n img.style.top = `${layout.y}px`;\n // Transform will be applied in onload after we know the actual dimensions\n\n if (layout.zIndex) img.style.zIndex = String(layout.zIndex);\n\n // NOTE: Default styling will be applied in onload after image dimensions are known\n // This ensures height-relative clip-path is calculated correctly with proper width\n // Element starts with opacity 0 so it's not visible until onload completes\n applyClassNameToElement(img, this.defaultClassName);\n\n // Hover event handlers\n // Use isInvolved() to prevent hover styles on images that are focused or animating\n img.addEventListener('mouseenter', () => {\n this.hoveredImage = { element: img, layout };\n if (!this.zoomEngine.isInvolved(img)) {\n // Use cached rendered width for consistent clip-path centering (prevents shifting)\n const cachedWidth = (img as any).cachedRenderedWidth;\n applyStylesToElementWithState(img, this.fullConfig.styling?.hover, imageHeight, cachedWidth);\n applyClassNameToElement(img, this.hoverClassName);\n }\n });\n\n img.addEventListener('mouseleave', () => {\n this.hoveredImage = null;\n if (!this.zoomEngine.isInvolved(img)) {\n // Use cached rendered width for consistent clip-path centering (prevents shifting)\n const cachedWidth = (img as any).cachedRenderedWidth;\n applyStylesToElementWithState(img, this.fullConfig.styling?.default, imageHeight, cachedWidth);\n removeClassNameFromElement(img, this.hoverClassName);\n applyClassNameToElement(img, this.defaultClassName);\n }\n });\n\n img.addEventListener('click', (e: MouseEvent) => {\n e.stopPropagation();\n this.handleImageClick(img, layout);\n });\n\n img.style.opacity = '0';\n img.style.transition = this.entryAnimationEngine.getTransitionCSS();\n\n img.onload = () => {\n // Ignore if generation has changed (stale callback from previous load)\n if (currentGeneration !== this.loadGeneration) {\n return;\n }\n\n const aspectRatio = img.naturalWidth / img.naturalHeight;\n const renderedWidth = imageHeight * aspectRatio;\n\n // Debug: mark that onload was called\n img.dataset.onloadCalled = 'true';\n if ((window as any).DEBUG_CLIPPATH) {\n console.log(`[onload #${index}] Called with imageHeight=${imageHeight}, renderedWidth=${renderedWidth}`);\n }\n\n // Set explicit width so transform calculations are accurate\n img.style.width = `${renderedWidth}px`;\n\n // Store rendered width and aspect ratio on element for use in event handlers and focused state\n (img as any).cachedRenderedWidth = renderedWidth;\n (img as any).aspectRatio = aspectRatio;\n\n // Reapply default styling with correct width for height-relative clip-path centering\n // Now we know both height and the rendered width (from aspect ratio)\n applyStylesToElementWithState(img, this.fullConfig.styling?.default, imageHeight, renderedWidth);\n\n // Use EntryAnimationEngine for start position calculation\n const finalPosition = { x: layout.x, y: layout.y };\n const imageSize = { width: renderedWidth, height: imageHeight };\n\n const startPosition = this.entryAnimationEngine.calculateStartPosition(\n finalPosition,\n imageSize,\n containerBounds,\n index,\n imageUrls.length\n );\n\n // Calculate start rotation based on entry rotation config\n const startRotation = this.entryAnimationEngine.calculateStartRotation(layout.rotation);\n\n // Calculate start scale based on entry scale config\n const startScale = this.entryAnimationEngine.calculateStartScale(layout.scale);\n\n const finalTransform = this.entryAnimationEngine.buildFinalTransform(\n layout.rotation,\n layout.scale,\n renderedWidth,\n imageHeight\n );\n const startTransform = this.entryAnimationEngine.buildStartTransform(\n startPosition,\n finalPosition,\n layout.rotation,\n layout.scale,\n renderedWidth,\n imageHeight,\n startRotation,\n startScale\n );\n\n if (this.fullConfig.config.debug?.enabled && index < 3) {\n console.log(`Image ${index}:`, {\n finalPosition,\n imageSize,\n left: layout.x,\n top: layout.y,\n finalTransform,\n renderedWidth,\n renderedHeight: imageHeight\n });\n }\n\n img.style.transform = startTransform;\n img.dataset.finalTransform = finalTransform;\n\n // Store animation data for JS-animated paths (bounce, elastic, wave)\n // or when rotation/scale animation is needed\n const needsJSAnimation = this.entryAnimationEngine.requiresJSAnimation() ||\n this.entryAnimationEngine.requiresJSRotation() ||\n this.entryAnimationEngine.requiresJSScale() ||\n startRotation !== layout.rotation ||\n startScale !== layout.scale;\n\n if (needsJSAnimation) {\n img.dataset.startX = String(startPosition.x);\n img.dataset.startY = String(startPosition.y);\n img.dataset.endX = String(finalPosition.x);\n img.dataset.endY = String(finalPosition.y);\n img.dataset.imageWidth = String(renderedWidth);\n img.dataset.imageHeight = String(imageHeight);\n img.dataset.rotation = String(layout.rotation);\n img.dataset.scale = String(layout.scale);\n img.dataset.startRotation = String(startRotation);\n img.dataset.startScale = String(startScale);\n }\n\n this.displayQueue.push(img);\n };\n\n img.onerror = () => processedCount++;\n\n // Set src AFTER onload handler to ensure it catches cached images\n img.src = url;\n });\n }\n\n private async handleImageClick(imageElement: HTMLImageElement, originalLayout: ImageLayout): Promise<void> {\n if (!this.containerEl) return;\n\n const isFocused = this.zoomEngine.isFocused(imageElement);\n const bounds: ContainerBounds = {\n width: this.containerEl.offsetWidth,\n height: this.containerEl.offsetHeight\n };\n\n if (isFocused) {\n await this.zoomEngine.unfocusImage();\n this.currentFocusIndex = null;\n this.swipeEngine?.disable();\n this.hideCounter();\n this.hideNavButtons();\n this.hideFocusIndicator();\n } else {\n // Pause idle animation immediately before focus animation begins\n this.idleAnimationEngine?.pauseForImage(imageElement);\n\n // Track the focused image index for keyboard navigation\n const imageId = imageElement.dataset.imageId;\n this.currentFocusIndex = imageId !== undefined ? parseInt(imageId, 10) : null;\n this.swipeEngine?.enable();\n this.containerEl?.focus({ preventScroll: true });\n await this.zoomEngine.focusImage(imageElement, bounds, originalLayout);\n if (this.currentFocusIndex !== null) {\n this.updateCounter(this.currentFocusIndex);\n }\n this.showNavButtons();\n this.showFocusIndicator();\n }\n }\n\n /**\n * Clear the image cloud and reset state\n */\n clearImageCloud(): void {\n // Clear queue processing interval to prevent stale images from being added\n if (this.queueInterval !== null) {\n clearInterval(this.queueInterval);\n this.queueInterval = null;\n }\n // Increment generation to invalidate pending image onload handlers\n this.loadGeneration++;\n this.displayQueue = [];\n\n this.hideFocusIndicator();\n\n if (this.containerEl) {\n this.containerEl.querySelectorAll('.fbn-ic-image, .fbn-ic-debug-center').forEach(el => el.remove());\n }\n this.imageElements = [];\n this.imageLayouts = [];\n this.currentFocusIndex = null;\n this.hoveredImage = null;\n this.layoutEngine.reset();\n this.zoomEngine.reset();\n this.idleAnimationEngine?.stopAll();\n this.imagesLoaded = false;\n }\n\n private showLoading(show: boolean): void {\n if (!this.fullConfig.ui.showLoadingSpinner || !this.loadingEl) return;\n if (show) {\n this.loadingEl.classList.remove('fbn-ic-hidden');\n } else {\n this.loadingEl.classList.add('fbn-ic-hidden');\n }\n }\n\n private showError(message: string): void {\n if (!this.errorEl) return;\n this.errorEl.textContent = message;\n this.errorEl.classList.remove('fbn-ic-hidden');\n }\n\n private hideError(): void {\n if (this.errorEl) {\n this.errorEl.classList.add('fbn-ic-hidden');\n }\n }\n\n private updateCounter(index: number): void {\n if (!this.fullConfig.ui.showImageCounter || !this.counterEl) return;\n this.counterEl.textContent = `${index + 1} of ${this.imageElements.length}`;\n this.counterEl.classList.remove('fbn-ic-hidden');\n }\n\n private hideCounter(): void {\n if (this.counterEl) {\n this.counterEl.classList.add('fbn-ic-hidden');\n }\n }\n\n private showFocusIndicator(): void {\n this.containerEl?.classList.add('fbn-ic-has-focus');\n }\n\n private hideFocusIndicator(): void {\n this.containerEl?.classList.remove('fbn-ic-has-focus');\n }\n\n private showNavButtons(): void {\n this.prevButtonEl?.classList.remove('fbn-ic-hidden');\n this.nextButtonEl?.classList.remove('fbn-ic-hidden');\n }\n\n private hideNavButtons(): void {\n this.prevButtonEl?.classList.add('fbn-ic-hidden');\n this.nextButtonEl?.classList.add('fbn-ic-hidden');\n }\n\n /**\n * Destroy the gallery and clean up resources\n */\n destroy(): void {\n this.clearImageCloud();\n // Remove auto-created UI elements\n if (this.loadingElAutoCreated && this.loadingEl) {\n this.loadingEl.remove();\n this.loadingEl = null;\n }\n if (this.errorElAutoCreated && this.errorEl) {\n this.errorEl.remove();\n this.errorEl = null;\n }\n if (this.counterElAutoCreated && this.counterEl) {\n this.counterEl.remove();\n this.counterEl = null;\n }\n if (this.prevButtonElAutoCreated && this.prevButtonEl) {\n this.prevButtonEl.remove();\n this.prevButtonEl = null;\n }\n if (this.nextButtonElAutoCreated && this.nextButtonEl) {\n this.nextButtonEl.remove();\n this.nextButtonEl = null;\n }\n // Remove event listeners\n if (this.resizeTimeout !== null) {\n clearTimeout(this.resizeTimeout);\n }\n this.swipeEngine?.destroy();\n this.idleAnimationEngine?.stopAll();\n this.idleAnimationEngine = null;\n }\n}\n","import {\n forwardRef,\n useEffect,\n useImperativeHandle,\n useRef,\n type CSSProperties,\n} from 'react';\nimport { ImageCloud as ImageCloudCore } from '../ImageCloud';\nimport type { ImageCloudOptions } from '../config/types';\n\nexport type ImageCloudProps = Omit<ImageCloudOptions, 'container'> & {\n className?: string;\n style?: CSSProperties;\n};\n\nexport interface ImageCloudRef {\n instance: ImageCloudCore | null;\n}\n\nexport const ImageCloud = forwardRef<ImageCloudRef, ImageCloudProps>(\n function ImageCloud({ className, style, ...options }, ref) {\n const containerRef = useRef<HTMLDivElement>(null);\n const instanceRef = useRef<ImageCloudCore | null>(null);\n\n useImperativeHandle(ref, () => ({\n get instance() {\n return instanceRef.current;\n },\n }));\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n const cloud = new ImageCloudCore({\n container: containerRef.current,\n ...options,\n });\n instanceRef.current = cloud;\n\n cloud.init().catch((err) => {\n console.error('ImageCloud init failed:', err);\n });\n\n return () => {\n cloud.destroy();\n instanceRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [JSON.stringify(options)]);\n\n return <div ref={containerRef} className={className} style={style} />;\n }\n);\n\n// Re-export core types for convenience\nexport type {\n ImageCloudOptions,\n LayoutAlgorithm,\n LayoutConfig,\n AnimationConfig,\n ImageStylingConfig,\n} from '../config/types';\n"],"names":["SHADOW_PRESETS","BOUNCE_PRESETS","ELASTIC_PRESETS","WAVE_PATH_PRESETS","DEFAULT_PATH_CONFIG","DEFAULT_ENTRY_ROTATION","DEFAULT_ENTRY_SCALE","DEFAULT_STYLING","DEFAULT_RADIAL_CONFIG","DEFAULT_WAVE_CONFIG","DEFAULT_HONEYCOMB_CONFIG","DEFAULT_RESPONSIVE_BREAKPOINTS","DEFAULT_IMAGE_SIZING","DEFAULT_IMAGE_ROTATION","DEFAULT_IMAGE_CONFIG","DEFAULT_SHARED_LOADER_CONFIG","DEFAULT_DEBUG_CONFIG","DEFAULT_IDLE_WIGGLE","DEFAULT_IDLE_PULSE","DEFAULT_IDLE_BLINK","DEFAULT_IDLE_SPIN","DEFAULT_IDLE_CONFIG","DEFAULT_CONFIG","deepMergeStyleState","base","override","merged","deepMergeStyling","defaults","userStyling","mergedDefault","mergedHover","mergedFocused","deepMergeImageConfig","userImage","userVariance","validMin","validMax","userRange","convertLegacyRotationConfig","userConfig","legacyRotation","convertLegacyVarianceConfig","legacyVariance","mergeConfig","combinedImageConfig","loaders","mergedConfig","legacyUi","honeycombClip","resolveBounceConfig","preset","overrides","resolveElasticConfig","resolveWavePathConfig","AnimationEngine","config","params","transforms","x","y","element","from","to","duration","easing","animDuration","animEasing","fromTransform","toTransform","animation","handle","commitStyle","snapshot","currentTransform","allAnimations","anim","transformStr","matrix","scale","rotation","properties","resolve","originalState","ms","lerp","start","end","t","calculateBouncePosition","overshoot","bounces","decayRatio","dx","dy","keyframes","generateBounceKeyframes","progress","segmentStart","segmentEnd","segmentOvershoot","isOvershootPhase","i","segmentT","easeOutQuad","fromProgress","k","currentTime","currentOvershoot","bounceTime","calculateElasticPosition","stiffness","damping","mass","oscillations","omega","zeta","dampedFreq","envelope","oscillation","calculateWavePosition","amplitude","frequency","decay","decayRate","phase","length","perpX","perpY","wavePhase","decayFactor","waveOffset","progressT","easeOutCubic","calculateWobbleRotation","finalRotation","wobbleConfig","wobbleOffset","calculatePopScale","finalScale","popConfig","bounceDecay","undershoot","currentScale","prevTime","prevScale","segmentProgress","easedProgress","animatePath","options","startPosition","endPosition","pathConfig","imageWidth","imageHeight","onComplete","rotationConfig","startRotation","scaleConfig","startScale","pathType","animateRotation","isWobbleMode","needsRotationAnimation","animateScale","isPopMode","startTime","centerOffsetX","centerOffsetY","tick","elapsed","position","translateX","translateY","currentRotation","requiresJSAnimation","LAYOUT_ENTRY_DEFAULTS","EntryAnimationEngine","layoutAlgorithm","finalPosition","imageSize","containerBounds","imageIndex","totalImages","offset","centerX","centerY","distLeft","distRight","distTop","distBottom","minDist","startX","startY","edge","_finalPosition","_imageSize","edges","randomEdge","circularConfig","distribution","radius","radiusConfig","percentage","angle","_imageIndex","centerTranslate","offsetX","offsetY","startConfig","range","spinCount","direction","IdleAnimationEngine","entryDurationMs","index","entryDuration","delay","startDelay","entry","type","cfg","onOpacity","endDeg","fn","result","speed","RandomPlacementLayout","imageConfig","imageCount","layouts","width","height","padding","baseImageSize","rotationMode","minRotation","maxRotation","varianceMin","varianceMax","hasVariance","halfWidth","halfHeight","maxX","maxY","minX","minY","scaledImageSize","layout","min","max","RadialPlacementLayout","scaleDecay","radialConfig","cx","cy","estimatedMaxRings","maxRadius","varianceScale","centerSize","processedCount","currentRing","normalizedRing","ringScale","ringStep","radiusY","radiusX","circumference","estimatedItemWidth","itemsInRing","angleStep","ringOffset","combinedScale","DEFAULT_GRID_CONFIG","OVERFLOW_OFFSET_PATTERN","GridPlacementLayout","gridConfig","availableWidth","availableHeight","columns","rows","hasRowStagger","hasColumnStagger","effectiveColumns","effectiveRows","cellWidth","cellHeight","staggerOffsetX","staggerOffsetY","overlapMultiplier","cellBasedSize","totalGridWidth","totalGridHeight","gridOffsetX","gridOffsetY","cellCount","hasFixedGrid","isOverflowMode","cellStackCount","overflowOffsetPx","col","row","stackLayer","overflowIndex","targetCell","cellCenterX","cellCenterY","patternIndex","pattern","maxJitterX","maxJitterY","itemsInLastRow","lastRowWidth","alignmentOffset","zIndex","_baseImageSize","aspectRatio","GOLDEN_ANGLE","DEFAULT_SPIRAL_CONFIG","SpiralPlacementLayout","spiralConfig","directionMultiplier","theta","normalizedRadius","decayScale","clampedX","clampedY","baseRotation","rotationVariance","tangentAngle","b","psi","tightness","maxTheta","a","maxComputedRadius","DEFAULT_CLUSTER_CONFIG","ClusterPlacementLayout","clusterConfig","clusterCount","clusterCenters","imagesPerCluster","clusterIdx","cluster","imagesInThisCluster","distance","sizeMultiplier","normalizedDistance","configCount","clusterSpacing","countByImages","countBySpace","count","centers","bestCandidate","bestMinDistance","attempt","candidate","minDistance","existing","u","v","value","WavePlacementLayout","waveConfig","phaseShift","synchronization","imagesPerRow","halfImageWidth","endX","horizontalSpacing","minCenterY","maxCenterY","rowSpacing","rowIndex","baseY","imgInRow","waveY","containerWidth","normalizedX","derivative","HEXAGON_REF_HEIGHT","_R","HEXAGON_REF_POINTS","HEXAGON_COL_STEP_RATIO","HEXAGON_ROW_OFFSET_RATIO","getHexTilingParams","hexH","hexCubeToPixel","_cz","originX","originY","colStep","HEX_RING_DIRECTIONS","getHexRingCells","ring","cells","cz","dz","step","HoneycombPlacementLayout","_imageConfig","containerCX","containerCY","spacing","placed","px","py","LayoutEngine","imageId","newConfig","viewportWidth","breakpoints","sizing","responsiveHeight","breakpoint","maxHeight","userBaseHeight","minSize","maxSize","targetCoverage","densityFactor","areaPerImage","calculatedHeight","finalHeight","floor","maxRing","total","COL_STEP_RATIO","HALF_WIDTH_RATIO","maxV","maxH","ZoomState","CLIP_PATH_SHAPES","CLIP_PATH_SHAPES_HEIGHT_RELATIVE","getClipPath","shape","calculateHeightRelativeClipPath","shapeDef","xs","ys","shapeCenterX","shapeCenterY","scaledBBoxWidth","imageCenterX","imageCenterY","horizontalOffset","verticalOffset","scaledX","scaledY","isShadowPreset","resolveShadow","shadow","buildFilterString","filter","parts","ds","buildSingleBorder","style","color","buildStyleProperties","state","styles","baseRadius","baseBorder","topBorder","rightBorder","bottomBorder","leftBorder","filterStr","clipPathValue","isConfig","clipPathInput","applyStylesToElement","applyStylesToElementWithState","resolveClassName","className","applyClassNameToElement","resolved","cls","removeClassNameFromElement","Z_INDEX","ZoomEngine","animationEngine","styling","callback","normalizedPercent","targetHeight","focusHeight","focusWidth","maxWidth","targetX","targetY","fromWidth","fromHeight","toWidth","toHeight","fromTransformStr","toTransformStr","originalZIndex","isToFocused","styleConfig","updateClipPath","currentHeight","currentWidth","fromDimensions","originalWidth","originalHeight","focusDimensions","focusTransform","startTransform","startWidth","startHeight","toState","targetWidth","computed","midWidth","midHeight","pureX","pureY","imageElement","myGeneration","completedOutgoing","incomingFrom","incomingFromDimensions","completedFocusing","incomingUnfocus","outgoingElement","incomingUnfocusElement","newImageElement","animate","centeredTransform","SWIPE_THRESHOLD_PX","SWIPE_VELOCITY_THRESHOLD","SWIPE_MIN_DISTANCE_FOR_VELOCITY","DRAG_DAMPING","SNAP_BACK_DURATION_MS","HORIZONTAL_ANGLE_THRESHOLD_DEG","_SwipeEngine","container","callbacks","e","touch","deltaX","deltaY","angleDeg","dampedOffset","_e","deltaTime","velocity","absDistance","navigated","SwipeEngine","GoogleDriveLoader","source","folderUrl","recursive","urls","patterns","match","folderId","error","imageUrls","query","url","response","validFiles","file","fileUrls","fileUrl","fileId","metadataUrl","metadata","data","subfolders","f","folder","subfolderImages","_filter","html","imageIdPattern","matches","m","id","imageIds","args","StaticImageLoader","validUrls","filename","basePath","files","controller","timeoutId","cleanBase","origin","cleanPath","CompositeLoader","preparePromises","loader","ImageFilter","extensions","extension","FUNCTIONAL_CSS","injectFunctionalStyles","ImageCloud$1","entryConfig","idleConfig","el","img","idx","cachedWidth","entries","shared","childLoaders","inner","uiConfig","ref","spinner","text","nextId","nextElement","prevId","prevElement","newHeight","responsive","sizingResult","currentGeneration","displayImage","timing","finalTransform","imgIndex","startQueueProcessing","observer","marker","renderedWidth","originalLayout","isFocused","bounds","show","message","ImageCloud","forwardRef","containerRef","useRef","instanceRef","useImperativeHandle","useEffect","cloud","ImageCloudCore","err","jsx"],"mappings":"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"}
|