@hdcodedev/snowfall 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -345,6 +345,8 @@ var initializeAccumulation = (accumulationMap, config) => {
345
345
  leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),
346
346
  rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),
347
347
  maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,
348
+ leftMax: existing?.leftSide.length === height ? existing.leftMax : 0,
349
+ rightMax: existing?.rightSide.length === height ? existing.rightMax : 0,
348
350
  borderRadius,
349
351
  curveOffsets: calculateCurveOffsets(width, borderRadius, isBottom),
350
352
  sideGravityMultipliers: calculateGravityMultipliers(height),
@@ -352,9 +354,10 @@ var initializeAccumulation = (accumulationMap, config) => {
352
354
  });
353
355
  });
354
356
  };
355
- var accumulateSide = (sideArray, rectHeight, localY, maxSideHeight, borderRadius, config) => {
357
+ var accumulateSide = (sideArray, rectHeight, localY, maxSideHeight, borderRadius, config, currentMax) => {
356
358
  const spread = 4;
357
359
  const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);
360
+ let newMax = currentMax;
358
361
  for (let dy = -spread; dy <= spread; dy++) {
359
362
  const y = localY + dy;
360
363
  if (y >= 0 && y < sideArray.length) {
@@ -363,9 +366,12 @@ var accumulateSide = (sideArray, rectHeight, localY, maxSideHeight, borderRadius
363
366
  if (borderRadius > 0 && (inTop || inBottom)) continue;
364
367
  const normalizedDist = Math.abs(dy) / spread;
365
368
  const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;
366
- sideArray[y] = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);
369
+ const newHeight = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);
370
+ sideArray[y] = newHeight;
371
+ if (newHeight > newMax) newMax = newHeight;
367
372
  }
368
373
  }
374
+ return newMax;
369
375
  };
370
376
  var updateSnowflakes = (snowflakes, elementRects, config, dt, worldWidth, worldHeight) => {
371
377
  const scrollX = window.scrollX;
@@ -391,13 +397,13 @@ var updateSnowflakes = (snowflakes, elementRects, config, dt, worldWidth, worldH
391
397
  const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);
392
398
  if (flakeViewportX >= rect.left - 5 && flakeViewportX < rect.left + 3) {
393
399
  if (!isCorner) {
394
- accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);
400
+ acc.leftMax = accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.leftMax);
395
401
  landed = true;
396
402
  }
397
403
  }
398
404
  if (!landed && flakeViewportX > rect.right - 3 && flakeViewportX <= rect.right + 5) {
399
405
  if (!isCorner) {
400
- accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);
406
+ acc.rightMax = accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.rightMax);
401
407
  landed = true;
402
408
  }
403
409
  }
@@ -464,10 +470,20 @@ var meltAndSmoothAccumulation = (elementRects, config, dt) => {
464
470
  for (let i = 0; i < acc.heights.length; i++) {
465
471
  if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);
466
472
  }
473
+ let leftMax = 0;
474
+ let rightMax = 0;
467
475
  for (let i = 0; i < acc.leftSide.length; i++) {
468
- if (acc.leftSide[i] > 0) acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);
469
- if (acc.rightSide[i] > 0) acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);
476
+ if (acc.leftSide[i] > 0) {
477
+ acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);
478
+ if (acc.leftSide[i] > leftMax) leftMax = acc.leftSide[i];
479
+ }
480
+ if (acc.rightSide[i] > 0) {
481
+ acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);
482
+ if (acc.rightSide[i] > rightMax) rightMax = acc.rightSide[i];
483
+ }
470
484
  }
485
+ acc.leftMax = leftMax;
486
+ acc.rightMax = rightMax;
471
487
  }
472
488
  };
473
489
 
@@ -570,8 +586,8 @@ var drawSideAccumulations = (ctx, elementRects, scrollX, scrollY) => {
570
586
  for (const item of elementRects) {
571
587
  const { rect, acc } = item;
572
588
  if (acc.maxSideHeight === 0) continue;
573
- const hasLeftSnow = acc.leftSide.some((h) => h > 0.3);
574
- const hasRightSnow = acc.rightSide.some((h) => h > 0.3);
589
+ const hasLeftSnow = acc.leftMax > 0.3;
590
+ const hasRightSnow = acc.rightMax > 0.3;
575
591
  if (!hasLeftSnow && !hasRightSnow) continue;
576
592
  if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, scrollX, scrollY);
577
593
  if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, scrollX, scrollY);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/Snowfall.tsx","../src/SnowfallProvider.tsx","../src/utils/snowfall/constants.ts","../src/utils/snowfall/dom.ts","../src/utils/snowfall/physics.ts","../src/utils/snowfall/draw.ts","../src/DebugPanel.tsx"],"sourcesContent":["'use client';\n\n// Main components\nexport { default as Snowfall } from './Snowfall';\nexport { SnowfallProvider, useSnowfall, DEFAULT_PHYSICS } from './SnowfallProvider';\nexport { default as DebugPanel } from './DebugPanel';\n\n// Types\nexport type { PhysicsConfig, PerformanceMetrics } from './SnowfallProvider';\n","'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { useSnowfall } from './SnowfallProvider';\nimport { Snowflake, SnowAccumulation } from './utils/snowfall/types';\nimport { getElementRects } from './utils/snowfall/dom';\nimport { createSnowflake, initializeAccumulation, meltAndSmoothAccumulation, updateSnowflakes } from './utils/snowfall/physics';\nimport { drawAccumulations, drawSideAccumulations, drawSnowflakes } from './utils/snowfall/draw';\n\nexport default function Snowfall() {\n const { isEnabled, physicsConfig, setMetrics } = useSnowfall();\n const isEnabledRef = useRef(isEnabled);\n const physicsConfigRef = useRef(physicsConfig);\n const setMetricsRef = useRef(setMetrics);\n const [isMounted, setIsMounted] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const snowflakesRef = useRef<Snowflake[]>([]);\n const accumulationRef = useRef<Map<Element, SnowAccumulation>>(new Map());\n const animationIdRef = useRef<number>(0);\n\n // Cache DPR to avoid reading it every frame (only changes on resize)\n // Initialize with safe default for SSR, actual value set in resizeCanvas\n const dprRef = useRef(1);\n\n // Performance metrics tracking\n const fpsFrames = useRef<number[]>([]);\n const metricsRef = useRef({\n scanTime: 0,\n rectUpdateTime: 0,\n frameTime: 0,\n rafGap: 0,\n clearTime: 0,\n physicsTime: 0,\n drawTime: 0,\n });\n\n useEffect(() => {\n requestAnimationFrame(() => setIsMounted(true));\n }, []);\n\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n physicsConfigRef.current = physicsConfig;\n }, [physicsConfig]);\n\n useEffect(() => {\n setMetricsRef.current = setMetrics;\n }, [setMetrics]);\n\n useEffect(() => {\n if (!isMounted) return;\n\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const resizeCanvas = () => {\n if (canvasRef.current) {\n // Use viewport dimensions for fixed canvas\n const newWidth = window.innerWidth;\n const newHeight = window.innerHeight;\n\n // Handle high DPI displays - cache DPR in ref for use in animation loop\n const dpr = window.devicePixelRatio || 1;\n dprRef.current = dpr;\n canvasRef.current.width = newWidth * dpr;\n canvasRef.current.height = newHeight * dpr;\n\n // Set CSS size\n canvasRef.current.style.width = `${newWidth}px`;\n canvasRef.current.style.height = `${newHeight}px`;\n }\n };\n resizeCanvas();\n\n const windowResizeObserver = new ResizeObserver(() => {\n resizeCanvas();\n });\n windowResizeObserver.observe(document.body);\n\n // Separate observer for snow accumulation surfaces\n const surfaceObserver = new ResizeObserver((entries) => {\n // Check if any accumulation element actually changed size\n let needsUpdate = false;\n for (const entry of entries) {\n if (entry.target.isConnected) {\n needsUpdate = true;\n break;\n }\n }\n if (needsUpdate) {\n // Re-run initialization to adapt to new sizes\n // We do NOT want to infinitely recurse, so initAccumulationWrapper\n // must be careful about disconnection if called from here.\n // Actually, we don't need to disconnect/reconnect here if the set of elements hasn't changed,\n // but getAccumulationSurfaces (called by init) might find new ones or drop old ones.\n // For simplicity, we just trigger the scan.\n initAccumulationWrapper();\n }\n });\n\n snowflakesRef.current = [];\n\n const initAccumulationWrapper = () => {\n const scanStart = performance.now();\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n\n // Sync observer with current surfaces\n surfaceObserver.disconnect();\n for (const [el] of accumulationRef.current) {\n surfaceObserver.observe(el);\n }\n\n metricsRef.current.scanTime = performance.now() - scanStart;\n };\n initAccumulationWrapper();\n\n // Delay visibility slightly to ensure smooth fade-in after canvas is ready\n requestAnimationFrame(() => {\n if (isMounted) setIsVisible(true);\n });\n\n let lastTime = 0;\n let lastMetricsUpdate = 0;\n // Holds current frame's element positions\n let elementRects: ReturnType<typeof getElementRects> = [];\n\n const animate = (currentTime: number) => {\n if (lastTime === 0) {\n lastTime = currentTime;\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n\n const deltaTime = Math.min(currentTime - lastTime, 50);\n\n // Always track FPS so we have data when panel opens\n const now = performance.now();\n fpsFrames.current.push(now);\n fpsFrames.current = fpsFrames.current.filter(t => now - t < 1000);\n\n // Track detailed performance metrics\n metricsRef.current.rafGap = currentTime - lastTime;\n\n lastTime = currentTime;\n const dt = deltaTime / 16.67;\n\n const frameStartTime = performance.now();\n\n // Time canvas clear\n const clearStart = performance.now();\n\n // Reset transform to clear the entire viewport-sized canvas\n // We use the cached dpr scaling, so we clear the logical width/height\n const dpr = dprRef.current;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);\n\n // Translate the context to Simulate Scrolling\n // We move the 'world' up by scrollY, so absolute coordinates draw in the correct place relative to viewport\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n ctx.translate(-scrollX, -scrollY);\n\n metricsRef.current.clearTime = performance.now() - clearStart;\n\n const snowflakes = snowflakesRef.current;\n\n // PERFORMANCE: Update element rects EVERY FRAME to track layout changes and animations\n // getBoundingClientRect() is necessary to handle moving/animating elements\n // getBoundingClientRect() is fast enough for the accumulation targets (< 50 elements)\n const rectStart = performance.now();\n elementRects = getElementRects(accumulationRef.current);\n metricsRef.current.rectUpdateTime = performance.now() - rectStart;\n\n // Time physics\n const physicsStart = performance.now();\n meltAndSmoothAccumulation(elementRects, physicsConfigRef.current, dt);\n updateSnowflakes(\n snowflakes,\n elementRects,\n physicsConfigRef.current,\n dt,\n document.documentElement.scrollWidth,\n document.documentElement.scrollHeight\n );\n metricsRef.current.physicsTime = performance.now() - physicsStart;\n\n // Draw Snowflakes (batched for performance)\n const drawStart = performance.now();\n drawSnowflakes(ctx, snowflakes);\n\n // Spawn new snowflakes with adaptive rate based on performance\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const currentFps = fpsFrames.current.length;\n\n // Adaptive spawn rate: reduce load when FPS is low to prevent death spirals\n // Low FPS (<40): 20% spawn rate | Normal FPS: 100% spawn rate\n const shouldSpawn = currentFps >= 40 || Math.random() < 0.2;\n\n if (shouldSpawn) {\n const isBackground = Math.random() < 0.4;\n snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));\n }\n }\n\n // Draw Accumulation\n // We draw accumulations in World Space (by passing scroll offset to draw functions)\n // This aligns perfectly with the translated context and world-space snowflakes.\n\n // Viewport culling: Filter to only visible elements before drawing\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const visibleRects = elementRects.filter(({ rect }) =>\n rect.right >= 0 && rect.left <= viewportWidth &&\n rect.bottom >= 0 && rect.top <= viewportHeight\n );\n\n // Only call draw functions if there are visible elements\n if (visibleRects.length > 0) {\n drawAccumulations(ctx, visibleRects, scrollX, scrollY);\n drawSideAccumulations(ctx, visibleRects, scrollX, scrollY);\n }\n\n metricsRef.current.drawTime = performance.now() - drawStart;\n metricsRef.current.frameTime = performance.now() - frameStartTime;\n\n // Update metrics every 500ms\n if (currentTime - lastMetricsUpdate > 500) {\n setMetricsRef.current({\n fps: fpsFrames.current.length,\n frameTime: metricsRef.current.frameTime,\n scanTime: metricsRef.current.scanTime,\n rectUpdateTime: metricsRef.current.rectUpdateTime,\n surfaceCount: accumulationRef.current.size,\n flakeCount: snowflakes.length,\n maxFlakes: physicsConfigRef.current.MAX_FLAKES,\n rafGap: metricsRef.current.rafGap,\n clearTime: metricsRef.current.clearTime,\n physicsTime: metricsRef.current.physicsTime,\n drawTime: metricsRef.current.drawTime,\n });\n lastMetricsUpdate = currentTime;\n }\n\n animationIdRef.current = requestAnimationFrame(animate);\n };\n\n animationIdRef.current = requestAnimationFrame(animate);\n\n const handleResize = () => {\n resizeCanvas();\n accumulationRef.current.clear();\n initAccumulationWrapper();\n };\n\n window.addEventListener('resize', handleResize);\n\n // Periodic surface scan every 5 seconds to detect DOM changes\n const checkInterval = setInterval(initAccumulationWrapper, 5000);\n\n return () => {\n cancelAnimationFrame(animationIdRef.current);\n window.removeEventListener('resize', handleResize);\n clearInterval(checkInterval);\n windowResizeObserver.disconnect();\n surfaceObserver.disconnect();\n };\n }, [isMounted]);\n\n if (!isMounted) return null;\n\n return (\n <>\n <canvas\n ref={canvasRef}\n style={{\n position: 'fixed', // FIXED position to eliminate scroll jitter\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n willChange: 'transform',\n }}\n aria-hidden=\"true\"\n />\n\n </>\n );\n}\n","'use client';\n\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n\nexport interface PhysicsConfig {\n MAX_FLAKES: number;\n MELT_SPEED: number;\n WIND_STRENGTH: number;\n ACCUMULATION: {\n SIDE_RATE: number;\n TOP_RATE: number;\n BOTTOM_RATE: number;\n };\n MAX_DEPTH: {\n TOP: number;\n BOTTOM: number;\n SIDE: number;\n };\n FLAKE_SIZE: {\n MIN: number;\n MAX: number;\n };\n MAX_SURFACES: number;\n}\n\nexport const DEFAULT_PHYSICS: PhysicsConfig = {\n MAX_FLAKES: 1000,\n MELT_SPEED: 0.00001,\n WIND_STRENGTH: 1.5,\n ACCUMULATION: {\n SIDE_RATE: 1,\n TOP_RATE: 5,\n BOTTOM_RATE: 5.0,\n },\n MAX_DEPTH: {\n TOP: 100,\n BOTTOM: 50,\n SIDE: 20,\n },\n FLAKE_SIZE: {\n MIN: 0.5,\n MAX: 1.6,\n },\n MAX_SURFACES: 15\n};\n\nexport interface PerformanceMetrics {\n fps: number;\n frameTime: number;\n scanTime: number;\n rectUpdateTime: number;\n surfaceCount: number;\n flakeCount: number;\n maxFlakes: number;\n // Detailed metrics\n rafGap: number; // Time between requestAnimationFrame calls\n clearTime: number; // Time to clear canvas\n physicsTime: number; // Time for physics updates\n drawTime: number; // Time to draw snowflakes and accumulation\n}\n\ninterface SnowfallContextType {\n isEnabled: boolean;\n toggleSnow: () => void;\n physicsConfig: PhysicsConfig;\n updatePhysicsConfig: (config: Partial<PhysicsConfig>) => void;\n resetPhysics: () => void;\n debugMode: boolean;\n toggleDebug: () => void;\n metrics: PerformanceMetrics | null;\n setMetrics: (metrics: PerformanceMetrics) => void;\n}\n\nconst SnowfallContext = createContext<SnowfallContextType | undefined>(undefined);\n\nexport function SnowfallProvider({ children, initialDebug = false }: { children: ReactNode; initialDebug?: boolean }) {\n const [isEnabled, setIsEnabled] = useState(true);\n const [physicsConfig, setPhysicsConfig] = useState<PhysicsConfig>(DEFAULT_PHYSICS);\n const [debugMode, setDebugMode] = useState(initialDebug);\n const [metrics, setMetrics] = useState<PerformanceMetrics | null>(null);\n\n const toggleSnow = () => {\n setIsEnabled((prev) => !prev);\n };\n\n const toggleDebug = () => {\n setDebugMode((prev) => !prev);\n };\n\n const updatePhysicsConfig = (config: Partial<PhysicsConfig>) => {\n setPhysicsConfig((prev) => ({\n ...prev,\n ...config,\n ACCUMULATION: {\n ...prev.ACCUMULATION,\n ...(config.ACCUMULATION || {}),\n },\n MAX_DEPTH: {\n ...prev.MAX_DEPTH,\n ...(config.MAX_DEPTH || {}),\n },\n FLAKE_SIZE: {\n ...prev.FLAKE_SIZE,\n ...(config.FLAKE_SIZE || {}),\n },\n }));\n };\n\n const resetPhysics = () => {\n setPhysicsConfig(DEFAULT_PHYSICS);\n };\n\n return (\n <SnowfallContext.Provider value={{\n isEnabled,\n toggleSnow,\n physicsConfig,\n updatePhysicsConfig,\n resetPhysics,\n debugMode,\n toggleDebug,\n metrics,\n setMetrics,\n }}>\n {children}\n </SnowfallContext.Provider>\n );\n}\n\nexport function useSnowfall() {\n const context = useContext(SnowfallContext);\n if (context === undefined) {\n throw new Error('useSnowfall must be used within a SnowfallProvider');\n }\n return context;\n}\n","export const ATTR_SNOWFALL = 'data-snowfall';\n\nexport const VAL_IGNORE = 'ignore';\nexport const VAL_TOP = 'top';\nexport const VAL_BOTTOM = 'bottom';\n\nexport const TAG_HEADER = 'header';\nexport const TAG_FOOTER = 'footer';\n\nexport const ROLE_BANNER = 'banner';\nexport const ROLE_CONTENTINFO = 'contentinfo';\n\n// Mathematical constants\nexport const TAU = Math.PI * 2; // Full circle in radians\n","import { SnowAccumulation, ElementSurface, SnowfallSurface } from './types';\nimport {\n ATTR_SNOWFALL, VAL_IGNORE, VAL_TOP, VAL_BOTTOM,\n TAG_HEADER, TAG_FOOTER, ROLE_BANNER, ROLE_CONTENTINFO\n} from './constants';\n\n// Headers: snow accumulates on BOTTOM edge\n// Footers: default to TOP surface (snow piles on top)\n// Use data-snowfall attributes to override this behavior\nconst BOTTOM_TAGS = [TAG_HEADER];\nconst BOTTOM_ROLES = [ROLE_BANNER];\n\nconst AUTO_DETECT_TAGS = [TAG_HEADER, TAG_FOOTER, 'article', 'section', 'aside', 'nav'];\nconst AUTO_DETECT_ROLES = [`[role=\"${ROLE_BANNER}\"]`, `[role=\"${ROLE_CONTENTINFO}\"]`, '[role=\"main\"]'];\nconst AUTO_DETECT_CLASSES = [\n '.card', '[class*=\"card\"]', '[class*=\"Card\"]',\n '[class*=\"bg-\"]', '[class*=\"shadow-\"]', '[class*=\"rounded-\"]'\n];\n\n// Helper to get element type (Top vs Bottom surface) based on tags/roles\n// This is used to determine if snow should sit ON TOP or hang from the BOTTOM.\n\nexport const getElementType = (el: Element): SnowfallSurface => {\n const tagName = el.tagName.toLowerCase();\n if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;\n\n const role = el.getAttribute('role');\n if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;\n\n // Default: snow accumulates on top of elements (natural physics)\n return VAL_TOP;\n};\n\nconst shouldAccumulate = (el: Element, precomputedStyle?: CSSStyleDeclaration): boolean => {\n if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;\n // Explicit opt-in\n if (el.hasAttribute(ATTR_SNOWFALL)) return true;\n\n const styles = precomputedStyle || window.getComputedStyle(el);\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n if (!isVisible) return false;\n\n // Check for visual prominence\n const bgColor = styles.backgroundColor;\n const hasBackground = bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent';\n const hasBorder = (parseFloat(styles.borderWidth) > 0 && styles.borderColor !== 'transparent' && styles.borderColor !== 'rgba(0, 0, 0, 0)') && styles.borderStyle !== 'none';\n const hasBoxShadow = styles.boxShadow !== 'none';\n const hasFilter = styles.filter !== 'none' && styles.filter.includes('drop-shadow');\n const hasBackdropFilter = styles.backdropFilter !== 'none';\n\n return hasBackground || hasBorder || hasBoxShadow || hasFilter || hasBackdropFilter;\n};\n\nexport const getAccumulationSurfaces = (maxSurfaces: number = 5): { el: Element; type: SnowfallSurface }[] => {\n // No explicit clearing needed as we don't cache styles persistently anymore.\n const surfaces: { el: Element; type: SnowfallSurface }[] = [];\n const seen = new Set<Element>();\n\n const candidates = document.querySelectorAll(\n [\n `[${ATTR_SNOWFALL}]`,\n ...AUTO_DETECT_TAGS,\n ...AUTO_DETECT_ROLES,\n ...AUTO_DETECT_CLASSES\n ].join(', ')\n );\n\n for (const el of candidates) {\n if (surfaces.length >= maxSurfaces) break;\n if (seen.has(el)) continue;\n\n // Manual override check first\n const manualOverride = el.getAttribute(ATTR_SNOWFALL);\n if (manualOverride === VAL_IGNORE) continue;\n\n // If manually opted in, skip some heuristic checks but keep basic visibility/size sanity\n const isManuallyIncluded = manualOverride !== null;\n const styles = window.getComputedStyle(el);\n\n // Visibility Check: Must be visible and opaque enough\n const isVisible = styles.display !== 'none' && styles.visibility !== 'hidden' && parseFloat(styles.opacity) > 0.1;\n\n if (!isVisible && !isManuallyIncluded) continue;\n\n const rect = el.getBoundingClientRect();\n const hasSize = rect.width >= 100 && rect.height >= 50;\n if (!hasSize && !isManuallyIncluded) continue;\n\n const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;\n const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());\n const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute('role') || '');\n const isBottomSurface = isBottomTag || isBottomRole || manualOverride === VAL_BOTTOM;\n\n if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) continue;\n\n if (shouldAccumulate(el, styles)) {\n let type: SnowfallSurface = getElementType(el);\n if (manualOverride === VAL_BOTTOM) type = VAL_BOTTOM;\n else if (manualOverride === VAL_TOP) type = VAL_TOP;\n\n surfaces.push({ el, type });\n seen.add(el);\n }\n }\n\n return surfaces;\n};\n\nexport const getElementRects = (accumulationMap: Map<Element, SnowAccumulation>): ElementSurface[] => {\n const elementRects: ElementSurface[] = [];\n for (const [el, acc] of accumulationMap.entries()) {\n if (!el.isConnected) continue;\n // PURE VIEWPORT RECT. No scroll math.\n const rect = el.getBoundingClientRect();\n elementRects.push({ el, rect, acc });\n }\n return elementRects;\n};\n","import { PhysicsConfig } from '../../SnowfallProvider';\nimport { Snowflake, SnowAccumulation, ElementSurface } from './types';\nimport { getAccumulationSurfaces } from './dom';\nimport { VAL_BOTTOM, TAU } from './constants';\n\n// Opacity buckets for batched rendering (reduce globalAlpha state changes)\nconst OPACITY_BUCKETS = [0.3, 0.5, 0.7, 0.9];\n\n/**\n * Quantize opacity to nearest bucket for efficient batching during draw.\n * This eliminates runtime bucketing overhead by snapping at creation time.\n */\nconst quantizeOpacity = (opacity: number): number => {\n return OPACITY_BUCKETS.reduce((prev, curr) =>\n Math.abs(curr - opacity) < Math.abs(prev - opacity) ? curr : prev\n );\n};\n\nexport const createSnowflake = (\n worldWidth: number,\n config: PhysicsConfig,\n isBackground = false\n): Snowflake => {\n // Two random calls per flake:\n // 1) horizontal position\n // 2) DNA seed for all other traits\n const x = Math.random() * worldWidth;\n const dna = Math.random();\n\n // Pseudo-random trait extraction from DNA\n const noise = {\n speed: (dna * 13) % 1,\n wind: (dna * 7) % 1,\n wobblePhase: (dna * 23) % 1,\n wobbleSpeed: (dna * 5) % 1\n };\n\n const { MIN, MAX } = config.FLAKE_SIZE;\n const sizeRatio = dna;\n\n // Background vs foreground tuning\n const profile = isBackground\n ? {\n sizeMin: MIN * 0.6,\n sizeRange: (MAX - MIN) * 0.4,\n speedBase: 0.2,\n speedScale: 0.3,\n noiseSpeedScale: 0.2,\n windScale: config.WIND_STRENGTH * 0.625,\n opacityBase: 0.2,\n opacityScale: 0.2,\n wobbleBase: 0.005,\n wobbleScale: 0.015\n }\n : {\n sizeMin: MIN,\n sizeRange: MAX - MIN,\n speedBase: 0.5,\n speedScale: 0.5,\n noiseSpeedScale: 0.3,\n windScale: config.WIND_STRENGTH,\n opacityBase: 0.5,\n opacityScale: 0.3,\n wobbleBase: 0.01,\n wobbleScale: 0.02\n };\n\n const radius = profile.sizeMin + sizeRatio * profile.sizeRange;\n const glowRadius = radius * 1.5;\n\n // Calculate opacity and quantize to bucket for efficient batched rendering\n const rawOpacity = profile.opacityBase + sizeRatio * profile.opacityScale;\n const opacity = quantizeOpacity(rawOpacity);\n\n // Pre-calculate glow opacity (also quantized for batching)\n const rawGlowOpacity = opacity * 0.2;\n const glowOpacity = quantizeOpacity(rawGlowOpacity);\n\n return {\n x: x,\n y: window.scrollY - 5,\n radius: radius,\n glowRadius: glowRadius,\n speed:\n radius * profile.speedScale +\n noise.speed * profile.noiseSpeedScale +\n profile.speedBase,\n wind: (noise.wind - 0.5) * profile.windScale,\n opacity: opacity,\n glowOpacity: glowOpacity,\n wobble: noise.wobblePhase * TAU,\n wobbleSpeed: noise.wobbleSpeed * profile.wobbleScale + profile.wobbleBase,\n sizeRatio: sizeRatio,\n isBackground: isBackground\n };\n};\n\n/**\n * Initialize max heights array for snow accumulation with edge tapering and smoothing.\n * Exported for benchmarking.\n */\nexport const initializeMaxHeights = (\n width: number,\n baseMax: number,\n borderRadius: number,\n isBottom: boolean = false\n): number[] => {\n let maxHeights = new Array(width);\n for (let i = 0; i < width; i++) {\n let edgeFactor = 1.0;\n if (!isBottom && borderRadius > 0) {\n if (i < borderRadius) {\n edgeFactor = Math.pow(i / borderRadius, 1.2);\n } else if (i > width - borderRadius) {\n edgeFactor = Math.pow((width - i) / borderRadius, 1.2);\n }\n }\n maxHeights[i] = baseMax * edgeFactor * (0.85 + Math.random() * 0.15);\n }\n\n const smoothPasses = 4;\n for (let p = 0; p < smoothPasses; p++) {\n const smoothed = [...maxHeights];\n for (let i = 1; i < width - 1; i++) {\n smoothed[i] = (maxHeights[i - 1] + maxHeights[i] + maxHeights[i + 1]) / 3;\n }\n maxHeights = smoothed;\n }\n\n return maxHeights;\n};\n\nconst calculateCurveOffsets = (width: number, borderRadius: number, isBottom: boolean): number[] => {\n const offsets = new Array(width).fill(0);\n if (borderRadius <= 0 || isBottom) return offsets;\n\n for (let x = 0; x < width; x++) {\n let offset = 0;\n if (x < borderRadius) {\n const dist = borderRadius - x;\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n } else if (x > width - borderRadius) {\n const dist = x - (width - borderRadius);\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n }\n offsets[x] = offset;\n }\n return offsets;\n};\n\nconst calculateGravityMultipliers = (height: number): number[] => {\n const multipliers = new Array(height);\n for (let i = 0; i < height; i++) {\n const ratio = i / height;\n multipliers[i] = Math.sqrt(ratio);\n }\n return multipliers;\n};\n\nexport const initializeAccumulation = (\n accumulationMap: Map<Element, SnowAccumulation>,\n config: PhysicsConfig\n) => {\n // Scan DOM for new valid surfaces\n const elements = getAccumulationSurfaces(config.MAX_SURFACES);\n // Prune disconnected elements\n for (const [el] of accumulationMap.entries()) {\n if (!el.isConnected) {\n accumulationMap.delete(el);\n }\n }\n\n elements.forEach(({ el, type }) => {\n const existing = accumulationMap.get(el);\n const rect = el.getBoundingClientRect();\n const width = Math.ceil(rect.width);\n const isBottom = type === VAL_BOTTOM;\n\n if (existing && existing.heights.length === width) {\n existing.type = type;\n if (existing.borderRadius !== undefined) {\n const styleBuffer = window.getComputedStyle(el);\n existing.borderRadius = parseFloat(styleBuffer.borderTopLeftRadius) || 0;\n existing.curveOffsets = calculateCurveOffsets(width, existing.borderRadius, isBottom);\n // Initialize gravity multipliers if height matches but they're missing\n if (existing.leftSide.length === Math.ceil(rect.height) && !existing.sideGravityMultipliers) {\n existing.sideGravityMultipliers = calculateGravityMultipliers(existing.leftSide.length);\n }\n }\n return;\n }\n\n const height = Math.ceil(rect.height);\n const baseMax = isBottom ? config.MAX_DEPTH.BOTTOM : config.MAX_DEPTH.TOP;\n\n const styles = window.getComputedStyle(el);\n const borderRadius = parseFloat(styles.borderTopLeftRadius) || 0;\n\n const maxHeights = initializeMaxHeights(width, baseMax, borderRadius, isBottom);\n\n accumulationMap.set(el, {\n heights: existing?.heights.length === width ? existing.heights : new Array(width).fill(0),\n maxHeights,\n leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),\n rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),\n maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,\n borderRadius,\n curveOffsets: calculateCurveOffsets(width, borderRadius, isBottom),\n sideGravityMultipliers: calculateGravityMultipliers(height),\n type\n });\n });\n};\n\nexport const accumulateSide = (\n sideArray: number[],\n rectHeight: number,\n localY: number,\n maxSideHeight: number,\n borderRadius: number,\n config: PhysicsConfig\n) => {\n const spread = 4;\n const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);\n\n for (let dy = -spread; dy <= spread; dy++) {\n const y = localY + dy;\n if (y >= 0 && y < sideArray.length) {\n const inTop = y < borderRadius;\n const inBottom = y > rectHeight - borderRadius;\n if (borderRadius > 0 && (inTop || inBottom)) continue;\n\n const normalizedDist = Math.abs(dy) / spread;\n const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;\n sideArray[y] = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);\n }\n }\n};\n\nexport const updateSnowflakes = (\n snowflakes: Snowflake[],\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number,\n worldWidth: number,\n worldHeight: number\n) => {\n // Scroll used for World -> Viewport mapping for collision\n // Flakes are in World Space.\n // DOM Rects are in Viewport Space (relative to the window).\n // To check collision, we subtract scrollX/Y from Flake coordinates to get their Viewport position.\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (let i = snowflakes.length - 1; i >= 0; i--) {\n const flake = snowflakes[i];\n\n flake.wobble += flake.wobbleSpeed * dt;\n flake.x += (flake.wind + Math.sin(flake.wobble) * 0.5) * dt;\n flake.y += (flake.speed + Math.cos(flake.wobble * 0.5) * 0.1) * dt;\n\n let landed = false;\n\n for (const item of elementRects) {\n const { rect, acc } = item;\n const isBottom = acc.type === VAL_BOTTOM;\n\n // Flake is World Space. Rect is Viewport Space.\n // Map Flake to Viewport Relative for collision check vs Rect\n const flakeViewportX = flake.x - scrollX;\n const flakeViewportY = flake.y - scrollY;\n\n // Simple Collision Check\n const isInVerticalBounds = flakeViewportY >= rect.top && flakeViewportY <= rect.bottom;\n\n // Side collisions\n // Check if flake hits the left or right edge of the element\n if (!landed && acc.maxSideHeight > 0 && !isBottom) {\n if (isInVerticalBounds) {\n const localY = Math.floor(flakeViewportY - rect.top);\n const borderRadius = acc.borderRadius;\n\n const isInTopCorner = localY < borderRadius;\n const isInBottomCorner = localY > rect.height - borderRadius;\n const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);\n\n if (flakeViewportX >= rect.left - 5 && flakeViewportX < rect.left + 3) {\n if (!isCorner) {\n accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);\n landed = true;\n }\n }\n\n if (!landed && flakeViewportX > rect.right - 3 && flakeViewportX <= rect.right + 5) {\n if (!isCorner) {\n accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);\n landed = true;\n }\n }\n\n if (landed) break;\n }\n }\n\n // Top/Bottom accumulation\n // Check if flake hits the primary horizontal surface\n if (flakeViewportX >= rect.left && flakeViewportX <= rect.right) {\n const localX = Math.floor(flakeViewportX - rect.left);\n const currentHeight = acc.heights[localX] || 0;\n const maxHeight = acc.maxHeights[localX] || 5;\n\n const surfaceY = isBottom ? rect.bottom - currentHeight : rect.top - currentHeight;\n\n if (flakeViewportY >= surfaceY && flakeViewportY < surfaceY + 10 && currentHeight < maxHeight) {\n const shouldAccumulate = isBottom ? Math.random() < 0.15 : true;\n\n if (shouldAccumulate) {\n const baseSpread = Math.ceil(flake.radius);\n const spread = baseSpread + Math.floor(Math.random() * 2);\n const accumRate = isBottom ? config.ACCUMULATION.BOTTOM_RATE : config.ACCUMULATION.TOP_RATE;\n const centerOffset = Math.floor(Math.random() * 3) - 1;\n\n for (let dx = -spread; dx <= spread; dx++) {\n if (Math.random() < 0.15) continue;\n const idx = localX + dx + centerOffset;\n if (idx >= 0 && idx < acc.heights.length) {\n const dist = Math.abs(dx);\n const pixelMax = acc.maxHeights[idx] || 5;\n\n const normDist = dist / spread;\n const falloff = (Math.cos(normDist * Math.PI) + 1) / 2;\n const baseAdd = 0.3 * falloff;\n\n const randomFactor = 0.8 + Math.random() * 0.4;\n const addHeight = baseAdd * randomFactor * accumRate;\n\n if (acc.heights[idx] < pixelMax && addHeight > 0) {\n acc.heights[idx] = Math.min(pixelMax, acc.heights[idx] + addHeight);\n }\n }\n }\n\n if (isBottom) {\n landed = true;\n break;\n }\n }\n\n if (!isBottom) {\n landed = true;\n break;\n }\n }\n }\n }\n\n if (landed || flake.y > worldHeight + 10 || flake.x < -20 || flake.x > worldWidth + 20) {\n snowflakes.splice(i, 1);\n }\n }\n};\n\nexport const meltAndSmoothAccumulation = (\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number\n) => {\n for (const { acc } of elementRects) {\n const meltRate = config.MELT_SPEED * dt;\n const len = acc.heights.length;\n\n // Smooth\n if (len > 2) {\n for (let i = 1; i < len - 1; i++) {\n if (acc.heights[i] > 0.05) {\n const avg = (acc.heights[i - 1] + acc.heights[i + 1]) / 2;\n acc.heights[i] = acc.heights[i] * 0.99 + avg * 0.01;\n }\n }\n }\n\n // Melt\n for (let i = 0; i < acc.heights.length; i++) {\n if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);\n }\n for (let i = 0; i < acc.leftSide.length; i++) {\n if (acc.leftSide[i] > 0) acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);\n if (acc.rightSide[i] > 0) acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);\n }\n }\n};\n","import { Snowflake, ElementSurface } from './types';\nimport { VAL_BOTTOM, TAU } from './constants';\n\nconst ACC_FILL_STYLE = 'rgba(255, 255, 255, 0.95)';\nconst ACC_SHADOW_COLOR = 'rgba(200, 230, 255, 0.6)';\n\n\n/**\n * Single-pass snowflake rendering for optimal cache locality.\n * Flakes are tracked in World Space.\n * The Canvas Context is translated by (-scrollX, -scrollY), effectively viewing the World.\n * \n * PERFORMANCE: All glow/core values are pre-calculated at creation time (physics.ts),\n * enabling a simple single-pass render with excellent cache performance.\n */\nexport const drawSnowflakes = (ctx: CanvasRenderingContext2D, flakes: Snowflake[]) => {\n if (flakes.length === 0) return;\n\n ctx.fillStyle = '#FFFFFF';\n\n // Single pass: Draw glow (behind) then core (front) for each flake\n for (const flake of flakes) {\n // Draw glow effect first (behind the core) - skip for background flakes\n if (!flake.isBackground) {\n ctx.globalAlpha = flake.glowOpacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.glowRadius, 0, TAU);\n ctx.fill();\n }\n\n // Draw core on top\n ctx.globalAlpha = flake.opacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius, 0, TAU);\n ctx.fill();\n }\n\n // Reset alpha once at the end\n ctx.globalAlpha = 1.0;\n};\n\nexport const drawAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 4;\n ctx.shadowOffsetY = -1;\n // Explicitly reset alpha as we modified it in drawSnowflake\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n // Iterate over all accumulation surfaces to build the single path\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (!acc.heights.some(h => h > 0.1)) continue;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\n\n // Precompute world-space coordinates (hoist additions out of loops)\n const worldLeft = rect.left + scrollX;\n const worldBaseY = baseY + scrollY;\n\n let first = true;\n const step = 2;\n const len = acc.heights.length;\n\n // Draw the uneven top surface of the snow\n for (let x = 0; x < len; x += step) {\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n if (first) {\n ctx.moveTo(px, py);\n first = false;\n } else {\n ctx.lineTo(px, py);\n }\n }\n\n if ((len - 1) % step !== 0) {\n const x = len - 1;\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n // Draw the bottom edge (aligned with element border) to close the shape\n for (let x = len - 1; x >= 0; x -= step) {\n const px = worldLeft + x;\n const py = worldBaseY + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = worldLeft + startX;\n const startPy = worldBaseY + (acc.curveOffsets[startX] || 0);\n ctx.lineTo(startPx, startPy);\n }\n\n // Fill all accumulations in one pass to batch shadow rendering\n ctx.fill();\n\n ctx.shadowBlur = 0;\n ctx.shadowOffsetY = 0;\n};\n\nexport const drawSideAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 3;\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n const drawSide = (sideArray: number[], isLeft: boolean, multipliers: number[], rect: DOMRect, dx: number, dy: number) => {\n const baseX = isLeft ? rect.left : rect.right;\n\n // Precompute world-space coordinates\n const worldBaseX = baseX + dx;\n const worldTop = rect.top + dy;\n const worldBottom = rect.bottom + dy;\n\n ctx.moveTo(worldBaseX, worldTop);\n\n // Draw the uneven side profile\n for (let y = 0; y < sideArray.length; y += 2) {\n const width = sideArray[y] || 0;\n const nextY = Math.min(y + 2, sideArray.length - 1);\n const nextWidth = sideArray[nextY] || 0;\n\n const gravityMultiplier = multipliers[y] || 0;\n\n const py = worldTop + y;\n const px = (isLeft ? worldBaseX - (width * gravityMultiplier) : worldBaseX + (width * gravityMultiplier));\n const ny = worldTop + nextY;\n const nGravityMultiplier = multipliers[nextY] || 0;\n const nx = (isLeft ? worldBaseX - (nextWidth * nGravityMultiplier) : worldBaseX + (nextWidth * nGravityMultiplier));\n\n ctx.lineTo(px, py);\n ctx.lineTo(nx, ny);\n }\n\n // Close at the bottom\n ctx.lineTo(worldBaseX, worldBottom);\n };\n\n // Scan elements and append their side snow profiles to the current path for batched rendering\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (acc.maxSideHeight === 0) continue;\n\n const hasLeftSnow = acc.leftSide.some(h => h > 0.3);\n const hasRightSnow = acc.rightSide.some(h => h > 0.3);\n\n if (!hasLeftSnow && !hasRightSnow) continue;\n\n if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n }\n\n ctx.fill();\n\n ctx.shadowBlur = 0;\n};\n","'use client';\n\nimport { useSnowfall } from './SnowfallProvider';\nimport { useEffect, useState } from 'react';\n\nexport default function DebugPanel({ defaultOpen = true }: { defaultOpen?: boolean }) {\n const { debugMode, toggleDebug, metrics } = useSnowfall();\n const [isMinimized, setIsMinimized] = useState(!defaultOpen);\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.shiftKey && e.key === 'D') {\n toggleDebug();\n }\n };\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleDebug]);\n\n const copyToClipboard = () => {\n if (metrics) {\n const data = {\n ...metrics,\n timestamp: new Date().toISOString(),\n userAgent: navigator.userAgent,\n canvasSize: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n };\n navigator.clipboard.writeText(JSON.stringify(data, null, 2));\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n };\n\n if (!debugMode) return null;\n\n return (\n <div\n data-snowfall=\"top\"\n style={{\n position: 'fixed',\n bottom: '80px',\n left: '24px',\n backgroundColor: 'rgba(15, 23, 42, 0.75)',\n backdropFilter: 'blur(16px)',\n WebkitBackdropFilter: 'blur(16px)',\n color: '#e2e8f0',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',\n fontSize: '12px',\n padding: isMinimized ? '12px' : '20px',\n borderRadius: '16px',\n zIndex: 10000,\n minWidth: isMinimized ? 'auto' : '300px',\n maxWidth: '100%',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.2), 0 8px 10px -6px rgba(0, 0, 0, 0.2)',\n transition: 'all 0.2s ease',\n }}>\n <div style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: isMinimized ? 0 : '16px',\n gap: '16px'\n }}>\n <div style={{ fontWeight: '600', color: '#fff', display: 'flex', alignItems: 'center', gap: '8px' }}>\n <span style={{ fontSize: '14px' }}>❄️</span>\n <span style={{\n background: 'linear-gradient(to right, #60a5fa, #22d3ee)',\n WebkitBackgroundClip: 'text',\n WebkitTextFillColor: 'transparent',\n fontWeight: '700'\n }}>DEBUG</span>\n </div>\n <div style={{ display: 'flex', gap: '8px' }}>\n <button\n onClick={() => setIsMinimized(!isMinimized)}\n style={{\n background: 'rgba(255, 255, 255, 0.1)',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n color: '#fff',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '10px',\n }}\n >\n {isMinimized ? '▲' : '▼'}\n </button>\n <button\n onClick={toggleDebug}\n style={{\n background: 'rgba(239, 68, 68, 0.15)',\n border: '1px solid rgba(239, 68, 68, 0.2)',\n color: '#f87171',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '12px',\n }}\n >\n ✕\n </button>\n </div>\n </div>\n\n {!isMinimized && metrics && (\n <>\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Performance\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>FPS</span>\n <span style={{ fontWeight: 'bold', color: metrics.fps < 30 ? '#f87171' : metrics.fps < 50 ? '#facc15' : '#4ade80' }}>\n {metrics.fps.toFixed(1)}\n </span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Frame Time</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.frameTime.toFixed(2)}ms</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: metrics.rafGap && metrics.rafGap > 20 ? '#fbbf24' : 'inherit' }}>rAF Gap</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.rafGap?.toFixed(1) || 0}ms</span>\n </div>\n </div>\n\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Detailed Timings\n </div>\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '4px 12px' }}>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Clear</span> <span style={{ fontFamily: 'monospace' }}>{metrics.clearTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Physics</span> <span style={{ fontFamily: 'monospace' }}>{metrics.physicsTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Draw</span> <span style={{ fontFamily: 'monospace' }}>{metrics.drawTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Scan</span> <span style={{ fontFamily: 'monospace' }}>{metrics.scanTime.toFixed(2)}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between', gridColumn: 'span 2' }}><span>Rect Update</span> <span style={{ fontFamily: 'monospace' }}>{metrics.rectUpdateTime.toFixed(2)}ms</span></div>\n </div>\n </div>\n\n <div style={{ marginBottom: '16px' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Counts\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Snowflakes</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.flakeCount} / {metrics.maxFlakes}</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span>Surfaces</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.surfaceCount}</span>\n </div>\n </div>\n\n <button\n onClick={copyToClipboard}\n style={{\n width: '100%',\n padding: '10px',\n background: copied ? 'rgba(34, 197, 94, 0.2)' : 'rgba(255, 255, 255, 0.05)',\n border: copied ? '1px solid rgba(34, 197, 94, 0.5)' : '1px solid rgba(255, 255, 255, 0.1)',\n color: copied ? '#4ade80' : '#fff',\n cursor: 'pointer',\n borderRadius: '8px',\n fontSize: '11px',\n fontWeight: '600',\n transition: 'all 0.2s',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '6px'\n }}\n >\n {copied ? '✓ COPIED' : '📋 COPY METRICS'}\n </button>\n\n <div style={{ marginTop: '12px', fontSize: '10px', color: '#64748b', textAlign: 'center' }}>\n Shift+D to toggle\n </div>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","shouldAccumulate","import_jsx_runtime","import_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/Snowfall.tsx","../src/SnowfallProvider.tsx","../src/utils/snowfall/constants.ts","../src/utils/snowfall/dom.ts","../src/utils/snowfall/physics.ts","../src/utils/snowfall/draw.ts","../src/DebugPanel.tsx"],"sourcesContent":["'use client';\n\n// Main components\nexport { default as Snowfall } from './Snowfall';\nexport { SnowfallProvider, useSnowfall, DEFAULT_PHYSICS } from './SnowfallProvider';\nexport { default as DebugPanel } from './DebugPanel';\n\n// Types\nexport type { PhysicsConfig, PerformanceMetrics } from './SnowfallProvider';\n","'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { useSnowfall } from './SnowfallProvider';\nimport { Snowflake, SnowAccumulation } from './utils/snowfall/types';\nimport { getElementRects } from './utils/snowfall/dom';\nimport { createSnowflake, initializeAccumulation, meltAndSmoothAccumulation, updateSnowflakes } from './utils/snowfall/physics';\nimport { drawAccumulations, drawSideAccumulations, drawSnowflakes } from './utils/snowfall/draw';\n\nexport default function Snowfall() {\n const { isEnabled, physicsConfig, setMetrics } = useSnowfall();\n const isEnabledRef = useRef(isEnabled);\n const physicsConfigRef = useRef(physicsConfig);\n const setMetricsRef = useRef(setMetrics);\n const [isMounted, setIsMounted] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const snowflakesRef = useRef<Snowflake[]>([]);\n const accumulationRef = useRef<Map<Element, SnowAccumulation>>(new Map());\n const animationIdRef = useRef<number>(0);\n\n // Cache DPR to avoid reading it every frame (only changes on resize)\n // Initialize with safe default for SSR, actual value set in resizeCanvas\n const dprRef = useRef(1);\n\n // Performance metrics tracking\n const fpsFrames = useRef<number[]>([]);\n const metricsRef = useRef({\n scanTime: 0,\n rectUpdateTime: 0,\n frameTime: 0,\n rafGap: 0,\n clearTime: 0,\n physicsTime: 0,\n drawTime: 0,\n });\n\n useEffect(() => {\n requestAnimationFrame(() => setIsMounted(true));\n }, []);\n\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n physicsConfigRef.current = physicsConfig;\n }, [physicsConfig]);\n\n useEffect(() => {\n setMetricsRef.current = setMetrics;\n }, [setMetrics]);\n\n useEffect(() => {\n if (!isMounted) return;\n\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const resizeCanvas = () => {\n if (canvasRef.current) {\n // Use viewport dimensions for fixed canvas\n const newWidth = window.innerWidth;\n const newHeight = window.innerHeight;\n\n // Handle high DPI displays - cache DPR in ref for use in animation loop\n const dpr = window.devicePixelRatio || 1;\n dprRef.current = dpr;\n canvasRef.current.width = newWidth * dpr;\n canvasRef.current.height = newHeight * dpr;\n\n // Set CSS size\n canvasRef.current.style.width = `${newWidth}px`;\n canvasRef.current.style.height = `${newHeight}px`;\n }\n };\n resizeCanvas();\n\n const windowResizeObserver = new ResizeObserver(() => {\n resizeCanvas();\n });\n windowResizeObserver.observe(document.body);\n\n // Separate observer for snow accumulation surfaces\n const surfaceObserver = new ResizeObserver((entries) => {\n // Check if any accumulation element actually changed size\n let needsUpdate = false;\n for (const entry of entries) {\n if (entry.target.isConnected) {\n needsUpdate = true;\n break;\n }\n }\n if (needsUpdate) {\n // Re-run initialization to adapt to new sizes\n // We do NOT want to infinitely recurse, so initAccumulationWrapper\n // must be careful about disconnection if called from here.\n // Actually, we don't need to disconnect/reconnect here if the set of elements hasn't changed,\n // but getAccumulationSurfaces (called by init) might find new ones or drop old ones.\n // For simplicity, we just trigger the scan.\n initAccumulationWrapper();\n }\n });\n\n snowflakesRef.current = [];\n\n const initAccumulationWrapper = () => {\n const scanStart = performance.now();\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n\n // Sync observer with current surfaces\n surfaceObserver.disconnect();\n for (const [el] of accumulationRef.current) {\n surfaceObserver.observe(el);\n }\n\n metricsRef.current.scanTime = performance.now() - scanStart;\n };\n initAccumulationWrapper();\n\n // Delay visibility slightly to ensure smooth fade-in after canvas is ready\n requestAnimationFrame(() => {\n if (isMounted) setIsVisible(true);\n });\n\n let lastTime = 0;\n let lastMetricsUpdate = 0;\n // Holds current frame's element positions\n let elementRects: ReturnType<typeof getElementRects> = [];\n\n const animate = (currentTime: number) => {\n if (lastTime === 0) {\n lastTime = currentTime;\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n\n const deltaTime = Math.min(currentTime - lastTime, 50);\n\n // Always track FPS so we have data when panel opens\n const now = performance.now();\n fpsFrames.current.push(now);\n fpsFrames.current = fpsFrames.current.filter(t => now - t < 1000);\n\n // Track detailed performance metrics\n metricsRef.current.rafGap = currentTime - lastTime;\n\n lastTime = currentTime;\n const dt = deltaTime / 16.67;\n\n const frameStartTime = performance.now();\n\n // Time canvas clear\n const clearStart = performance.now();\n\n // Reset transform to clear the entire viewport-sized canvas\n // We use the cached dpr scaling, so we clear the logical width/height\n const dpr = dprRef.current;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);\n\n // Translate the context to Simulate Scrolling\n // We move the 'world' up by scrollY, so absolute coordinates draw in the correct place relative to viewport\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n ctx.translate(-scrollX, -scrollY);\n\n metricsRef.current.clearTime = performance.now() - clearStart;\n\n const snowflakes = snowflakesRef.current;\n\n // PERFORMANCE: Update element rects EVERY FRAME to track layout changes and animations\n // getBoundingClientRect() is necessary to handle moving/animating elements\n // getBoundingClientRect() is fast enough for the accumulation targets (< 50 elements)\n const rectStart = performance.now();\n elementRects = getElementRects(accumulationRef.current);\n metricsRef.current.rectUpdateTime = performance.now() - rectStart;\n\n // Time physics\n const physicsStart = performance.now();\n meltAndSmoothAccumulation(elementRects, physicsConfigRef.current, dt);\n updateSnowflakes(\n snowflakes,\n elementRects,\n physicsConfigRef.current,\n dt,\n document.documentElement.scrollWidth,\n document.documentElement.scrollHeight\n );\n metricsRef.current.physicsTime = performance.now() - physicsStart;\n\n // Draw Snowflakes (batched for performance)\n const drawStart = performance.now();\n drawSnowflakes(ctx, snowflakes);\n\n // Spawn new snowflakes with adaptive rate based on performance\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const currentFps = fpsFrames.current.length;\n\n // Adaptive spawn rate: reduce load when FPS is low to prevent death spirals\n // Low FPS (<40): 20% spawn rate | Normal FPS: 100% spawn rate\n const shouldSpawn = currentFps >= 40 || Math.random() < 0.2;\n\n if (shouldSpawn) {\n const isBackground = Math.random() < 0.4;\n snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));\n }\n }\n\n // Draw Accumulation\n // We draw accumulations in World Space (by passing scroll offset to draw functions)\n // This aligns perfectly with the translated context and world-space snowflakes.\n\n // Viewport culling: Filter to only visible elements before drawing\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const visibleRects = elementRects.filter(({ rect }) =>\n rect.right >= 0 && rect.left <= viewportWidth &&\n rect.bottom >= 0 && rect.top <= viewportHeight\n );\n\n // Only call draw functions if there are visible elements\n if (visibleRects.length > 0) {\n drawAccumulations(ctx, visibleRects, scrollX, scrollY);\n drawSideAccumulations(ctx, visibleRects, scrollX, scrollY);\n }\n\n metricsRef.current.drawTime = performance.now() - drawStart;\n metricsRef.current.frameTime = performance.now() - frameStartTime;\n\n // Update metrics every 500ms\n if (currentTime - lastMetricsUpdate > 500) {\n setMetricsRef.current({\n fps: fpsFrames.current.length,\n frameTime: metricsRef.current.frameTime,\n scanTime: metricsRef.current.scanTime,\n rectUpdateTime: metricsRef.current.rectUpdateTime,\n surfaceCount: accumulationRef.current.size,\n flakeCount: snowflakes.length,\n maxFlakes: physicsConfigRef.current.MAX_FLAKES,\n rafGap: metricsRef.current.rafGap,\n clearTime: metricsRef.current.clearTime,\n physicsTime: metricsRef.current.physicsTime,\n drawTime: metricsRef.current.drawTime,\n });\n lastMetricsUpdate = currentTime;\n }\n\n animationIdRef.current = requestAnimationFrame(animate);\n };\n\n animationIdRef.current = requestAnimationFrame(animate);\n\n const handleResize = () => {\n resizeCanvas();\n accumulationRef.current.clear();\n initAccumulationWrapper();\n };\n\n window.addEventListener('resize', handleResize);\n\n // Periodic surface scan every 5 seconds to detect DOM changes\n const checkInterval = setInterval(initAccumulationWrapper, 5000);\n\n return () => {\n cancelAnimationFrame(animationIdRef.current);\n window.removeEventListener('resize', handleResize);\n clearInterval(checkInterval);\n windowResizeObserver.disconnect();\n surfaceObserver.disconnect();\n };\n }, [isMounted]);\n\n if (!isMounted) return null;\n\n return (\n <>\n <canvas\n ref={canvasRef}\n style={{\n position: 'fixed', // FIXED position to eliminate scroll jitter\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n willChange: 'transform',\n }}\n aria-hidden=\"true\"\n />\n\n </>\n );\n}\n","'use client';\n\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n\nexport interface PhysicsConfig {\n MAX_FLAKES: number;\n MELT_SPEED: number;\n WIND_STRENGTH: number;\n ACCUMULATION: {\n SIDE_RATE: number;\n TOP_RATE: number;\n BOTTOM_RATE: number;\n };\n MAX_DEPTH: {\n TOP: number;\n BOTTOM: number;\n SIDE: number;\n };\n FLAKE_SIZE: {\n MIN: number;\n MAX: number;\n };\n MAX_SURFACES: number;\n}\n\nexport const DEFAULT_PHYSICS: PhysicsConfig = {\n MAX_FLAKES: 1000,\n MELT_SPEED: 0.00001,\n WIND_STRENGTH: 1.5,\n ACCUMULATION: {\n SIDE_RATE: 1,\n TOP_RATE: 5,\n BOTTOM_RATE: 5.0,\n },\n MAX_DEPTH: {\n TOP: 100,\n BOTTOM: 50,\n SIDE: 20,\n },\n FLAKE_SIZE: {\n MIN: 0.5,\n MAX: 1.6,\n },\n MAX_SURFACES: 15\n};\n\nexport interface PerformanceMetrics {\n fps: number;\n frameTime: number;\n scanTime: number;\n rectUpdateTime: number;\n surfaceCount: number;\n flakeCount: number;\n maxFlakes: number;\n // Detailed metrics\n rafGap: number; // Time between requestAnimationFrame calls\n clearTime: number; // Time to clear canvas\n physicsTime: number; // Time for physics updates\n drawTime: number; // Time to draw snowflakes and accumulation\n}\n\ninterface SnowfallContextType {\n isEnabled: boolean;\n toggleSnow: () => void;\n physicsConfig: PhysicsConfig;\n updatePhysicsConfig: (config: Partial<PhysicsConfig>) => void;\n resetPhysics: () => void;\n debugMode: boolean;\n toggleDebug: () => void;\n metrics: PerformanceMetrics | null;\n setMetrics: (metrics: PerformanceMetrics) => void;\n}\n\nconst SnowfallContext = createContext<SnowfallContextType | undefined>(undefined);\n\nexport function SnowfallProvider({ children, initialDebug = false }: { children: ReactNode; initialDebug?: boolean }) {\n const [isEnabled, setIsEnabled] = useState(true);\n const [physicsConfig, setPhysicsConfig] = useState<PhysicsConfig>(DEFAULT_PHYSICS);\n const [debugMode, setDebugMode] = useState(initialDebug);\n const [metrics, setMetrics] = useState<PerformanceMetrics | null>(null);\n\n const toggleSnow = () => {\n setIsEnabled((prev) => !prev);\n };\n\n const toggleDebug = () => {\n setDebugMode((prev) => !prev);\n };\n\n const updatePhysicsConfig = (config: Partial<PhysicsConfig>) => {\n setPhysicsConfig((prev) => ({\n ...prev,\n ...config,\n ACCUMULATION: {\n ...prev.ACCUMULATION,\n ...(config.ACCUMULATION || {}),\n },\n MAX_DEPTH: {\n ...prev.MAX_DEPTH,\n ...(config.MAX_DEPTH || {}),\n },\n FLAKE_SIZE: {\n ...prev.FLAKE_SIZE,\n ...(config.FLAKE_SIZE || {}),\n },\n }));\n };\n\n const resetPhysics = () => {\n setPhysicsConfig(DEFAULT_PHYSICS);\n };\n\n return (\n <SnowfallContext.Provider value={{\n isEnabled,\n toggleSnow,\n physicsConfig,\n updatePhysicsConfig,\n resetPhysics,\n debugMode,\n toggleDebug,\n metrics,\n setMetrics,\n }}>\n {children}\n </SnowfallContext.Provider>\n );\n}\n\nexport function useSnowfall() {\n const context = useContext(SnowfallContext);\n if (context === undefined) {\n throw new Error('useSnowfall must be used within a SnowfallProvider');\n }\n return context;\n}\n","export const ATTR_SNOWFALL = 'data-snowfall';\n\nexport const VAL_IGNORE = 'ignore';\nexport const VAL_TOP = 'top';\nexport const VAL_BOTTOM = 'bottom';\n\nexport const TAG_HEADER = 'header';\nexport const TAG_FOOTER = 'footer';\n\nexport const ROLE_BANNER = 'banner';\nexport const ROLE_CONTENTINFO = 'contentinfo';\n\n// Mathematical constants\nexport const TAU = Math.PI * 2; // Full circle in radians\n","import { SnowAccumulation, ElementSurface, SnowfallSurface } from './types';\nimport {\n ATTR_SNOWFALL, VAL_IGNORE, VAL_TOP, VAL_BOTTOM,\n TAG_HEADER, TAG_FOOTER, ROLE_BANNER, ROLE_CONTENTINFO\n} from './constants';\n\n// Headers: snow accumulates on BOTTOM edge\n// Footers: default to TOP surface (snow piles on top)\n// Use data-snowfall attributes to override this behavior\nconst BOTTOM_TAGS = [TAG_HEADER];\nconst BOTTOM_ROLES = [ROLE_BANNER];\n\nconst AUTO_DETECT_TAGS = [TAG_HEADER, TAG_FOOTER, 'article', 'section', 'aside', 'nav'];\nconst AUTO_DETECT_ROLES = [`[role=\"${ROLE_BANNER}\"]`, `[role=\"${ROLE_CONTENTINFO}\"]`, '[role=\"main\"]'];\nconst AUTO_DETECT_CLASSES = [\n '.card', '[class*=\"card\"]', '[class*=\"Card\"]',\n '[class*=\"bg-\"]', '[class*=\"shadow-\"]', '[class*=\"rounded-\"]'\n];\n\n// Helper to get element type (Top vs Bottom surface) based on tags/roles\n// This is used to determine if snow should sit ON TOP or hang from the BOTTOM.\n\nexport const getElementType = (el: Element): SnowfallSurface => {\n const tagName = el.tagName.toLowerCase();\n if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;\n\n const role = el.getAttribute('role');\n if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;\n\n // Default: snow accumulates on top of elements (natural physics)\n return VAL_TOP;\n};\n\nconst shouldAccumulate = (el: Element, precomputedStyle?: CSSStyleDeclaration): boolean => {\n if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;\n // Explicit opt-in\n if (el.hasAttribute(ATTR_SNOWFALL)) return true;\n\n const styles = precomputedStyle || window.getComputedStyle(el);\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n if (!isVisible) return false;\n\n // Check for visual prominence\n const bgColor = styles.backgroundColor;\n const hasBackground = bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent';\n const hasBorder = (parseFloat(styles.borderWidth) > 0 && styles.borderColor !== 'transparent' && styles.borderColor !== 'rgba(0, 0, 0, 0)') && styles.borderStyle !== 'none';\n const hasBoxShadow = styles.boxShadow !== 'none';\n const hasFilter = styles.filter !== 'none' && styles.filter.includes('drop-shadow');\n const hasBackdropFilter = styles.backdropFilter !== 'none';\n\n return hasBackground || hasBorder || hasBoxShadow || hasFilter || hasBackdropFilter;\n};\n\nexport const getAccumulationSurfaces = (maxSurfaces: number = 5): { el: Element; type: SnowfallSurface }[] => {\n // No explicit clearing needed as we don't cache styles persistently anymore.\n const surfaces: { el: Element; type: SnowfallSurface }[] = [];\n const seen = new Set<Element>();\n\n const candidates = document.querySelectorAll(\n [\n `[${ATTR_SNOWFALL}]`,\n ...AUTO_DETECT_TAGS,\n ...AUTO_DETECT_ROLES,\n ...AUTO_DETECT_CLASSES\n ].join(', ')\n );\n\n for (const el of candidates) {\n if (surfaces.length >= maxSurfaces) break;\n if (seen.has(el)) continue;\n\n // Manual override check first\n const manualOverride = el.getAttribute(ATTR_SNOWFALL);\n if (manualOverride === VAL_IGNORE) continue;\n\n // If manually opted in, skip some heuristic checks but keep basic visibility/size sanity\n const isManuallyIncluded = manualOverride !== null;\n const styles = window.getComputedStyle(el);\n\n // Visibility Check: Must be visible and opaque enough\n const isVisible = styles.display !== 'none' && styles.visibility !== 'hidden' && parseFloat(styles.opacity) > 0.1;\n\n if (!isVisible && !isManuallyIncluded) continue;\n\n const rect = el.getBoundingClientRect();\n const hasSize = rect.width >= 100 && rect.height >= 50;\n if (!hasSize && !isManuallyIncluded) continue;\n\n const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;\n const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());\n const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute('role') || '');\n const isBottomSurface = isBottomTag || isBottomRole || manualOverride === VAL_BOTTOM;\n\n if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) continue;\n\n if (shouldAccumulate(el, styles)) {\n let type: SnowfallSurface = getElementType(el);\n if (manualOverride === VAL_BOTTOM) type = VAL_BOTTOM;\n else if (manualOverride === VAL_TOP) type = VAL_TOP;\n\n surfaces.push({ el, type });\n seen.add(el);\n }\n }\n\n return surfaces;\n};\n\nexport const getElementRects = (accumulationMap: Map<Element, SnowAccumulation>): ElementSurface[] => {\n const elementRects: ElementSurface[] = [];\n for (const [el, acc] of accumulationMap.entries()) {\n if (!el.isConnected) continue;\n // PURE VIEWPORT RECT. No scroll math.\n const rect = el.getBoundingClientRect();\n elementRects.push({ el, rect, acc });\n }\n return elementRects;\n};\n","import { PhysicsConfig } from '../../SnowfallProvider';\nimport { Snowflake, SnowAccumulation, ElementSurface } from './types';\nimport { getAccumulationSurfaces } from './dom';\nimport { VAL_BOTTOM, TAU } from './constants';\n\n// Opacity buckets for batched rendering (reduce globalAlpha state changes)\nconst OPACITY_BUCKETS = [0.3, 0.5, 0.7, 0.9];\n\n/**\n * Quantize opacity to nearest bucket for efficient batching during draw.\n * This eliminates runtime bucketing overhead by snapping at creation time.\n */\nconst quantizeOpacity = (opacity: number): number => {\n return OPACITY_BUCKETS.reduce((prev, curr) =>\n Math.abs(curr - opacity) < Math.abs(prev - opacity) ? curr : prev\n );\n};\n\nexport const createSnowflake = (\n worldWidth: number,\n config: PhysicsConfig,\n isBackground = false\n): Snowflake => {\n // Two random calls per flake:\n // 1) horizontal position\n // 2) DNA seed for all other traits\n const x = Math.random() * worldWidth;\n const dna = Math.random();\n\n // Pseudo-random trait extraction from DNA\n const noise = {\n speed: (dna * 13) % 1,\n wind: (dna * 7) % 1,\n wobblePhase: (dna * 23) % 1,\n wobbleSpeed: (dna * 5) % 1\n };\n\n const { MIN, MAX } = config.FLAKE_SIZE;\n const sizeRatio = dna;\n\n // Background vs foreground tuning\n const profile = isBackground\n ? {\n sizeMin: MIN * 0.6,\n sizeRange: (MAX - MIN) * 0.4,\n speedBase: 0.2,\n speedScale: 0.3,\n noiseSpeedScale: 0.2,\n windScale: config.WIND_STRENGTH * 0.625,\n opacityBase: 0.2,\n opacityScale: 0.2,\n wobbleBase: 0.005,\n wobbleScale: 0.015\n }\n : {\n sizeMin: MIN,\n sizeRange: MAX - MIN,\n speedBase: 0.5,\n speedScale: 0.5,\n noiseSpeedScale: 0.3,\n windScale: config.WIND_STRENGTH,\n opacityBase: 0.5,\n opacityScale: 0.3,\n wobbleBase: 0.01,\n wobbleScale: 0.02\n };\n\n const radius = profile.sizeMin + sizeRatio * profile.sizeRange;\n const glowRadius = radius * 1.5;\n\n // Calculate opacity and quantize to bucket for efficient batched rendering\n const rawOpacity = profile.opacityBase + sizeRatio * profile.opacityScale;\n const opacity = quantizeOpacity(rawOpacity);\n\n // Pre-calculate glow opacity (also quantized for batching)\n const rawGlowOpacity = opacity * 0.2;\n const glowOpacity = quantizeOpacity(rawGlowOpacity);\n\n return {\n x: x,\n y: window.scrollY - 5,\n radius: radius,\n glowRadius: glowRadius,\n speed:\n radius * profile.speedScale +\n noise.speed * profile.noiseSpeedScale +\n profile.speedBase,\n wind: (noise.wind - 0.5) * profile.windScale,\n opacity: opacity,\n glowOpacity: glowOpacity,\n wobble: noise.wobblePhase * TAU,\n wobbleSpeed: noise.wobbleSpeed * profile.wobbleScale + profile.wobbleBase,\n sizeRatio: sizeRatio,\n isBackground: isBackground\n };\n};\n\n/**\n * Initialize max heights array for snow accumulation with edge tapering and smoothing.\n * Exported for benchmarking.\n */\nexport const initializeMaxHeights = (\n width: number,\n baseMax: number,\n borderRadius: number,\n isBottom: boolean = false\n): number[] => {\n let maxHeights = new Array(width);\n for (let i = 0; i < width; i++) {\n let edgeFactor = 1.0;\n if (!isBottom && borderRadius > 0) {\n if (i < borderRadius) {\n edgeFactor = Math.pow(i / borderRadius, 1.2);\n } else if (i > width - borderRadius) {\n edgeFactor = Math.pow((width - i) / borderRadius, 1.2);\n }\n }\n maxHeights[i] = baseMax * edgeFactor * (0.85 + Math.random() * 0.15);\n }\n\n const smoothPasses = 4;\n for (let p = 0; p < smoothPasses; p++) {\n const smoothed = [...maxHeights];\n for (let i = 1; i < width - 1; i++) {\n smoothed[i] = (maxHeights[i - 1] + maxHeights[i] + maxHeights[i + 1]) / 3;\n }\n maxHeights = smoothed;\n }\n\n return maxHeights;\n};\n\nconst calculateCurveOffsets = (width: number, borderRadius: number, isBottom: boolean): number[] => {\n const offsets = new Array(width).fill(0);\n if (borderRadius <= 0 || isBottom) return offsets;\n\n for (let x = 0; x < width; x++) {\n let offset = 0;\n if (x < borderRadius) {\n const dist = borderRadius - x;\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n } else if (x > width - borderRadius) {\n const dist = x - (width - borderRadius);\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n }\n offsets[x] = offset;\n }\n return offsets;\n};\n\nconst calculateGravityMultipliers = (height: number): number[] => {\n const multipliers = new Array(height);\n for (let i = 0; i < height; i++) {\n const ratio = i / height;\n multipliers[i] = Math.sqrt(ratio);\n }\n return multipliers;\n};\n\nexport const initializeAccumulation = (\n accumulationMap: Map<Element, SnowAccumulation>,\n config: PhysicsConfig\n) => {\n // Scan DOM for new valid surfaces\n const elements = getAccumulationSurfaces(config.MAX_SURFACES);\n // Prune disconnected elements\n for (const [el] of accumulationMap.entries()) {\n if (!el.isConnected) {\n accumulationMap.delete(el);\n }\n }\n\n elements.forEach(({ el, type }) => {\n const existing = accumulationMap.get(el);\n const rect = el.getBoundingClientRect();\n const width = Math.ceil(rect.width);\n const isBottom = type === VAL_BOTTOM;\n\n if (existing && existing.heights.length === width) {\n existing.type = type;\n if (existing.borderRadius !== undefined) {\n const styleBuffer = window.getComputedStyle(el);\n existing.borderRadius = parseFloat(styleBuffer.borderTopLeftRadius) || 0;\n existing.curveOffsets = calculateCurveOffsets(width, existing.borderRadius, isBottom);\n // Initialize gravity multipliers if height matches but they're missing\n if (existing.leftSide.length === Math.ceil(rect.height) && !existing.sideGravityMultipliers) {\n existing.sideGravityMultipliers = calculateGravityMultipliers(existing.leftSide.length);\n }\n }\n return;\n }\n\n const height = Math.ceil(rect.height);\n const baseMax = isBottom ? config.MAX_DEPTH.BOTTOM : config.MAX_DEPTH.TOP;\n\n const styles = window.getComputedStyle(el);\n const borderRadius = parseFloat(styles.borderTopLeftRadius) || 0;\n\n const maxHeights = initializeMaxHeights(width, baseMax, borderRadius, isBottom);\n\n accumulationMap.set(el, {\n heights: existing?.heights.length === width ? existing.heights : new Array(width).fill(0),\n maxHeights,\n leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),\n rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),\n maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,\n leftMax: existing?.leftSide.length === height ? existing.leftMax : 0,\n rightMax: existing?.rightSide.length === height ? existing.rightMax : 0,\n borderRadius,\n curveOffsets: calculateCurveOffsets(width, borderRadius, isBottom),\n sideGravityMultipliers: calculateGravityMultipliers(height),\n type\n });\n });\n};\n\n/**\n * Accumulate snow on a side array and return the new max height.\n * The caller should update acc.leftMax or acc.rightMax with the returned value.\n */\nexport const accumulateSide = (\n sideArray: number[],\n rectHeight: number,\n localY: number,\n maxSideHeight: number,\n borderRadius: number,\n config: PhysicsConfig,\n currentMax: number\n): number => {\n const spread = 4;\n const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);\n let newMax = currentMax;\n\n for (let dy = -spread; dy <= spread; dy++) {\n const y = localY + dy;\n if (y >= 0 && y < sideArray.length) {\n const inTop = y < borderRadius;\n const inBottom = y > rectHeight - borderRadius;\n if (borderRadius > 0 && (inTop || inBottom)) continue;\n\n const normalizedDist = Math.abs(dy) / spread;\n const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;\n const newHeight = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);\n sideArray[y] = newHeight;\n if (newHeight > newMax) newMax = newHeight;\n }\n }\n return newMax;\n};\n\nexport const updateSnowflakes = (\n snowflakes: Snowflake[],\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number,\n worldWidth: number,\n worldHeight: number\n) => {\n // Scroll used for World -> Viewport mapping for collision\n // Flakes are in World Space.\n // DOM Rects are in Viewport Space (relative to the window).\n // To check collision, we subtract scrollX/Y from Flake coordinates to get their Viewport position.\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (let i = snowflakes.length - 1; i >= 0; i--) {\n const flake = snowflakes[i];\n\n flake.wobble += flake.wobbleSpeed * dt;\n flake.x += (flake.wind + Math.sin(flake.wobble) * 0.5) * dt;\n flake.y += (flake.speed + Math.cos(flake.wobble * 0.5) * 0.1) * dt;\n\n let landed = false;\n\n for (const item of elementRects) {\n const { rect, acc } = item;\n const isBottom = acc.type === VAL_BOTTOM;\n\n // Flake is World Space. Rect is Viewport Space.\n // Map Flake to Viewport Relative for collision check vs Rect\n const flakeViewportX = flake.x - scrollX;\n const flakeViewportY = flake.y - scrollY;\n\n // Simple Collision Check\n const isInVerticalBounds = flakeViewportY >= rect.top && flakeViewportY <= rect.bottom;\n\n // Side collisions\n // Check if flake hits the left or right edge of the element\n if (!landed && acc.maxSideHeight > 0 && !isBottom) {\n if (isInVerticalBounds) {\n const localY = Math.floor(flakeViewportY - rect.top);\n const borderRadius = acc.borderRadius;\n\n const isInTopCorner = localY < borderRadius;\n const isInBottomCorner = localY > rect.height - borderRadius;\n const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);\n\n if (flakeViewportX >= rect.left - 5 && flakeViewportX < rect.left + 3) {\n if (!isCorner) {\n acc.leftMax = accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.leftMax);\n landed = true;\n }\n }\n\n if (!landed && flakeViewportX > rect.right - 3 && flakeViewportX <= rect.right + 5) {\n if (!isCorner) {\n acc.rightMax = accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.rightMax);\n landed = true;\n }\n }\n\n if (landed) break;\n }\n }\n\n // Top/Bottom accumulation\n // Check if flake hits the primary horizontal surface\n if (flakeViewportX >= rect.left && flakeViewportX <= rect.right) {\n const localX = Math.floor(flakeViewportX - rect.left);\n const currentHeight = acc.heights[localX] || 0;\n const maxHeight = acc.maxHeights[localX] || 5;\n\n const surfaceY = isBottom ? rect.bottom - currentHeight : rect.top - currentHeight;\n\n if (flakeViewportY >= surfaceY && flakeViewportY < surfaceY + 10 && currentHeight < maxHeight) {\n const shouldAccumulate = isBottom ? Math.random() < 0.15 : true;\n\n if (shouldAccumulate) {\n const baseSpread = Math.ceil(flake.radius);\n const spread = baseSpread + Math.floor(Math.random() * 2);\n const accumRate = isBottom ? config.ACCUMULATION.BOTTOM_RATE : config.ACCUMULATION.TOP_RATE;\n const centerOffset = Math.floor(Math.random() * 3) - 1;\n\n for (let dx = -spread; dx <= spread; dx++) {\n if (Math.random() < 0.15) continue;\n const idx = localX + dx + centerOffset;\n if (idx >= 0 && idx < acc.heights.length) {\n const dist = Math.abs(dx);\n const pixelMax = acc.maxHeights[idx] || 5;\n\n const normDist = dist / spread;\n const falloff = (Math.cos(normDist * Math.PI) + 1) / 2;\n const baseAdd = 0.3 * falloff;\n\n const randomFactor = 0.8 + Math.random() * 0.4;\n const addHeight = baseAdd * randomFactor * accumRate;\n\n if (acc.heights[idx] < pixelMax && addHeight > 0) {\n acc.heights[idx] = Math.min(pixelMax, acc.heights[idx] + addHeight);\n }\n }\n }\n\n if (isBottom) {\n landed = true;\n break;\n }\n }\n\n if (!isBottom) {\n landed = true;\n break;\n }\n }\n }\n }\n\n if (landed || flake.y > worldHeight + 10 || flake.x < -20 || flake.x > worldWidth + 20) {\n snowflakes.splice(i, 1);\n }\n }\n};\n\nexport const meltAndSmoothAccumulation = (\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number\n) => {\n for (const { acc } of elementRects) {\n const meltRate = config.MELT_SPEED * dt;\n const len = acc.heights.length;\n\n // Smooth\n if (len > 2) {\n for (let i = 1; i < len - 1; i++) {\n if (acc.heights[i] > 0.05) {\n const avg = (acc.heights[i - 1] + acc.heights[i + 1]) / 2;\n acc.heights[i] = acc.heights[i] * 0.99 + avg * 0.01;\n }\n }\n }\n\n // Melt\n for (let i = 0; i < acc.heights.length; i++) {\n if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);\n }\n // Melt sides and update max values\n let leftMax = 0;\n let rightMax = 0;\n for (let i = 0; i < acc.leftSide.length; i++) {\n if (acc.leftSide[i] > 0) {\n acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);\n if (acc.leftSide[i] > leftMax) leftMax = acc.leftSide[i];\n }\n if (acc.rightSide[i] > 0) {\n acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);\n if (acc.rightSide[i] > rightMax) rightMax = acc.rightSide[i];\n }\n }\n acc.leftMax = leftMax;\n acc.rightMax = rightMax;\n }\n};\n","import { Snowflake, ElementSurface } from './types';\nimport { VAL_BOTTOM, TAU } from './constants';\n\nconst ACC_FILL_STYLE = 'rgba(255, 255, 255, 0.95)';\nconst ACC_SHADOW_COLOR = 'rgba(200, 230, 255, 0.6)';\n\n\n/**\n * Single-pass snowflake rendering for optimal cache locality.\n * Flakes are tracked in World Space.\n * The Canvas Context is translated by (-scrollX, -scrollY), effectively viewing the World.\n * \n * PERFORMANCE: All glow/core values are pre-calculated at creation time (physics.ts),\n * enabling a simple single-pass render with excellent cache performance.\n */\nexport const drawSnowflakes = (ctx: CanvasRenderingContext2D, flakes: Snowflake[]) => {\n if (flakes.length === 0) return;\n\n ctx.fillStyle = '#FFFFFF';\n\n // Single pass: Draw glow (behind) then core (front) for each flake\n for (const flake of flakes) {\n // Draw glow effect first (behind the core) - skip for background flakes\n if (!flake.isBackground) {\n ctx.globalAlpha = flake.glowOpacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.glowRadius, 0, TAU);\n ctx.fill();\n }\n\n // Draw core on top\n ctx.globalAlpha = flake.opacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius, 0, TAU);\n ctx.fill();\n }\n\n // Reset alpha once at the end\n ctx.globalAlpha = 1.0;\n};\n\nexport const drawAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 4;\n ctx.shadowOffsetY = -1;\n // Explicitly reset alpha as we modified it in drawSnowflake\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n // Iterate over all accumulation surfaces to build the single path\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (!acc.heights.some(h => h > 0.1)) continue;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\n\n // Precompute world-space coordinates (hoist additions out of loops)\n const worldLeft = rect.left + scrollX;\n const worldBaseY = baseY + scrollY;\n\n let first = true;\n const step = 2;\n const len = acc.heights.length;\n\n // Draw the uneven top surface of the snow\n for (let x = 0; x < len; x += step) {\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n if (first) {\n ctx.moveTo(px, py);\n first = false;\n } else {\n ctx.lineTo(px, py);\n }\n }\n\n if ((len - 1) % step !== 0) {\n const x = len - 1;\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n // Draw the bottom edge (aligned with element border) to close the shape\n for (let x = len - 1; x >= 0; x -= step) {\n const px = worldLeft + x;\n const py = worldBaseY + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = worldLeft + startX;\n const startPy = worldBaseY + (acc.curveOffsets[startX] || 0);\n ctx.lineTo(startPx, startPy);\n }\n\n // Fill all accumulations in one pass to batch shadow rendering\n ctx.fill();\n\n ctx.shadowBlur = 0;\n ctx.shadowOffsetY = 0;\n};\n\nexport const drawSideAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 3;\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n const drawSide = (sideArray: number[], isLeft: boolean, multipliers: number[], rect: DOMRect, dx: number, dy: number) => {\n const baseX = isLeft ? rect.left : rect.right;\n\n // Precompute world-space coordinates\n const worldBaseX = baseX + dx;\n const worldTop = rect.top + dy;\n const worldBottom = rect.bottom + dy;\n\n ctx.moveTo(worldBaseX, worldTop);\n\n // Draw the uneven side profile\n for (let y = 0; y < sideArray.length; y += 2) {\n const width = sideArray[y] || 0;\n const nextY = Math.min(y + 2, sideArray.length - 1);\n const nextWidth = sideArray[nextY] || 0;\n\n const gravityMultiplier = multipliers[y] || 0;\n\n const py = worldTop + y;\n const px = (isLeft ? worldBaseX - (width * gravityMultiplier) : worldBaseX + (width * gravityMultiplier));\n const ny = worldTop + nextY;\n const nGravityMultiplier = multipliers[nextY] || 0;\n const nx = (isLeft ? worldBaseX - (nextWidth * nGravityMultiplier) : worldBaseX + (nextWidth * nGravityMultiplier));\n\n ctx.lineTo(px, py);\n ctx.lineTo(nx, ny);\n }\n\n // Close at the bottom\n ctx.lineTo(worldBaseX, worldBottom);\n };\n\n // Scan elements and append their side snow profiles to the current path for batched rendering\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (acc.maxSideHeight === 0) continue;\n\n const hasLeftSnow = acc.leftMax > 0.3;\n const hasRightSnow = acc.rightMax > 0.3;\n\n if (!hasLeftSnow && !hasRightSnow) continue;\n\n if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n }\n\n ctx.fill();\n\n ctx.shadowBlur = 0;\n};\n","'use client';\n\nimport { useSnowfall } from './SnowfallProvider';\nimport { useEffect, useState } from 'react';\n\nexport default function DebugPanel({ defaultOpen = true }: { defaultOpen?: boolean }) {\n const { debugMode, toggleDebug, metrics } = useSnowfall();\n const [isMinimized, setIsMinimized] = useState(!defaultOpen);\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.shiftKey && e.key === 'D') {\n toggleDebug();\n }\n };\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleDebug]);\n\n const copyToClipboard = () => {\n if (metrics) {\n const data = {\n ...metrics,\n timestamp: new Date().toISOString(),\n userAgent: navigator.userAgent,\n canvasSize: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n };\n navigator.clipboard.writeText(JSON.stringify(data, null, 2));\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n };\n\n if (!debugMode) return null;\n\n return (\n <div\n data-snowfall=\"top\"\n style={{\n position: 'fixed',\n bottom: '80px',\n left: '24px',\n backgroundColor: 'rgba(15, 23, 42, 0.75)',\n backdropFilter: 'blur(16px)',\n WebkitBackdropFilter: 'blur(16px)',\n color: '#e2e8f0',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',\n fontSize: '12px',\n padding: isMinimized ? '12px' : '20px',\n borderRadius: '16px',\n zIndex: 10000,\n minWidth: isMinimized ? 'auto' : '300px',\n maxWidth: '100%',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.2), 0 8px 10px -6px rgba(0, 0, 0, 0.2)',\n transition: 'all 0.2s ease',\n }}>\n <div style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: isMinimized ? 0 : '16px',\n gap: '16px'\n }}>\n <div style={{ fontWeight: '600', color: '#fff', display: 'flex', alignItems: 'center', gap: '8px' }}>\n <span style={{ fontSize: '14px' }}>❄️</span>\n <span style={{\n background: 'linear-gradient(to right, #60a5fa, #22d3ee)',\n WebkitBackgroundClip: 'text',\n WebkitTextFillColor: 'transparent',\n fontWeight: '700'\n }}>DEBUG</span>\n </div>\n <div style={{ display: 'flex', gap: '8px' }}>\n <button\n onClick={() => setIsMinimized(!isMinimized)}\n style={{\n background: 'rgba(255, 255, 255, 0.1)',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n color: '#fff',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '10px',\n }}\n >\n {isMinimized ? '▲' : '▼'}\n </button>\n <button\n onClick={toggleDebug}\n style={{\n background: 'rgba(239, 68, 68, 0.15)',\n border: '1px solid rgba(239, 68, 68, 0.2)',\n color: '#f87171',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '12px',\n }}\n >\n ✕\n </button>\n </div>\n </div>\n\n {!isMinimized && metrics && (\n <>\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Performance\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>FPS</span>\n <span style={{ fontWeight: 'bold', color: metrics.fps < 30 ? '#f87171' : metrics.fps < 50 ? '#facc15' : '#4ade80' }}>\n {metrics.fps.toFixed(1)}\n </span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Frame Time</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.frameTime.toFixed(2)}ms</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: metrics.rafGap && metrics.rafGap > 20 ? '#fbbf24' : 'inherit' }}>rAF Gap</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.rafGap?.toFixed(1) || 0}ms</span>\n </div>\n </div>\n\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Detailed Timings\n </div>\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '4px 12px' }}>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Clear</span> <span style={{ fontFamily: 'monospace' }}>{metrics.clearTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Physics</span> <span style={{ fontFamily: 'monospace' }}>{metrics.physicsTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Draw</span> <span style={{ fontFamily: 'monospace' }}>{metrics.drawTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Scan</span> <span style={{ fontFamily: 'monospace' }}>{metrics.scanTime.toFixed(2)}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between', gridColumn: 'span 2' }}><span>Rect Update</span> <span style={{ fontFamily: 'monospace' }}>{metrics.rectUpdateTime.toFixed(2)}ms</span></div>\n </div>\n </div>\n\n <div style={{ marginBottom: '16px' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Counts\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Snowflakes</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.flakeCount} / {metrics.maxFlakes}</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span>Surfaces</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.surfaceCount}</span>\n </div>\n </div>\n\n <button\n onClick={copyToClipboard}\n style={{\n width: '100%',\n padding: '10px',\n background: copied ? 'rgba(34, 197, 94, 0.2)' : 'rgba(255, 255, 255, 0.05)',\n border: copied ? '1px solid rgba(34, 197, 94, 0.5)' : '1px solid rgba(255, 255, 255, 0.1)',\n color: copied ? '#4ade80' : '#fff',\n cursor: 'pointer',\n borderRadius: '8px',\n fontSize: '11px',\n fontWeight: '600',\n transition: 'all 0.2s',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '6px'\n }}\n >\n {copied ? '✓ COPIED' : '📋 COPY METRICS'}\n </button>\n\n <div style={{ marginTop: '12px', fontSize: '10px', color: '#64748b', textAlign: 'center' }}>\n Shift+D to toggle\n </div>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","shouldAccumulate","import_jsx_runtime","import_react","import_jsx_runtime"]}
package/dist/index.mjs CHANGED
@@ -316,6 +316,8 @@ var initializeAccumulation = (accumulationMap, config) => {
316
316
  leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),
317
317
  rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),
318
318
  maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,
319
+ leftMax: existing?.leftSide.length === height ? existing.leftMax : 0,
320
+ rightMax: existing?.rightSide.length === height ? existing.rightMax : 0,
319
321
  borderRadius,
320
322
  curveOffsets: calculateCurveOffsets(width, borderRadius, isBottom),
321
323
  sideGravityMultipliers: calculateGravityMultipliers(height),
@@ -323,9 +325,10 @@ var initializeAccumulation = (accumulationMap, config) => {
323
325
  });
324
326
  });
325
327
  };
326
- var accumulateSide = (sideArray, rectHeight, localY, maxSideHeight, borderRadius, config) => {
328
+ var accumulateSide = (sideArray, rectHeight, localY, maxSideHeight, borderRadius, config, currentMax) => {
327
329
  const spread = 4;
328
330
  const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);
331
+ let newMax = currentMax;
329
332
  for (let dy = -spread; dy <= spread; dy++) {
330
333
  const y = localY + dy;
331
334
  if (y >= 0 && y < sideArray.length) {
@@ -334,9 +337,12 @@ var accumulateSide = (sideArray, rectHeight, localY, maxSideHeight, borderRadius
334
337
  if (borderRadius > 0 && (inTop || inBottom)) continue;
335
338
  const normalizedDist = Math.abs(dy) / spread;
336
339
  const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;
337
- sideArray[y] = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);
340
+ const newHeight = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);
341
+ sideArray[y] = newHeight;
342
+ if (newHeight > newMax) newMax = newHeight;
338
343
  }
339
344
  }
345
+ return newMax;
340
346
  };
341
347
  var updateSnowflakes = (snowflakes, elementRects, config, dt, worldWidth, worldHeight) => {
342
348
  const scrollX = window.scrollX;
@@ -362,13 +368,13 @@ var updateSnowflakes = (snowflakes, elementRects, config, dt, worldWidth, worldH
362
368
  const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);
363
369
  if (flakeViewportX >= rect.left - 5 && flakeViewportX < rect.left + 3) {
364
370
  if (!isCorner) {
365
- accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);
371
+ acc.leftMax = accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.leftMax);
366
372
  landed = true;
367
373
  }
368
374
  }
369
375
  if (!landed && flakeViewportX > rect.right - 3 && flakeViewportX <= rect.right + 5) {
370
376
  if (!isCorner) {
371
- accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);
377
+ acc.rightMax = accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.rightMax);
372
378
  landed = true;
373
379
  }
374
380
  }
@@ -435,10 +441,20 @@ var meltAndSmoothAccumulation = (elementRects, config, dt) => {
435
441
  for (let i = 0; i < acc.heights.length; i++) {
436
442
  if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);
437
443
  }
444
+ let leftMax = 0;
445
+ let rightMax = 0;
438
446
  for (let i = 0; i < acc.leftSide.length; i++) {
439
- if (acc.leftSide[i] > 0) acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);
440
- if (acc.rightSide[i] > 0) acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);
447
+ if (acc.leftSide[i] > 0) {
448
+ acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);
449
+ if (acc.leftSide[i] > leftMax) leftMax = acc.leftSide[i];
450
+ }
451
+ if (acc.rightSide[i] > 0) {
452
+ acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);
453
+ if (acc.rightSide[i] > rightMax) rightMax = acc.rightSide[i];
454
+ }
441
455
  }
456
+ acc.leftMax = leftMax;
457
+ acc.rightMax = rightMax;
442
458
  }
443
459
  };
444
460
 
@@ -541,8 +557,8 @@ var drawSideAccumulations = (ctx, elementRects, scrollX, scrollY) => {
541
557
  for (const item of elementRects) {
542
558
  const { rect, acc } = item;
543
559
  if (acc.maxSideHeight === 0) continue;
544
- const hasLeftSnow = acc.leftSide.some((h) => h > 0.3);
545
- const hasRightSnow = acc.rightSide.some((h) => h > 0.3);
560
+ const hasLeftSnow = acc.leftMax > 0.3;
561
+ const hasRightSnow = acc.rightMax > 0.3;
546
562
  if (!hasLeftSnow && !hasRightSnow) continue;
547
563
  if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, scrollX, scrollY);
548
564
  if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, scrollX, scrollY);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/Snowfall.tsx","../src/SnowfallProvider.tsx","../src/utils/snowfall/constants.ts","../src/utils/snowfall/dom.ts","../src/utils/snowfall/physics.ts","../src/utils/snowfall/draw.ts","../src/DebugPanel.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { useSnowfall } from './SnowfallProvider';\nimport { Snowflake, SnowAccumulation } from './utils/snowfall/types';\nimport { getElementRects } from './utils/snowfall/dom';\nimport { createSnowflake, initializeAccumulation, meltAndSmoothAccumulation, updateSnowflakes } from './utils/snowfall/physics';\nimport { drawAccumulations, drawSideAccumulations, drawSnowflakes } from './utils/snowfall/draw';\n\nexport default function Snowfall() {\n const { isEnabled, physicsConfig, setMetrics } = useSnowfall();\n const isEnabledRef = useRef(isEnabled);\n const physicsConfigRef = useRef(physicsConfig);\n const setMetricsRef = useRef(setMetrics);\n const [isMounted, setIsMounted] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const snowflakesRef = useRef<Snowflake[]>([]);\n const accumulationRef = useRef<Map<Element, SnowAccumulation>>(new Map());\n const animationIdRef = useRef<number>(0);\n\n // Cache DPR to avoid reading it every frame (only changes on resize)\n // Initialize with safe default for SSR, actual value set in resizeCanvas\n const dprRef = useRef(1);\n\n // Performance metrics tracking\n const fpsFrames = useRef<number[]>([]);\n const metricsRef = useRef({\n scanTime: 0,\n rectUpdateTime: 0,\n frameTime: 0,\n rafGap: 0,\n clearTime: 0,\n physicsTime: 0,\n drawTime: 0,\n });\n\n useEffect(() => {\n requestAnimationFrame(() => setIsMounted(true));\n }, []);\n\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n physicsConfigRef.current = physicsConfig;\n }, [physicsConfig]);\n\n useEffect(() => {\n setMetricsRef.current = setMetrics;\n }, [setMetrics]);\n\n useEffect(() => {\n if (!isMounted) return;\n\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const resizeCanvas = () => {\n if (canvasRef.current) {\n // Use viewport dimensions for fixed canvas\n const newWidth = window.innerWidth;\n const newHeight = window.innerHeight;\n\n // Handle high DPI displays - cache DPR in ref for use in animation loop\n const dpr = window.devicePixelRatio || 1;\n dprRef.current = dpr;\n canvasRef.current.width = newWidth * dpr;\n canvasRef.current.height = newHeight * dpr;\n\n // Set CSS size\n canvasRef.current.style.width = `${newWidth}px`;\n canvasRef.current.style.height = `${newHeight}px`;\n }\n };\n resizeCanvas();\n\n const windowResizeObserver = new ResizeObserver(() => {\n resizeCanvas();\n });\n windowResizeObserver.observe(document.body);\n\n // Separate observer for snow accumulation surfaces\n const surfaceObserver = new ResizeObserver((entries) => {\n // Check if any accumulation element actually changed size\n let needsUpdate = false;\n for (const entry of entries) {\n if (entry.target.isConnected) {\n needsUpdate = true;\n break;\n }\n }\n if (needsUpdate) {\n // Re-run initialization to adapt to new sizes\n // We do NOT want to infinitely recurse, so initAccumulationWrapper\n // must be careful about disconnection if called from here.\n // Actually, we don't need to disconnect/reconnect here if the set of elements hasn't changed,\n // but getAccumulationSurfaces (called by init) might find new ones or drop old ones.\n // For simplicity, we just trigger the scan.\n initAccumulationWrapper();\n }\n });\n\n snowflakesRef.current = [];\n\n const initAccumulationWrapper = () => {\n const scanStart = performance.now();\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n\n // Sync observer with current surfaces\n surfaceObserver.disconnect();\n for (const [el] of accumulationRef.current) {\n surfaceObserver.observe(el);\n }\n\n metricsRef.current.scanTime = performance.now() - scanStart;\n };\n initAccumulationWrapper();\n\n // Delay visibility slightly to ensure smooth fade-in after canvas is ready\n requestAnimationFrame(() => {\n if (isMounted) setIsVisible(true);\n });\n\n let lastTime = 0;\n let lastMetricsUpdate = 0;\n // Holds current frame's element positions\n let elementRects: ReturnType<typeof getElementRects> = [];\n\n const animate = (currentTime: number) => {\n if (lastTime === 0) {\n lastTime = currentTime;\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n\n const deltaTime = Math.min(currentTime - lastTime, 50);\n\n // Always track FPS so we have data when panel opens\n const now = performance.now();\n fpsFrames.current.push(now);\n fpsFrames.current = fpsFrames.current.filter(t => now - t < 1000);\n\n // Track detailed performance metrics\n metricsRef.current.rafGap = currentTime - lastTime;\n\n lastTime = currentTime;\n const dt = deltaTime / 16.67;\n\n const frameStartTime = performance.now();\n\n // Time canvas clear\n const clearStart = performance.now();\n\n // Reset transform to clear the entire viewport-sized canvas\n // We use the cached dpr scaling, so we clear the logical width/height\n const dpr = dprRef.current;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);\n\n // Translate the context to Simulate Scrolling\n // We move the 'world' up by scrollY, so absolute coordinates draw in the correct place relative to viewport\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n ctx.translate(-scrollX, -scrollY);\n\n metricsRef.current.clearTime = performance.now() - clearStart;\n\n const snowflakes = snowflakesRef.current;\n\n // PERFORMANCE: Update element rects EVERY FRAME to track layout changes and animations\n // getBoundingClientRect() is necessary to handle moving/animating elements\n // getBoundingClientRect() is fast enough for the accumulation targets (< 50 elements)\n const rectStart = performance.now();\n elementRects = getElementRects(accumulationRef.current);\n metricsRef.current.rectUpdateTime = performance.now() - rectStart;\n\n // Time physics\n const physicsStart = performance.now();\n meltAndSmoothAccumulation(elementRects, physicsConfigRef.current, dt);\n updateSnowflakes(\n snowflakes,\n elementRects,\n physicsConfigRef.current,\n dt,\n document.documentElement.scrollWidth,\n document.documentElement.scrollHeight\n );\n metricsRef.current.physicsTime = performance.now() - physicsStart;\n\n // Draw Snowflakes (batched for performance)\n const drawStart = performance.now();\n drawSnowflakes(ctx, snowflakes);\n\n // Spawn new snowflakes with adaptive rate based on performance\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const currentFps = fpsFrames.current.length;\n\n // Adaptive spawn rate: reduce load when FPS is low to prevent death spirals\n // Low FPS (<40): 20% spawn rate | Normal FPS: 100% spawn rate\n const shouldSpawn = currentFps >= 40 || Math.random() < 0.2;\n\n if (shouldSpawn) {\n const isBackground = Math.random() < 0.4;\n snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));\n }\n }\n\n // Draw Accumulation\n // We draw accumulations in World Space (by passing scroll offset to draw functions)\n // This aligns perfectly with the translated context and world-space snowflakes.\n\n // Viewport culling: Filter to only visible elements before drawing\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const visibleRects = elementRects.filter(({ rect }) =>\n rect.right >= 0 && rect.left <= viewportWidth &&\n rect.bottom >= 0 && rect.top <= viewportHeight\n );\n\n // Only call draw functions if there are visible elements\n if (visibleRects.length > 0) {\n drawAccumulations(ctx, visibleRects, scrollX, scrollY);\n drawSideAccumulations(ctx, visibleRects, scrollX, scrollY);\n }\n\n metricsRef.current.drawTime = performance.now() - drawStart;\n metricsRef.current.frameTime = performance.now() - frameStartTime;\n\n // Update metrics every 500ms\n if (currentTime - lastMetricsUpdate > 500) {\n setMetricsRef.current({\n fps: fpsFrames.current.length,\n frameTime: metricsRef.current.frameTime,\n scanTime: metricsRef.current.scanTime,\n rectUpdateTime: metricsRef.current.rectUpdateTime,\n surfaceCount: accumulationRef.current.size,\n flakeCount: snowflakes.length,\n maxFlakes: physicsConfigRef.current.MAX_FLAKES,\n rafGap: metricsRef.current.rafGap,\n clearTime: metricsRef.current.clearTime,\n physicsTime: metricsRef.current.physicsTime,\n drawTime: metricsRef.current.drawTime,\n });\n lastMetricsUpdate = currentTime;\n }\n\n animationIdRef.current = requestAnimationFrame(animate);\n };\n\n animationIdRef.current = requestAnimationFrame(animate);\n\n const handleResize = () => {\n resizeCanvas();\n accumulationRef.current.clear();\n initAccumulationWrapper();\n };\n\n window.addEventListener('resize', handleResize);\n\n // Periodic surface scan every 5 seconds to detect DOM changes\n const checkInterval = setInterval(initAccumulationWrapper, 5000);\n\n return () => {\n cancelAnimationFrame(animationIdRef.current);\n window.removeEventListener('resize', handleResize);\n clearInterval(checkInterval);\n windowResizeObserver.disconnect();\n surfaceObserver.disconnect();\n };\n }, [isMounted]);\n\n if (!isMounted) return null;\n\n return (\n <>\n <canvas\n ref={canvasRef}\n style={{\n position: 'fixed', // FIXED position to eliminate scroll jitter\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n willChange: 'transform',\n }}\n aria-hidden=\"true\"\n />\n\n </>\n );\n}\n","'use client';\n\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n\nexport interface PhysicsConfig {\n MAX_FLAKES: number;\n MELT_SPEED: number;\n WIND_STRENGTH: number;\n ACCUMULATION: {\n SIDE_RATE: number;\n TOP_RATE: number;\n BOTTOM_RATE: number;\n };\n MAX_DEPTH: {\n TOP: number;\n BOTTOM: number;\n SIDE: number;\n };\n FLAKE_SIZE: {\n MIN: number;\n MAX: number;\n };\n MAX_SURFACES: number;\n}\n\nexport const DEFAULT_PHYSICS: PhysicsConfig = {\n MAX_FLAKES: 1000,\n MELT_SPEED: 0.00001,\n WIND_STRENGTH: 1.5,\n ACCUMULATION: {\n SIDE_RATE: 1,\n TOP_RATE: 5,\n BOTTOM_RATE: 5.0,\n },\n MAX_DEPTH: {\n TOP: 100,\n BOTTOM: 50,\n SIDE: 20,\n },\n FLAKE_SIZE: {\n MIN: 0.5,\n MAX: 1.6,\n },\n MAX_SURFACES: 15\n};\n\nexport interface PerformanceMetrics {\n fps: number;\n frameTime: number;\n scanTime: number;\n rectUpdateTime: number;\n surfaceCount: number;\n flakeCount: number;\n maxFlakes: number;\n // Detailed metrics\n rafGap: number; // Time between requestAnimationFrame calls\n clearTime: number; // Time to clear canvas\n physicsTime: number; // Time for physics updates\n drawTime: number; // Time to draw snowflakes and accumulation\n}\n\ninterface SnowfallContextType {\n isEnabled: boolean;\n toggleSnow: () => void;\n physicsConfig: PhysicsConfig;\n updatePhysicsConfig: (config: Partial<PhysicsConfig>) => void;\n resetPhysics: () => void;\n debugMode: boolean;\n toggleDebug: () => void;\n metrics: PerformanceMetrics | null;\n setMetrics: (metrics: PerformanceMetrics) => void;\n}\n\nconst SnowfallContext = createContext<SnowfallContextType | undefined>(undefined);\n\nexport function SnowfallProvider({ children, initialDebug = false }: { children: ReactNode; initialDebug?: boolean }) {\n const [isEnabled, setIsEnabled] = useState(true);\n const [physicsConfig, setPhysicsConfig] = useState<PhysicsConfig>(DEFAULT_PHYSICS);\n const [debugMode, setDebugMode] = useState(initialDebug);\n const [metrics, setMetrics] = useState<PerformanceMetrics | null>(null);\n\n const toggleSnow = () => {\n setIsEnabled((prev) => !prev);\n };\n\n const toggleDebug = () => {\n setDebugMode((prev) => !prev);\n };\n\n const updatePhysicsConfig = (config: Partial<PhysicsConfig>) => {\n setPhysicsConfig((prev) => ({\n ...prev,\n ...config,\n ACCUMULATION: {\n ...prev.ACCUMULATION,\n ...(config.ACCUMULATION || {}),\n },\n MAX_DEPTH: {\n ...prev.MAX_DEPTH,\n ...(config.MAX_DEPTH || {}),\n },\n FLAKE_SIZE: {\n ...prev.FLAKE_SIZE,\n ...(config.FLAKE_SIZE || {}),\n },\n }));\n };\n\n const resetPhysics = () => {\n setPhysicsConfig(DEFAULT_PHYSICS);\n };\n\n return (\n <SnowfallContext.Provider value={{\n isEnabled,\n toggleSnow,\n physicsConfig,\n updatePhysicsConfig,\n resetPhysics,\n debugMode,\n toggleDebug,\n metrics,\n setMetrics,\n }}>\n {children}\n </SnowfallContext.Provider>\n );\n}\n\nexport function useSnowfall() {\n const context = useContext(SnowfallContext);\n if (context === undefined) {\n throw new Error('useSnowfall must be used within a SnowfallProvider');\n }\n return context;\n}\n","export const ATTR_SNOWFALL = 'data-snowfall';\n\nexport const VAL_IGNORE = 'ignore';\nexport const VAL_TOP = 'top';\nexport const VAL_BOTTOM = 'bottom';\n\nexport const TAG_HEADER = 'header';\nexport const TAG_FOOTER = 'footer';\n\nexport const ROLE_BANNER = 'banner';\nexport const ROLE_CONTENTINFO = 'contentinfo';\n\n// Mathematical constants\nexport const TAU = Math.PI * 2; // Full circle in radians\n","import { SnowAccumulation, ElementSurface, SnowfallSurface } from './types';\nimport {\n ATTR_SNOWFALL, VAL_IGNORE, VAL_TOP, VAL_BOTTOM,\n TAG_HEADER, TAG_FOOTER, ROLE_BANNER, ROLE_CONTENTINFO\n} from './constants';\n\n// Headers: snow accumulates on BOTTOM edge\n// Footers: default to TOP surface (snow piles on top)\n// Use data-snowfall attributes to override this behavior\nconst BOTTOM_TAGS = [TAG_HEADER];\nconst BOTTOM_ROLES = [ROLE_BANNER];\n\nconst AUTO_DETECT_TAGS = [TAG_HEADER, TAG_FOOTER, 'article', 'section', 'aside', 'nav'];\nconst AUTO_DETECT_ROLES = [`[role=\"${ROLE_BANNER}\"]`, `[role=\"${ROLE_CONTENTINFO}\"]`, '[role=\"main\"]'];\nconst AUTO_DETECT_CLASSES = [\n '.card', '[class*=\"card\"]', '[class*=\"Card\"]',\n '[class*=\"bg-\"]', '[class*=\"shadow-\"]', '[class*=\"rounded-\"]'\n];\n\n// Helper to get element type (Top vs Bottom surface) based on tags/roles\n// This is used to determine if snow should sit ON TOP or hang from the BOTTOM.\n\nexport const getElementType = (el: Element): SnowfallSurface => {\n const tagName = el.tagName.toLowerCase();\n if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;\n\n const role = el.getAttribute('role');\n if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;\n\n // Default: snow accumulates on top of elements (natural physics)\n return VAL_TOP;\n};\n\nconst shouldAccumulate = (el: Element, precomputedStyle?: CSSStyleDeclaration): boolean => {\n if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;\n // Explicit opt-in\n if (el.hasAttribute(ATTR_SNOWFALL)) return true;\n\n const styles = precomputedStyle || window.getComputedStyle(el);\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n if (!isVisible) return false;\n\n // Check for visual prominence\n const bgColor = styles.backgroundColor;\n const hasBackground = bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent';\n const hasBorder = (parseFloat(styles.borderWidth) > 0 && styles.borderColor !== 'transparent' && styles.borderColor !== 'rgba(0, 0, 0, 0)') && styles.borderStyle !== 'none';\n const hasBoxShadow = styles.boxShadow !== 'none';\n const hasFilter = styles.filter !== 'none' && styles.filter.includes('drop-shadow');\n const hasBackdropFilter = styles.backdropFilter !== 'none';\n\n return hasBackground || hasBorder || hasBoxShadow || hasFilter || hasBackdropFilter;\n};\n\nexport const getAccumulationSurfaces = (maxSurfaces: number = 5): { el: Element; type: SnowfallSurface }[] => {\n // No explicit clearing needed as we don't cache styles persistently anymore.\n const surfaces: { el: Element; type: SnowfallSurface }[] = [];\n const seen = new Set<Element>();\n\n const candidates = document.querySelectorAll(\n [\n `[${ATTR_SNOWFALL}]`,\n ...AUTO_DETECT_TAGS,\n ...AUTO_DETECT_ROLES,\n ...AUTO_DETECT_CLASSES\n ].join(', ')\n );\n\n for (const el of candidates) {\n if (surfaces.length >= maxSurfaces) break;\n if (seen.has(el)) continue;\n\n // Manual override check first\n const manualOverride = el.getAttribute(ATTR_SNOWFALL);\n if (manualOverride === VAL_IGNORE) continue;\n\n // If manually opted in, skip some heuristic checks but keep basic visibility/size sanity\n const isManuallyIncluded = manualOverride !== null;\n const styles = window.getComputedStyle(el);\n\n // Visibility Check: Must be visible and opaque enough\n const isVisible = styles.display !== 'none' && styles.visibility !== 'hidden' && parseFloat(styles.opacity) > 0.1;\n\n if (!isVisible && !isManuallyIncluded) continue;\n\n const rect = el.getBoundingClientRect();\n const hasSize = rect.width >= 100 && rect.height >= 50;\n if (!hasSize && !isManuallyIncluded) continue;\n\n const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;\n const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());\n const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute('role') || '');\n const isBottomSurface = isBottomTag || isBottomRole || manualOverride === VAL_BOTTOM;\n\n if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) continue;\n\n if (shouldAccumulate(el, styles)) {\n let type: SnowfallSurface = getElementType(el);\n if (manualOverride === VAL_BOTTOM) type = VAL_BOTTOM;\n else if (manualOverride === VAL_TOP) type = VAL_TOP;\n\n surfaces.push({ el, type });\n seen.add(el);\n }\n }\n\n return surfaces;\n};\n\nexport const getElementRects = (accumulationMap: Map<Element, SnowAccumulation>): ElementSurface[] => {\n const elementRects: ElementSurface[] = [];\n for (const [el, acc] of accumulationMap.entries()) {\n if (!el.isConnected) continue;\n // PURE VIEWPORT RECT. No scroll math.\n const rect = el.getBoundingClientRect();\n elementRects.push({ el, rect, acc });\n }\n return elementRects;\n};\n","import { PhysicsConfig } from '../../SnowfallProvider';\nimport { Snowflake, SnowAccumulation, ElementSurface } from './types';\nimport { getAccumulationSurfaces } from './dom';\nimport { VAL_BOTTOM, TAU } from './constants';\n\n// Opacity buckets for batched rendering (reduce globalAlpha state changes)\nconst OPACITY_BUCKETS = [0.3, 0.5, 0.7, 0.9];\n\n/**\n * Quantize opacity to nearest bucket for efficient batching during draw.\n * This eliminates runtime bucketing overhead by snapping at creation time.\n */\nconst quantizeOpacity = (opacity: number): number => {\n return OPACITY_BUCKETS.reduce((prev, curr) =>\n Math.abs(curr - opacity) < Math.abs(prev - opacity) ? curr : prev\n );\n};\n\nexport const createSnowflake = (\n worldWidth: number,\n config: PhysicsConfig,\n isBackground = false\n): Snowflake => {\n // Two random calls per flake:\n // 1) horizontal position\n // 2) DNA seed for all other traits\n const x = Math.random() * worldWidth;\n const dna = Math.random();\n\n // Pseudo-random trait extraction from DNA\n const noise = {\n speed: (dna * 13) % 1,\n wind: (dna * 7) % 1,\n wobblePhase: (dna * 23) % 1,\n wobbleSpeed: (dna * 5) % 1\n };\n\n const { MIN, MAX } = config.FLAKE_SIZE;\n const sizeRatio = dna;\n\n // Background vs foreground tuning\n const profile = isBackground\n ? {\n sizeMin: MIN * 0.6,\n sizeRange: (MAX - MIN) * 0.4,\n speedBase: 0.2,\n speedScale: 0.3,\n noiseSpeedScale: 0.2,\n windScale: config.WIND_STRENGTH * 0.625,\n opacityBase: 0.2,\n opacityScale: 0.2,\n wobbleBase: 0.005,\n wobbleScale: 0.015\n }\n : {\n sizeMin: MIN,\n sizeRange: MAX - MIN,\n speedBase: 0.5,\n speedScale: 0.5,\n noiseSpeedScale: 0.3,\n windScale: config.WIND_STRENGTH,\n opacityBase: 0.5,\n opacityScale: 0.3,\n wobbleBase: 0.01,\n wobbleScale: 0.02\n };\n\n const radius = profile.sizeMin + sizeRatio * profile.sizeRange;\n const glowRadius = radius * 1.5;\n\n // Calculate opacity and quantize to bucket for efficient batched rendering\n const rawOpacity = profile.opacityBase + sizeRatio * profile.opacityScale;\n const opacity = quantizeOpacity(rawOpacity);\n\n // Pre-calculate glow opacity (also quantized for batching)\n const rawGlowOpacity = opacity * 0.2;\n const glowOpacity = quantizeOpacity(rawGlowOpacity);\n\n return {\n x: x,\n y: window.scrollY - 5,\n radius: radius,\n glowRadius: glowRadius,\n speed:\n radius * profile.speedScale +\n noise.speed * profile.noiseSpeedScale +\n profile.speedBase,\n wind: (noise.wind - 0.5) * profile.windScale,\n opacity: opacity,\n glowOpacity: glowOpacity,\n wobble: noise.wobblePhase * TAU,\n wobbleSpeed: noise.wobbleSpeed * profile.wobbleScale + profile.wobbleBase,\n sizeRatio: sizeRatio,\n isBackground: isBackground\n };\n};\n\n/**\n * Initialize max heights array for snow accumulation with edge tapering and smoothing.\n * Exported for benchmarking.\n */\nexport const initializeMaxHeights = (\n width: number,\n baseMax: number,\n borderRadius: number,\n isBottom: boolean = false\n): number[] => {\n let maxHeights = new Array(width);\n for (let i = 0; i < width; i++) {\n let edgeFactor = 1.0;\n if (!isBottom && borderRadius > 0) {\n if (i < borderRadius) {\n edgeFactor = Math.pow(i / borderRadius, 1.2);\n } else if (i > width - borderRadius) {\n edgeFactor = Math.pow((width - i) / borderRadius, 1.2);\n }\n }\n maxHeights[i] = baseMax * edgeFactor * (0.85 + Math.random() * 0.15);\n }\n\n const smoothPasses = 4;\n for (let p = 0; p < smoothPasses; p++) {\n const smoothed = [...maxHeights];\n for (let i = 1; i < width - 1; i++) {\n smoothed[i] = (maxHeights[i - 1] + maxHeights[i] + maxHeights[i + 1]) / 3;\n }\n maxHeights = smoothed;\n }\n\n return maxHeights;\n};\n\nconst calculateCurveOffsets = (width: number, borderRadius: number, isBottom: boolean): number[] => {\n const offsets = new Array(width).fill(0);\n if (borderRadius <= 0 || isBottom) return offsets;\n\n for (let x = 0; x < width; x++) {\n let offset = 0;\n if (x < borderRadius) {\n const dist = borderRadius - x;\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n } else if (x > width - borderRadius) {\n const dist = x - (width - borderRadius);\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n }\n offsets[x] = offset;\n }\n return offsets;\n};\n\nconst calculateGravityMultipliers = (height: number): number[] => {\n const multipliers = new Array(height);\n for (let i = 0; i < height; i++) {\n const ratio = i / height;\n multipliers[i] = Math.sqrt(ratio);\n }\n return multipliers;\n};\n\nexport const initializeAccumulation = (\n accumulationMap: Map<Element, SnowAccumulation>,\n config: PhysicsConfig\n) => {\n // Scan DOM for new valid surfaces\n const elements = getAccumulationSurfaces(config.MAX_SURFACES);\n // Prune disconnected elements\n for (const [el] of accumulationMap.entries()) {\n if (!el.isConnected) {\n accumulationMap.delete(el);\n }\n }\n\n elements.forEach(({ el, type }) => {\n const existing = accumulationMap.get(el);\n const rect = el.getBoundingClientRect();\n const width = Math.ceil(rect.width);\n const isBottom = type === VAL_BOTTOM;\n\n if (existing && existing.heights.length === width) {\n existing.type = type;\n if (existing.borderRadius !== undefined) {\n const styleBuffer = window.getComputedStyle(el);\n existing.borderRadius = parseFloat(styleBuffer.borderTopLeftRadius) || 0;\n existing.curveOffsets = calculateCurveOffsets(width, existing.borderRadius, isBottom);\n // Initialize gravity multipliers if height matches but they're missing\n if (existing.leftSide.length === Math.ceil(rect.height) && !existing.sideGravityMultipliers) {\n existing.sideGravityMultipliers = calculateGravityMultipliers(existing.leftSide.length);\n }\n }\n return;\n }\n\n const height = Math.ceil(rect.height);\n const baseMax = isBottom ? config.MAX_DEPTH.BOTTOM : config.MAX_DEPTH.TOP;\n\n const styles = window.getComputedStyle(el);\n const borderRadius = parseFloat(styles.borderTopLeftRadius) || 0;\n\n const maxHeights = initializeMaxHeights(width, baseMax, borderRadius, isBottom);\n\n accumulationMap.set(el, {\n heights: existing?.heights.length === width ? existing.heights : new Array(width).fill(0),\n maxHeights,\n leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),\n rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),\n maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,\n borderRadius,\n curveOffsets: calculateCurveOffsets(width, borderRadius, isBottom),\n sideGravityMultipliers: calculateGravityMultipliers(height),\n type\n });\n });\n};\n\nexport const accumulateSide = (\n sideArray: number[],\n rectHeight: number,\n localY: number,\n maxSideHeight: number,\n borderRadius: number,\n config: PhysicsConfig\n) => {\n const spread = 4;\n const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);\n\n for (let dy = -spread; dy <= spread; dy++) {\n const y = localY + dy;\n if (y >= 0 && y < sideArray.length) {\n const inTop = y < borderRadius;\n const inBottom = y > rectHeight - borderRadius;\n if (borderRadius > 0 && (inTop || inBottom)) continue;\n\n const normalizedDist = Math.abs(dy) / spread;\n const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;\n sideArray[y] = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);\n }\n }\n};\n\nexport const updateSnowflakes = (\n snowflakes: Snowflake[],\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number,\n worldWidth: number,\n worldHeight: number\n) => {\n // Scroll used for World -> Viewport mapping for collision\n // Flakes are in World Space.\n // DOM Rects are in Viewport Space (relative to the window).\n // To check collision, we subtract scrollX/Y from Flake coordinates to get their Viewport position.\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (let i = snowflakes.length - 1; i >= 0; i--) {\n const flake = snowflakes[i];\n\n flake.wobble += flake.wobbleSpeed * dt;\n flake.x += (flake.wind + Math.sin(flake.wobble) * 0.5) * dt;\n flake.y += (flake.speed + Math.cos(flake.wobble * 0.5) * 0.1) * dt;\n\n let landed = false;\n\n for (const item of elementRects) {\n const { rect, acc } = item;\n const isBottom = acc.type === VAL_BOTTOM;\n\n // Flake is World Space. Rect is Viewport Space.\n // Map Flake to Viewport Relative for collision check vs Rect\n const flakeViewportX = flake.x - scrollX;\n const flakeViewportY = flake.y - scrollY;\n\n // Simple Collision Check\n const isInVerticalBounds = flakeViewportY >= rect.top && flakeViewportY <= rect.bottom;\n\n // Side collisions\n // Check if flake hits the left or right edge of the element\n if (!landed && acc.maxSideHeight > 0 && !isBottom) {\n if (isInVerticalBounds) {\n const localY = Math.floor(flakeViewportY - rect.top);\n const borderRadius = acc.borderRadius;\n\n const isInTopCorner = localY < borderRadius;\n const isInBottomCorner = localY > rect.height - borderRadius;\n const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);\n\n if (flakeViewportX >= rect.left - 5 && flakeViewportX < rect.left + 3) {\n if (!isCorner) {\n accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);\n landed = true;\n }\n }\n\n if (!landed && flakeViewportX > rect.right - 3 && flakeViewportX <= rect.right + 5) {\n if (!isCorner) {\n accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);\n landed = true;\n }\n }\n\n if (landed) break;\n }\n }\n\n // Top/Bottom accumulation\n // Check if flake hits the primary horizontal surface\n if (flakeViewportX >= rect.left && flakeViewportX <= rect.right) {\n const localX = Math.floor(flakeViewportX - rect.left);\n const currentHeight = acc.heights[localX] || 0;\n const maxHeight = acc.maxHeights[localX] || 5;\n\n const surfaceY = isBottom ? rect.bottom - currentHeight : rect.top - currentHeight;\n\n if (flakeViewportY >= surfaceY && flakeViewportY < surfaceY + 10 && currentHeight < maxHeight) {\n const shouldAccumulate = isBottom ? Math.random() < 0.15 : true;\n\n if (shouldAccumulate) {\n const baseSpread = Math.ceil(flake.radius);\n const spread = baseSpread + Math.floor(Math.random() * 2);\n const accumRate = isBottom ? config.ACCUMULATION.BOTTOM_RATE : config.ACCUMULATION.TOP_RATE;\n const centerOffset = Math.floor(Math.random() * 3) - 1;\n\n for (let dx = -spread; dx <= spread; dx++) {\n if (Math.random() < 0.15) continue;\n const idx = localX + dx + centerOffset;\n if (idx >= 0 && idx < acc.heights.length) {\n const dist = Math.abs(dx);\n const pixelMax = acc.maxHeights[idx] || 5;\n\n const normDist = dist / spread;\n const falloff = (Math.cos(normDist * Math.PI) + 1) / 2;\n const baseAdd = 0.3 * falloff;\n\n const randomFactor = 0.8 + Math.random() * 0.4;\n const addHeight = baseAdd * randomFactor * accumRate;\n\n if (acc.heights[idx] < pixelMax && addHeight > 0) {\n acc.heights[idx] = Math.min(pixelMax, acc.heights[idx] + addHeight);\n }\n }\n }\n\n if (isBottom) {\n landed = true;\n break;\n }\n }\n\n if (!isBottom) {\n landed = true;\n break;\n }\n }\n }\n }\n\n if (landed || flake.y > worldHeight + 10 || flake.x < -20 || flake.x > worldWidth + 20) {\n snowflakes.splice(i, 1);\n }\n }\n};\n\nexport const meltAndSmoothAccumulation = (\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number\n) => {\n for (const { acc } of elementRects) {\n const meltRate = config.MELT_SPEED * dt;\n const len = acc.heights.length;\n\n // Smooth\n if (len > 2) {\n for (let i = 1; i < len - 1; i++) {\n if (acc.heights[i] > 0.05) {\n const avg = (acc.heights[i - 1] + acc.heights[i + 1]) / 2;\n acc.heights[i] = acc.heights[i] * 0.99 + avg * 0.01;\n }\n }\n }\n\n // Melt\n for (let i = 0; i < acc.heights.length; i++) {\n if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);\n }\n for (let i = 0; i < acc.leftSide.length; i++) {\n if (acc.leftSide[i] > 0) acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);\n if (acc.rightSide[i] > 0) acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);\n }\n }\n};\n","import { Snowflake, ElementSurface } from './types';\nimport { VAL_BOTTOM, TAU } from './constants';\n\nconst ACC_FILL_STYLE = 'rgba(255, 255, 255, 0.95)';\nconst ACC_SHADOW_COLOR = 'rgba(200, 230, 255, 0.6)';\n\n\n/**\n * Single-pass snowflake rendering for optimal cache locality.\n * Flakes are tracked in World Space.\n * The Canvas Context is translated by (-scrollX, -scrollY), effectively viewing the World.\n * \n * PERFORMANCE: All glow/core values are pre-calculated at creation time (physics.ts),\n * enabling a simple single-pass render with excellent cache performance.\n */\nexport const drawSnowflakes = (ctx: CanvasRenderingContext2D, flakes: Snowflake[]) => {\n if (flakes.length === 0) return;\n\n ctx.fillStyle = '#FFFFFF';\n\n // Single pass: Draw glow (behind) then core (front) for each flake\n for (const flake of flakes) {\n // Draw glow effect first (behind the core) - skip for background flakes\n if (!flake.isBackground) {\n ctx.globalAlpha = flake.glowOpacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.glowRadius, 0, TAU);\n ctx.fill();\n }\n\n // Draw core on top\n ctx.globalAlpha = flake.opacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius, 0, TAU);\n ctx.fill();\n }\n\n // Reset alpha once at the end\n ctx.globalAlpha = 1.0;\n};\n\nexport const drawAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 4;\n ctx.shadowOffsetY = -1;\n // Explicitly reset alpha as we modified it in drawSnowflake\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n // Iterate over all accumulation surfaces to build the single path\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (!acc.heights.some(h => h > 0.1)) continue;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\n\n // Precompute world-space coordinates (hoist additions out of loops)\n const worldLeft = rect.left + scrollX;\n const worldBaseY = baseY + scrollY;\n\n let first = true;\n const step = 2;\n const len = acc.heights.length;\n\n // Draw the uneven top surface of the snow\n for (let x = 0; x < len; x += step) {\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n if (first) {\n ctx.moveTo(px, py);\n first = false;\n } else {\n ctx.lineTo(px, py);\n }\n }\n\n if ((len - 1) % step !== 0) {\n const x = len - 1;\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n // Draw the bottom edge (aligned with element border) to close the shape\n for (let x = len - 1; x >= 0; x -= step) {\n const px = worldLeft + x;\n const py = worldBaseY + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = worldLeft + startX;\n const startPy = worldBaseY + (acc.curveOffsets[startX] || 0);\n ctx.lineTo(startPx, startPy);\n }\n\n // Fill all accumulations in one pass to batch shadow rendering\n ctx.fill();\n\n ctx.shadowBlur = 0;\n ctx.shadowOffsetY = 0;\n};\n\nexport const drawSideAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 3;\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n const drawSide = (sideArray: number[], isLeft: boolean, multipliers: number[], rect: DOMRect, dx: number, dy: number) => {\n const baseX = isLeft ? rect.left : rect.right;\n\n // Precompute world-space coordinates\n const worldBaseX = baseX + dx;\n const worldTop = rect.top + dy;\n const worldBottom = rect.bottom + dy;\n\n ctx.moveTo(worldBaseX, worldTop);\n\n // Draw the uneven side profile\n for (let y = 0; y < sideArray.length; y += 2) {\n const width = sideArray[y] || 0;\n const nextY = Math.min(y + 2, sideArray.length - 1);\n const nextWidth = sideArray[nextY] || 0;\n\n const gravityMultiplier = multipliers[y] || 0;\n\n const py = worldTop + y;\n const px = (isLeft ? worldBaseX - (width * gravityMultiplier) : worldBaseX + (width * gravityMultiplier));\n const ny = worldTop + nextY;\n const nGravityMultiplier = multipliers[nextY] || 0;\n const nx = (isLeft ? worldBaseX - (nextWidth * nGravityMultiplier) : worldBaseX + (nextWidth * nGravityMultiplier));\n\n ctx.lineTo(px, py);\n ctx.lineTo(nx, ny);\n }\n\n // Close at the bottom\n ctx.lineTo(worldBaseX, worldBottom);\n };\n\n // Scan elements and append their side snow profiles to the current path for batched rendering\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (acc.maxSideHeight === 0) continue;\n\n const hasLeftSnow = acc.leftSide.some(h => h > 0.3);\n const hasRightSnow = acc.rightSide.some(h => h > 0.3);\n\n if (!hasLeftSnow && !hasRightSnow) continue;\n\n if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n }\n\n ctx.fill();\n\n ctx.shadowBlur = 0;\n};\n","'use client';\n\nimport { useSnowfall } from './SnowfallProvider';\nimport { useEffect, useState } from 'react';\n\nexport default function DebugPanel({ defaultOpen = true }: { defaultOpen?: boolean }) {\n const { debugMode, toggleDebug, metrics } = useSnowfall();\n const [isMinimized, setIsMinimized] = useState(!defaultOpen);\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.shiftKey && e.key === 'D') {\n toggleDebug();\n }\n };\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleDebug]);\n\n const copyToClipboard = () => {\n if (metrics) {\n const data = {\n ...metrics,\n timestamp: new Date().toISOString(),\n userAgent: navigator.userAgent,\n canvasSize: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n };\n navigator.clipboard.writeText(JSON.stringify(data, null, 2));\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n };\n\n if (!debugMode) return null;\n\n return (\n <div\n data-snowfall=\"top\"\n style={{\n position: 'fixed',\n bottom: '80px',\n left: '24px',\n backgroundColor: 'rgba(15, 23, 42, 0.75)',\n backdropFilter: 'blur(16px)',\n WebkitBackdropFilter: 'blur(16px)',\n color: '#e2e8f0',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',\n fontSize: '12px',\n padding: isMinimized ? '12px' : '20px',\n borderRadius: '16px',\n zIndex: 10000,\n minWidth: isMinimized ? 'auto' : '300px',\n maxWidth: '100%',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.2), 0 8px 10px -6px rgba(0, 0, 0, 0.2)',\n transition: 'all 0.2s ease',\n }}>\n <div style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: isMinimized ? 0 : '16px',\n gap: '16px'\n }}>\n <div style={{ fontWeight: '600', color: '#fff', display: 'flex', alignItems: 'center', gap: '8px' }}>\n <span style={{ fontSize: '14px' }}>❄️</span>\n <span style={{\n background: 'linear-gradient(to right, #60a5fa, #22d3ee)',\n WebkitBackgroundClip: 'text',\n WebkitTextFillColor: 'transparent',\n fontWeight: '700'\n }}>DEBUG</span>\n </div>\n <div style={{ display: 'flex', gap: '8px' }}>\n <button\n onClick={() => setIsMinimized(!isMinimized)}\n style={{\n background: 'rgba(255, 255, 255, 0.1)',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n color: '#fff',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '10px',\n }}\n >\n {isMinimized ? '▲' : '▼'}\n </button>\n <button\n onClick={toggleDebug}\n style={{\n background: 'rgba(239, 68, 68, 0.15)',\n border: '1px solid rgba(239, 68, 68, 0.2)',\n color: '#f87171',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '12px',\n }}\n >\n ✕\n </button>\n </div>\n </div>\n\n {!isMinimized && metrics && (\n <>\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Performance\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>FPS</span>\n <span style={{ fontWeight: 'bold', color: metrics.fps < 30 ? '#f87171' : metrics.fps < 50 ? '#facc15' : '#4ade80' }}>\n {metrics.fps.toFixed(1)}\n </span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Frame Time</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.frameTime.toFixed(2)}ms</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: metrics.rafGap && metrics.rafGap > 20 ? '#fbbf24' : 'inherit' }}>rAF Gap</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.rafGap?.toFixed(1) || 0}ms</span>\n </div>\n </div>\n\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Detailed Timings\n </div>\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '4px 12px' }}>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Clear</span> <span style={{ fontFamily: 'monospace' }}>{metrics.clearTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Physics</span> <span style={{ fontFamily: 'monospace' }}>{metrics.physicsTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Draw</span> <span style={{ fontFamily: 'monospace' }}>{metrics.drawTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Scan</span> <span style={{ fontFamily: 'monospace' }}>{metrics.scanTime.toFixed(2)}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between', gridColumn: 'span 2' }}><span>Rect Update</span> <span style={{ fontFamily: 'monospace' }}>{metrics.rectUpdateTime.toFixed(2)}ms</span></div>\n </div>\n </div>\n\n <div style={{ marginBottom: '16px' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Counts\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Snowflakes</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.flakeCount} / {metrics.maxFlakes}</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span>Surfaces</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.surfaceCount}</span>\n </div>\n </div>\n\n <button\n onClick={copyToClipboard}\n style={{\n width: '100%',\n padding: '10px',\n background: copied ? 'rgba(34, 197, 94, 0.2)' : 'rgba(255, 255, 255, 0.05)',\n border: copied ? '1px solid rgba(34, 197, 94, 0.5)' : '1px solid rgba(255, 255, 255, 0.1)',\n color: copied ? '#4ade80' : '#fff',\n cursor: 'pointer',\n borderRadius: '8px',\n fontSize: '11px',\n fontWeight: '600',\n transition: 'all 0.2s',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '6px'\n }}\n >\n {copied ? '✓ COPIED' : '📋 COPY METRICS'}\n </button>\n\n <div style={{ marginTop: '12px', fontSize: '10px', color: '#64748b', textAlign: 'center' }}>\n Shift+D to toggle\n </div>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;AAEA,SAAS,WAAW,QAAQ,YAAAA,iBAAgB;;;ACA5C,SAAgB,eAAe,YAAY,gBAA2B;AA+G9D;AAxFD,IAAM,kBAAiC;AAAA,EAC1C,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,cAAc;AAAA,IACV,WAAW;AAAA,IACX,UAAU;AAAA,IACV,aAAa;AAAA,EACjB;AAAA,EACA,WAAW;AAAA,IACP,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,MAAM;AAAA,EACV;AAAA,EACA,YAAY;AAAA,IACR,KAAK;AAAA,IACL,KAAK;AAAA,EACT;AAAA,EACA,cAAc;AAClB;AA6BA,IAAM,kBAAkB,cAA+C,MAAS;AAEzE,SAAS,iBAAiB,EAAE,UAAU,eAAe,MAAM,GAAoD;AAClH,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,IAAI;AAC/C,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAwB,eAAe;AACjF,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,YAAY;AACvD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAoC,IAAI;AAEtE,QAAM,aAAa,MAAM;AACrB,iBAAa,CAAC,SAAS,CAAC,IAAI;AAAA,EAChC;AAEA,QAAM,cAAc,MAAM;AACtB,iBAAa,CAAC,SAAS,CAAC,IAAI;AAAA,EAChC;AAEA,QAAM,sBAAsB,CAAC,WAAmC;AAC5D,qBAAiB,CAAC,UAAU;AAAA,MACxB,GAAG;AAAA,MACH,GAAG;AAAA,MACH,cAAc;AAAA,QACV,GAAG,KAAK;AAAA,QACR,GAAI,OAAO,gBAAgB,CAAC;AAAA,MAChC;AAAA,MACA,WAAW;AAAA,QACP,GAAG,KAAK;AAAA,QACR,GAAI,OAAO,aAAa,CAAC;AAAA,MAC7B;AAAA,MACA,YAAY;AAAA,QACR,GAAG,KAAK;AAAA,QACR,GAAI,OAAO,cAAc,CAAC;AAAA,MAC9B;AAAA,IACJ,EAAE;AAAA,EACN;AAEA,QAAM,eAAe,MAAM;AACvB,qBAAiB,eAAe;AAAA,EACpC;AAEA,SACI,oBAAC,gBAAgB,UAAhB,EAAyB,OAAO;AAAA,IAC7B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACJ,GACK,UACL;AAER;AAEO,SAAS,cAAc;AAC1B,QAAM,UAAU,WAAW,eAAe;AAC1C,MAAI,YAAY,QAAW;AACvB,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACxE;AACA,SAAO;AACX;;;ACvIO,IAAM,gBAAgB;AAEtB,IAAM,aAAa;AACnB,IAAM,UAAU;AAChB,IAAM,aAAa;AAEnB,IAAM,aAAa;AACnB,IAAM,aAAa;AAEnB,IAAM,cAAc;AACpB,IAAM,mBAAmB;AAGzB,IAAM,MAAM,KAAK,KAAK;;;ACJ7B,IAAM,cAAc,CAAC,UAAU;AAC/B,IAAM,eAAe,CAAC,WAAW;AAEjC,IAAM,mBAAmB,CAAC,YAAY,YAAY,WAAW,WAAW,SAAS,KAAK;AACtF,IAAM,oBAAoB,CAAC,UAAU,WAAW,MAAM,UAAU,gBAAgB,MAAM,eAAe;AACrG,IAAM,sBAAsB;AAAA,EACxB;AAAA,EAAS;AAAA,EAAmB;AAAA,EAC5B;AAAA,EAAkB;AAAA,EAAsB;AAC5C;AAKO,IAAM,iBAAiB,CAAC,OAAiC;AAC5D,QAAM,UAAU,GAAG,QAAQ,YAAY;AACvC,MAAI,YAAY,SAAS,OAAO,EAAG,QAAO;AAE1C,QAAM,OAAO,GAAG,aAAa,MAAM;AACnC,MAAI,QAAQ,aAAa,SAAS,IAAI,EAAG,QAAO;AAGhD,SAAO;AACX;AAEA,IAAM,mBAAmB,CAAC,IAAa,qBAAoD;AACvF,MAAI,GAAG,aAAa,aAAa,MAAM,WAAY,QAAO;AAE1D,MAAI,GAAG,aAAa,aAAa,EAAG,QAAO;AAE3C,QAAM,SAAS,oBAAoB,OAAO,iBAAiB,EAAE;AAC7D,QAAM,YAAY,OAAO,YAAY,UACjC,OAAO,eAAe,YACtB,WAAW,OAAO,OAAO,IAAI;AACjC,MAAI,CAAC,UAAW,QAAO;AAGvB,QAAM,UAAU,OAAO;AACvB,QAAM,gBAAgB,YAAY,sBAAsB,YAAY;AACpE,QAAM,YAAa,WAAW,OAAO,WAAW,IAAI,KAAK,OAAO,gBAAgB,iBAAiB,OAAO,gBAAgB,sBAAuB,OAAO,gBAAgB;AACtK,QAAM,eAAe,OAAO,cAAc;AAC1C,QAAM,YAAY,OAAO,WAAW,UAAU,OAAO,OAAO,SAAS,aAAa;AAClF,QAAM,oBAAoB,OAAO,mBAAmB;AAEpD,SAAO,iBAAiB,aAAa,gBAAgB,aAAa;AACtE;AAEO,IAAM,0BAA0B,CAAC,cAAsB,MAAgD;AAE1G,QAAM,WAAqD,CAAC;AAC5D,QAAM,OAAO,oBAAI,IAAa;AAE9B,QAAM,aAAa,SAAS;AAAA,IACxB;AAAA,MACI,IAAI,aAAa;AAAA,MACjB,GAAG;AAAA,MACH,GAAG;AAAA,MACH,GAAG;AAAA,IACP,EAAE,KAAK,IAAI;AAAA,EACf;AAEA,aAAW,MAAM,YAAY;AACzB,QAAI,SAAS,UAAU,YAAa;AACpC,QAAI,KAAK,IAAI,EAAE,EAAG;AAGlB,UAAM,iBAAiB,GAAG,aAAa,aAAa;AACpD,QAAI,mBAAmB,WAAY;AAGnC,UAAM,qBAAqB,mBAAmB;AAC9C,UAAM,SAAS,OAAO,iBAAiB,EAAE;AAGzC,UAAM,YAAY,OAAO,YAAY,UAAU,OAAO,eAAe,YAAY,WAAW,OAAO,OAAO,IAAI;AAE9G,QAAI,CAAC,aAAa,CAAC,mBAAoB;AAEvC,UAAM,OAAO,GAAG,sBAAsB;AACtC,UAAM,UAAU,KAAK,SAAS,OAAO,KAAK,UAAU;AACpD,QAAI,CAAC,WAAW,CAAC,mBAAoB;AAErC,UAAM,oBAAoB,KAAK,OAAO,MAAM,KAAK,UAAU,OAAO,cAAc;AAChF,UAAM,cAAc,YAAY,SAAS,GAAG,QAAQ,YAAY,CAAC;AACjE,UAAM,eAAe,aAAa,SAAS,GAAG,aAAa,MAAM,KAAK,EAAE;AACxE,UAAM,kBAAkB,eAAe,gBAAgB,mBAAmB;AAE1E,QAAI,qBAAqB,CAAC,mBAAmB,CAAC,mBAAoB;AAElE,QAAI,iBAAiB,IAAI,MAAM,GAAG;AAC9B,UAAI,OAAwB,eAAe,EAAE;AAC7C,UAAI,mBAAmB,WAAY,QAAO;AAAA,eACjC,mBAAmB,QAAS,QAAO;AAE5C,eAAS,KAAK,EAAE,IAAI,KAAK,CAAC;AAC1B,WAAK,IAAI,EAAE;AAAA,IACf;AAAA,EACJ;AAEA,SAAO;AACX;AAEO,IAAM,kBAAkB,CAAC,oBAAsE;AAClG,QAAM,eAAiC,CAAC;AACxC,aAAW,CAAC,IAAI,GAAG,KAAK,gBAAgB,QAAQ,GAAG;AAC/C,QAAI,CAAC,GAAG,YAAa;AAErB,UAAM,OAAO,GAAG,sBAAsB;AACtC,iBAAa,KAAK,EAAE,IAAI,MAAM,IAAI,CAAC;AAAA,EACvC;AACA,SAAO;AACX;;;ACjHA,IAAM,kBAAkB,CAAC,KAAK,KAAK,KAAK,GAAG;AAM3C,IAAM,kBAAkB,CAAC,YAA4B;AACjD,SAAO,gBAAgB;AAAA,IAAO,CAAC,MAAM,SACjC,KAAK,IAAI,OAAO,OAAO,IAAI,KAAK,IAAI,OAAO,OAAO,IAAI,OAAO;AAAA,EACjE;AACJ;AAEO,IAAM,kBAAkB,CAC3B,YACA,QACA,eAAe,UACH;AAIZ,QAAM,IAAI,KAAK,OAAO,IAAI;AAC1B,QAAM,MAAM,KAAK,OAAO;AAGxB,QAAM,QAAQ;AAAA,IACV,OAAQ,MAAM,KAAM;AAAA,IACpB,MAAO,MAAM,IAAK;AAAA,IAClB,aAAc,MAAM,KAAM;AAAA,IAC1B,aAAc,MAAM,IAAK;AAAA,EAC7B;AAEA,QAAM,EAAE,KAAK,IAAI,IAAI,OAAO;AAC5B,QAAM,YAAY;AAGlB,QAAM,UAAU,eACV;AAAA,IACE,SAAS,MAAM;AAAA,IACf,YAAY,MAAM,OAAO;AAAA,IACzB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,WAAW,OAAO,gBAAgB;AAAA,IAClC,aAAa;AAAA,IACb,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,aAAa;AAAA,EACjB,IACE;AAAA,IACE,SAAS;AAAA,IACT,WAAW,MAAM;AAAA,IACjB,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,WAAW,OAAO;AAAA,IAClB,aAAa;AAAA,IACb,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,aAAa;AAAA,EACjB;AAEJ,QAAM,SAAS,QAAQ,UAAU,YAAY,QAAQ;AACrD,QAAM,aAAa,SAAS;AAG5B,QAAM,aAAa,QAAQ,cAAc,YAAY,QAAQ;AAC7D,QAAM,UAAU,gBAAgB,UAAU;AAG1C,QAAM,iBAAiB,UAAU;AACjC,QAAM,cAAc,gBAAgB,cAAc;AAElD,SAAO;AAAA,IACH;AAAA,IACA,GAAG,OAAO,UAAU;AAAA,IACpB;AAAA,IACA;AAAA,IACA,OACI,SAAS,QAAQ,aACjB,MAAM,QAAQ,QAAQ,kBACtB,QAAQ;AAAA,IACZ,OAAO,MAAM,OAAO,OAAO,QAAQ;AAAA,IACnC;AAAA,IACA;AAAA,IACA,QAAQ,MAAM,cAAc;AAAA,IAC5B,aAAa,MAAM,cAAc,QAAQ,cAAc,QAAQ;AAAA,IAC/D;AAAA,IACA;AAAA,EACJ;AACJ;AAMO,IAAM,uBAAuB,CAChC,OACA,SACA,cACA,WAAoB,UACT;AACX,MAAI,aAAa,IAAI,MAAM,KAAK;AAChC,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC5B,QAAI,aAAa;AACjB,QAAI,CAAC,YAAY,eAAe,GAAG;AAC/B,UAAI,IAAI,cAAc;AAClB,qBAAa,KAAK,IAAI,IAAI,cAAc,GAAG;AAAA,MAC/C,WAAW,IAAI,QAAQ,cAAc;AACjC,qBAAa,KAAK,KAAK,QAAQ,KAAK,cAAc,GAAG;AAAA,MACzD;AAAA,IACJ;AACA,eAAW,CAAC,IAAI,UAAU,cAAc,OAAO,KAAK,OAAO,IAAI;AAAA,EACnE;AAEA,QAAM,eAAe;AACrB,WAAS,IAAI,GAAG,IAAI,cAAc,KAAK;AACnC,UAAM,WAAW,CAAC,GAAG,UAAU;AAC/B,aAAS,IAAI,GAAG,IAAI,QAAQ,GAAG,KAAK;AAChC,eAAS,CAAC,KAAK,WAAW,IAAI,CAAC,IAAI,WAAW,CAAC,IAAI,WAAW,IAAI,CAAC,KAAK;AAAA,IAC5E;AACA,iBAAa;AAAA,EACjB;AAEA,SAAO;AACX;AAEA,IAAM,wBAAwB,CAAC,OAAe,cAAsB,aAAgC;AAChG,QAAM,UAAU,IAAI,MAAM,KAAK,EAAE,KAAK,CAAC;AACvC,MAAI,gBAAgB,KAAK,SAAU,QAAO;AAE1C,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC5B,QAAI,SAAS;AACb,QAAI,IAAI,cAAc;AAClB,YAAM,OAAO,eAAe;AAC5B,eAAS,eAAe,KAAK,KAAK,KAAK,IAAI,GAAG,eAAe,eAAe,OAAO,IAAI,CAAC;AAAA,IAC5F,WAAW,IAAI,QAAQ,cAAc;AACjC,YAAM,OAAO,KAAK,QAAQ;AAC1B,eAAS,eAAe,KAAK,KAAK,KAAK,IAAI,GAAG,eAAe,eAAe,OAAO,IAAI,CAAC;AAAA,IAC5F;AACA,YAAQ,CAAC,IAAI;AAAA,EACjB;AACA,SAAO;AACX;AAEA,IAAM,8BAA8B,CAAC,WAA6B;AAC9D,QAAM,cAAc,IAAI,MAAM,MAAM;AACpC,WAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC7B,UAAM,QAAQ,IAAI;AAClB,gBAAY,CAAC,IAAI,KAAK,KAAK,KAAK;AAAA,EACpC;AACA,SAAO;AACX;AAEO,IAAM,yBAAyB,CAClC,iBACA,WACC;AAED,QAAM,WAAW,wBAAwB,OAAO,YAAY;AAE5D,aAAW,CAAC,EAAE,KAAK,gBAAgB,QAAQ,GAAG;AAC1C,QAAI,CAAC,GAAG,aAAa;AACjB,sBAAgB,OAAO,EAAE;AAAA,IAC7B;AAAA,EACJ;AAEA,WAAS,QAAQ,CAAC,EAAE,IAAI,KAAK,MAAM;AAC/B,UAAM,WAAW,gBAAgB,IAAI,EAAE;AACvC,UAAM,OAAO,GAAG,sBAAsB;AACtC,UAAM,QAAQ,KAAK,KAAK,KAAK,KAAK;AAClC,UAAM,WAAW,SAAS;AAE1B,QAAI,YAAY,SAAS,QAAQ,WAAW,OAAO;AAC/C,eAAS,OAAO;AAChB,UAAI,SAAS,iBAAiB,QAAW;AACrC,cAAM,cAAc,OAAO,iBAAiB,EAAE;AAC9C,iBAAS,eAAe,WAAW,YAAY,mBAAmB,KAAK;AACvE,iBAAS,eAAe,sBAAsB,OAAO,SAAS,cAAc,QAAQ;AAEpF,YAAI,SAAS,SAAS,WAAW,KAAK,KAAK,KAAK,MAAM,KAAK,CAAC,SAAS,wBAAwB;AACzF,mBAAS,yBAAyB,4BAA4B,SAAS,SAAS,MAAM;AAAA,QAC1F;AAAA,MACJ;AACA;AAAA,IACJ;AAEA,UAAM,SAAS,KAAK,KAAK,KAAK,MAAM;AACpC,UAAM,UAAU,WAAW,OAAO,UAAU,SAAS,OAAO,UAAU;AAEtE,UAAM,SAAS,OAAO,iBAAiB,EAAE;AACzC,UAAM,eAAe,WAAW,OAAO,mBAAmB,KAAK;AAE/D,UAAM,aAAa,qBAAqB,OAAO,SAAS,cAAc,QAAQ;AAE9E,oBAAgB,IAAI,IAAI;AAAA,MACpB,SAAS,UAAU,QAAQ,WAAW,QAAQ,SAAS,UAAU,IAAI,MAAM,KAAK,EAAE,KAAK,CAAC;AAAA,MACxF;AAAA,MACA,UAAU,UAAU,SAAS,WAAW,SAAS,SAAS,WAAW,IAAI,MAAM,MAAM,EAAE,KAAK,CAAC;AAAA,MAC7F,WAAW,UAAU,UAAU,WAAW,SAAS,SAAS,YAAY,IAAI,MAAM,MAAM,EAAE,KAAK,CAAC;AAAA,MAChG,eAAe,WAAW,IAAI,OAAO,UAAU;AAAA,MAC/C;AAAA,MACA,cAAc,sBAAsB,OAAO,cAAc,QAAQ;AAAA,MACjE,wBAAwB,4BAA4B,MAAM;AAAA,MAC1D;AAAA,IACJ,CAAC;AAAA,EACL,CAAC;AACL;AAEO,IAAM,iBAAiB,CAC1B,WACA,YACA,QACA,eACA,cACA,WACC;AACD,QAAM,SAAS;AACf,QAAM,YAAY,OAAO,aAAa,aAAa,MAAM,KAAK,OAAO,IAAI;AAEzE,WAAS,KAAK,CAAC,QAAQ,MAAM,QAAQ,MAAM;AACvC,UAAM,IAAI,SAAS;AACnB,QAAI,KAAK,KAAK,IAAI,UAAU,QAAQ;AAChC,YAAM,QAAQ,IAAI;AAClB,YAAM,WAAW,IAAI,aAAa;AAClC,UAAI,eAAe,MAAM,SAAS,UAAW;AAE7C,YAAM,iBAAiB,KAAK,IAAI,EAAE,IAAI;AACtC,YAAM,WAAW,KAAK,IAAI,iBAAiB,KAAK,EAAE,IAAI,KAAK;AAC3D,gBAAU,CAAC,IAAI,KAAK,IAAI,eAAe,UAAU,CAAC,IAAI,YAAY,OAAO;AAAA,IAC7E;AAAA,EACJ;AACJ;AAEO,IAAM,mBAAmB,CAC5B,YACA,cACA,QACA,IACA,YACA,gBACC;AAKD,QAAM,UAAU,OAAO;AACvB,QAAM,UAAU,OAAO;AAEvB,WAAS,IAAI,WAAW,SAAS,GAAG,KAAK,GAAG,KAAK;AAC7C,UAAM,QAAQ,WAAW,CAAC;AAE1B,UAAM,UAAU,MAAM,cAAc;AACpC,UAAM,MAAM,MAAM,OAAO,KAAK,IAAI,MAAM,MAAM,IAAI,OAAO;AACzD,UAAM,MAAM,MAAM,QAAQ,KAAK,IAAI,MAAM,SAAS,GAAG,IAAI,OAAO;AAEhE,QAAI,SAAS;AAEb,eAAW,QAAQ,cAAc;AAC7B,YAAM,EAAE,MAAM,IAAI,IAAI;AACtB,YAAM,WAAW,IAAI,SAAS;AAI9B,YAAM,iBAAiB,MAAM,IAAI;AACjC,YAAM,iBAAiB,MAAM,IAAI;AAGjC,YAAM,qBAAqB,kBAAkB,KAAK,OAAO,kBAAkB,KAAK;AAIhF,UAAI,CAAC,UAAU,IAAI,gBAAgB,KAAK,CAAC,UAAU;AAC/C,YAAI,oBAAoB;AACpB,gBAAM,SAAS,KAAK,MAAM,iBAAiB,KAAK,GAAG;AACnD,gBAAM,eAAe,IAAI;AAEzB,gBAAM,gBAAgB,SAAS;AAC/B,gBAAM,mBAAmB,SAAS,KAAK,SAAS;AAChD,gBAAM,WAAW,eAAe,MAAM,iBAAiB;AAEvD,cAAI,kBAAkB,KAAK,OAAO,KAAK,iBAAiB,KAAK,OAAO,GAAG;AACnE,gBAAI,CAAC,UAAU;AACX,6BAAe,IAAI,UAAU,KAAK,QAAQ,QAAQ,IAAI,eAAe,cAAc,MAAM;AACzF,uBAAS;AAAA,YACb;AAAA,UACJ;AAEA,cAAI,CAAC,UAAU,iBAAiB,KAAK,QAAQ,KAAK,kBAAkB,KAAK,QAAQ,GAAG;AAChF,gBAAI,CAAC,UAAU;AACX,6BAAe,IAAI,WAAW,KAAK,QAAQ,QAAQ,IAAI,eAAe,cAAc,MAAM;AAC1F,uBAAS;AAAA,YACb;AAAA,UACJ;AAEA,cAAI,OAAQ;AAAA,QAChB;AAAA,MACJ;AAIA,UAAI,kBAAkB,KAAK,QAAQ,kBAAkB,KAAK,OAAO;AAC7D,cAAM,SAAS,KAAK,MAAM,iBAAiB,KAAK,IAAI;AACpD,cAAM,gBAAgB,IAAI,QAAQ,MAAM,KAAK;AAC7C,cAAM,YAAY,IAAI,WAAW,MAAM,KAAK;AAE5C,cAAM,WAAW,WAAW,KAAK,SAAS,gBAAgB,KAAK,MAAM;AAErE,YAAI,kBAAkB,YAAY,iBAAiB,WAAW,MAAM,gBAAgB,WAAW;AAC3F,gBAAMC,oBAAmB,WAAW,KAAK,OAAO,IAAI,OAAO;AAE3D,cAAIA,mBAAkB;AAClB,kBAAM,aAAa,KAAK,KAAK,MAAM,MAAM;AACzC,kBAAM,SAAS,aAAa,KAAK,MAAM,KAAK,OAAO,IAAI,CAAC;AACxD,kBAAM,YAAY,WAAW,OAAO,aAAa,cAAc,OAAO,aAAa;AACnF,kBAAM,eAAe,KAAK,MAAM,KAAK,OAAO,IAAI,CAAC,IAAI;AAErD,qBAAS,KAAK,CAAC,QAAQ,MAAM,QAAQ,MAAM;AACvC,kBAAI,KAAK,OAAO,IAAI,KAAM;AAC1B,oBAAM,MAAM,SAAS,KAAK;AAC1B,kBAAI,OAAO,KAAK,MAAM,IAAI,QAAQ,QAAQ;AACtC,sBAAM,OAAO,KAAK,IAAI,EAAE;AACxB,sBAAM,WAAW,IAAI,WAAW,GAAG,KAAK;AAExC,sBAAM,WAAW,OAAO;AACxB,sBAAM,WAAW,KAAK,IAAI,WAAW,KAAK,EAAE,IAAI,KAAK;AACrD,sBAAM,UAAU,MAAM;AAEtB,sBAAM,eAAe,MAAM,KAAK,OAAO,IAAI;AAC3C,sBAAM,YAAY,UAAU,eAAe;AAE3C,oBAAI,IAAI,QAAQ,GAAG,IAAI,YAAY,YAAY,GAAG;AAC9C,sBAAI,QAAQ,GAAG,IAAI,KAAK,IAAI,UAAU,IAAI,QAAQ,GAAG,IAAI,SAAS;AAAA,gBACtE;AAAA,cACJ;AAAA,YACJ;AAEA,gBAAI,UAAU;AACV,uBAAS;AACT;AAAA,YACJ;AAAA,UACJ;AAEA,cAAI,CAAC,UAAU;AACX,qBAAS;AACT;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAEA,QAAI,UAAU,MAAM,IAAI,cAAc,MAAM,MAAM,IAAI,OAAO,MAAM,IAAI,aAAa,IAAI;AACpF,iBAAW,OAAO,GAAG,CAAC;AAAA,IAC1B;AAAA,EACJ;AACJ;AAEO,IAAM,4BAA4B,CACrC,cACA,QACA,OACC;AACD,aAAW,EAAE,IAAI,KAAK,cAAc;AAChC,UAAM,WAAW,OAAO,aAAa;AACrC,UAAM,MAAM,IAAI,QAAQ;AAGxB,QAAI,MAAM,GAAG;AACT,eAAS,IAAI,GAAG,IAAI,MAAM,GAAG,KAAK;AAC9B,YAAI,IAAI,QAAQ,CAAC,IAAI,MAAM;AACvB,gBAAM,OAAO,IAAI,QAAQ,IAAI,CAAC,IAAI,IAAI,QAAQ,IAAI,CAAC,KAAK;AACxD,cAAI,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,OAAO,MAAM;AAAA,QACnD;AAAA,MACJ;AAAA,IACJ;AAGA,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,QAAQ,KAAK;AACzC,UAAI,IAAI,QAAQ,CAAC,IAAI,EAAG,KAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,QAAQ,CAAC,IAAI,QAAQ;AAAA,IAClF;AACA,aAAS,IAAI,GAAG,IAAI,IAAI,SAAS,QAAQ,KAAK;AAC1C,UAAI,IAAI,SAAS,CAAC,IAAI,EAAG,KAAI,SAAS,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,SAAS,CAAC,IAAI,QAAQ;AACjF,UAAI,IAAI,UAAU,CAAC,IAAI,EAAG,KAAI,UAAU,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI,QAAQ;AAAA,IACxF;AAAA,EACJ;AACJ;;;ACnYA,IAAM,iBAAiB;AACvB,IAAM,mBAAmB;AAWlB,IAAM,iBAAiB,CAAC,KAA+B,WAAwB;AAClF,MAAI,OAAO,WAAW,EAAG;AAEzB,MAAI,YAAY;AAGhB,aAAW,SAAS,QAAQ;AAExB,QAAI,CAAC,MAAM,cAAc;AACrB,UAAI,cAAc,MAAM;AACxB,UAAI,UAAU;AACd,UAAI,IAAI,MAAM,GAAG,MAAM,GAAG,MAAM,YAAY,GAAG,GAAG;AAClD,UAAI,KAAK;AAAA,IACb;AAGA,QAAI,cAAc,MAAM;AACxB,QAAI,UAAU;AACd,QAAI,IAAI,MAAM,GAAG,MAAM,GAAG,MAAM,QAAQ,GAAG,GAAG;AAC9C,QAAI,KAAK;AAAA,EACb;AAGA,MAAI,cAAc;AACtB;AAEO,IAAM,oBAAoB,CAC7B,KACA,cACA,SACA,YACC;AACD,MAAI,YAAY;AAChB,MAAI,cAAc;AAClB,MAAI,aAAa;AACjB,MAAI,gBAAgB;AAEpB,MAAI,cAAc;AAElB,MAAI,UAAU;AAGd,aAAW,QAAQ,cAAc;AAC7B,UAAM,EAAE,MAAM,IAAI,IAAI;AAEtB,QAAI,CAAC,IAAI,QAAQ,KAAK,OAAK,IAAI,GAAG,EAAG;AAErC,UAAM,WAAW,IAAI,SAAS;AAC9B,UAAM,QAAQ,WAAW,KAAK,SAAS,IAAI,KAAK,MAAM;AAGtD,UAAM,YAAY,KAAK,OAAO;AAC9B,UAAM,aAAa,QAAQ;AAE3B,QAAI,QAAQ;AACZ,UAAM,OAAO;AACb,UAAM,MAAM,IAAI,QAAQ;AAGxB,aAAS,IAAI,GAAG,IAAI,KAAK,KAAK,MAAM;AAChC,YAAM,SAAS,IAAI,QAAQ,CAAC,KAAK;AACjC,YAAM,KAAK,YAAY;AACvB,YAAM,KAAK,aAAa,UAAU,IAAI,aAAa,CAAC,KAAK;AACzD,UAAI,OAAO;AACP,YAAI,OAAO,IAAI,EAAE;AACjB,gBAAQ;AAAA,MACZ,OAAO;AACH,YAAI,OAAO,IAAI,EAAE;AAAA,MACrB;AAAA,IACJ;AAEA,SAAK,MAAM,KAAK,SAAS,GAAG;AACxB,YAAM,IAAI,MAAM;AAChB,YAAM,SAAS,IAAI,QAAQ,CAAC,KAAK;AACjC,YAAM,KAAK,YAAY;AACvB,YAAM,KAAK,aAAa,UAAU,IAAI,aAAa,CAAC,KAAK;AACzD,UAAI,OAAO,IAAI,EAAE;AAAA,IACrB;AAGA,aAAS,IAAI,MAAM,GAAG,KAAK,GAAG,KAAK,MAAM;AACrC,YAAM,KAAK,YAAY;AACvB,YAAM,KAAK,cAAc,IAAI,aAAa,CAAC,KAAK;AAChD,UAAI,OAAO,IAAI,EAAE;AAAA,IACrB;AAEA,UAAM,SAAS;AACf,UAAM,UAAU,YAAY;AAC5B,UAAM,UAAU,cAAc,IAAI,aAAa,MAAM,KAAK;AAC1D,QAAI,OAAO,SAAS,OAAO;AAAA,EAC/B;AAGA,MAAI,KAAK;AAET,MAAI,aAAa;AACjB,MAAI,gBAAgB;AACxB;AAEO,IAAM,wBAAwB,CACjC,KACA,cACA,SACA,YACC;AACD,MAAI,YAAY;AAChB,MAAI,cAAc;AAClB,MAAI,aAAa;AACjB,MAAI,cAAc;AAElB,MAAI,UAAU;AAEd,QAAM,WAAW,CAAC,WAAqB,QAAiB,aAAuB,MAAe,IAAY,OAAe;AACrH,UAAM,QAAQ,SAAS,KAAK,OAAO,KAAK;AAGxC,UAAM,aAAa,QAAQ;AAC3B,UAAM,WAAW,KAAK,MAAM;AAC5B,UAAM,cAAc,KAAK,SAAS;AAElC,QAAI,OAAO,YAAY,QAAQ;AAG/B,aAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,GAAG;AAC1C,YAAM,QAAQ,UAAU,CAAC,KAAK;AAC9B,YAAM,QAAQ,KAAK,IAAI,IAAI,GAAG,UAAU,SAAS,CAAC;AAClD,YAAM,YAAY,UAAU,KAAK,KAAK;AAEtC,YAAM,oBAAoB,YAAY,CAAC,KAAK;AAE5C,YAAM,KAAK,WAAW;AACtB,YAAM,KAAM,SAAS,aAAc,QAAQ,oBAAqB,aAAc,QAAQ;AACtF,YAAM,KAAK,WAAW;AACtB,YAAM,qBAAqB,YAAY,KAAK,KAAK;AACjD,YAAM,KAAM,SAAS,aAAc,YAAY,qBAAsB,aAAc,YAAY;AAE/F,UAAI,OAAO,IAAI,EAAE;AACjB,UAAI,OAAO,IAAI,EAAE;AAAA,IACrB;AAGA,QAAI,OAAO,YAAY,WAAW;AAAA,EACtC;AAGA,aAAW,QAAQ,cAAc;AAC7B,UAAM,EAAE,MAAM,IAAI,IAAI;AAEtB,QAAI,IAAI,kBAAkB,EAAG;AAE7B,UAAM,cAAc,IAAI,SAAS,KAAK,OAAK,IAAI,GAAG;AAClD,UAAM,eAAe,IAAI,UAAU,KAAK,OAAK,IAAI,GAAG;AAEpD,QAAI,CAAC,eAAe,CAAC,aAAc;AAEnC,QAAI,YAAa,UAAS,IAAI,UAAU,MAAM,IAAI,wBAAwB,MAAM,SAAS,OAAO;AAChG,QAAI,aAAc,UAAS,IAAI,WAAW,OAAO,IAAI,wBAAwB,MAAM,SAAS,OAAO;AAAA,EACvG;AAEA,MAAI,KAAK;AAET,MAAI,aAAa;AACrB;;;ALwGQ,mBACI,OAAAC,YADJ;AAhRO,SAAR,WAA4B;AAC/B,QAAM,EAAE,WAAW,eAAe,WAAW,IAAI,YAAY;AAC7D,QAAM,eAAe,OAAO,SAAS;AACrC,QAAM,mBAAmB,OAAO,aAAa;AAC7C,QAAM,gBAAgB,OAAO,UAAU;AACvC,QAAM,CAAC,WAAW,YAAY,IAAIC,UAAS,KAAK;AAChD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAEhD,QAAM,YAAY,OAA0B,IAAI;AAChD,QAAM,gBAAgB,OAAoB,CAAC,CAAC;AAC5C,QAAM,kBAAkB,OAAuC,oBAAI,IAAI,CAAC;AACxE,QAAM,iBAAiB,OAAe,CAAC;AAIvC,QAAM,SAAS,OAAO,CAAC;AAGvB,QAAM,YAAY,OAAiB,CAAC,CAAC;AACrC,QAAM,aAAa,OAAO;AAAA,IACtB,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,aAAa;AAAA,IACb,UAAU;AAAA,EACd,CAAC;AAED,YAAU,MAAM;AACZ,0BAAsB,MAAM,aAAa,IAAI,CAAC;AAAA,EAClD,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AACZ,iBAAa,UAAU;AAAA,EAC3B,GAAG,CAAC,SAAS,CAAC;AAEd,YAAU,MAAM;AACZ,qBAAiB,UAAU;AAAA,EAC/B,GAAG,CAAC,aAAa,CAAC;AAElB,YAAU,MAAM;AACZ,kBAAc,UAAU;AAAA,EAC5B,GAAG,CAAC,UAAU,CAAC;AAEf,YAAU,MAAM;AACZ,QAAI,CAAC,UAAW;AAEhB,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,OAAQ;AAEb,UAAM,MAAM,OAAO,WAAW,IAAI;AAClC,QAAI,CAAC,IAAK;AAEV,UAAM,eAAe,MAAM;AACvB,UAAI,UAAU,SAAS;AAEnB,cAAM,WAAW,OAAO;AACxB,cAAM,YAAY,OAAO;AAGzB,cAAM,MAAM,OAAO,oBAAoB;AACvC,eAAO,UAAU;AACjB,kBAAU,QAAQ,QAAQ,WAAW;AACrC,kBAAU,QAAQ,SAAS,YAAY;AAGvC,kBAAU,QAAQ,MAAM,QAAQ,GAAG,QAAQ;AAC3C,kBAAU,QAAQ,MAAM,SAAS,GAAG,SAAS;AAAA,MACjD;AAAA,IACJ;AACA,iBAAa;AAEb,UAAM,uBAAuB,IAAI,eAAe,MAAM;AAClD,mBAAa;AAAA,IACjB,CAAC;AACD,yBAAqB,QAAQ,SAAS,IAAI;AAG1C,UAAM,kBAAkB,IAAI,eAAe,CAAC,YAAY;AAEpD,UAAI,cAAc;AAClB,iBAAW,SAAS,SAAS;AACzB,YAAI,MAAM,OAAO,aAAa;AAC1B,wBAAc;AACd;AAAA,QACJ;AAAA,MACJ;AACA,UAAI,aAAa;AAOb,gCAAwB;AAAA,MAC5B;AAAA,IACJ,CAAC;AAED,kBAAc,UAAU,CAAC;AAEzB,UAAM,0BAA0B,MAAM;AAClC,YAAM,YAAY,YAAY,IAAI;AAClC,6BAAuB,gBAAgB,SAAS,iBAAiB,OAAO;AAGxE,sBAAgB,WAAW;AAC3B,iBAAW,CAAC,EAAE,KAAK,gBAAgB,SAAS;AACxC,wBAAgB,QAAQ,EAAE;AAAA,MAC9B;AAEA,iBAAW,QAAQ,WAAW,YAAY,IAAI,IAAI;AAAA,IACtD;AACA,4BAAwB;AAGxB,0BAAsB,MAAM;AACxB,UAAI,UAAW,cAAa,IAAI;AAAA,IACpC,CAAC;AAED,QAAI,WAAW;AACf,QAAI,oBAAoB;AAExB,QAAI,eAAmD,CAAC;AAExD,UAAM,UAAU,CAAC,gBAAwB;AACrC,UAAI,aAAa,GAAG;AAChB,mBAAW;AACX,uBAAe,UAAU,sBAAsB,OAAO;AACtD;AAAA,MACJ;AAGA,YAAM,YAAY,KAAK,IAAI,cAAc,UAAU,EAAE;AAGrD,YAAM,MAAM,YAAY,IAAI;AAC5B,gBAAU,QAAQ,KAAK,GAAG;AAC1B,gBAAU,UAAU,UAAU,QAAQ,OAAO,OAAK,MAAM,IAAI,GAAI;AAGhE,iBAAW,QAAQ,SAAS,cAAc;AAE1C,iBAAW;AACX,YAAM,KAAK,YAAY;AAEvB,YAAM,iBAAiB,YAAY,IAAI;AAGvC,YAAM,aAAa,YAAY,IAAI;AAInC,YAAM,MAAM,OAAO;AACnB,UAAI,aAAa,KAAK,GAAG,GAAG,KAAK,GAAG,CAAC;AACrC,UAAI,UAAU,GAAG,GAAG,OAAO,QAAQ,KAAK,OAAO,SAAS,GAAG;AAI3D,YAAM,UAAU,OAAO;AACvB,YAAM,UAAU,OAAO;AACvB,UAAI,UAAU,CAAC,SAAS,CAAC,OAAO;AAEhC,iBAAW,QAAQ,YAAY,YAAY,IAAI,IAAI;AAEnD,YAAM,aAAa,cAAc;AAKjC,YAAM,YAAY,YAAY,IAAI;AAClC,qBAAe,gBAAgB,gBAAgB,OAAO;AACtD,iBAAW,QAAQ,iBAAiB,YAAY,IAAI,IAAI;AAGxD,YAAM,eAAe,YAAY,IAAI;AACrC,gCAA0B,cAAc,iBAAiB,SAAS,EAAE;AACpE;AAAA,QACI;AAAA,QACA;AAAA,QACA,iBAAiB;AAAA,QACjB;AAAA,QACA,SAAS,gBAAgB;AAAA,QACzB,SAAS,gBAAgB;AAAA,MAC7B;AACA,iBAAW,QAAQ,cAAc,YAAY,IAAI,IAAI;AAGrD,YAAM,YAAY,YAAY,IAAI;AAClC,qBAAe,KAAK,UAAU;AAG9B,UAAI,aAAa,WAAW,WAAW,SAAS,iBAAiB,QAAQ,YAAY;AACjF,cAAM,aAAa,UAAU,QAAQ;AAIrC,cAAM,cAAc,cAAc,MAAM,KAAK,OAAO,IAAI;AAExD,YAAI,aAAa;AACb,gBAAM,eAAe,KAAK,OAAO,IAAI;AACrC,qBAAW,KAAK,gBAAgB,SAAS,gBAAgB,aAAa,iBAAiB,SAAS,YAAY,CAAC;AAAA,QACjH;AAAA,MACJ;AAOA,YAAM,gBAAgB,OAAO;AAC7B,YAAM,iBAAiB,OAAO;AAC9B,YAAM,eAAe,aAAa;AAAA,QAAO,CAAC,EAAE,KAAK,MAC7C,KAAK,SAAS,KAAK,KAAK,QAAQ,iBAChC,KAAK,UAAU,KAAK,KAAK,OAAO;AAAA,MACpC;AAGA,UAAI,aAAa,SAAS,GAAG;AACzB,0BAAkB,KAAK,cAAc,SAAS,OAAO;AACrD,8BAAsB,KAAK,cAAc,SAAS,OAAO;AAAA,MAC7D;AAEA,iBAAW,QAAQ,WAAW,YAAY,IAAI,IAAI;AAClD,iBAAW,QAAQ,YAAY,YAAY,IAAI,IAAI;AAGnD,UAAI,cAAc,oBAAoB,KAAK;AACvC,sBAAc,QAAQ;AAAA,UAClB,KAAK,UAAU,QAAQ;AAAA,UACvB,WAAW,WAAW,QAAQ;AAAA,UAC9B,UAAU,WAAW,QAAQ;AAAA,UAC7B,gBAAgB,WAAW,QAAQ;AAAA,UACnC,cAAc,gBAAgB,QAAQ;AAAA,UACtC,YAAY,WAAW;AAAA,UACvB,WAAW,iBAAiB,QAAQ;AAAA,UACpC,QAAQ,WAAW,QAAQ;AAAA,UAC3B,WAAW,WAAW,QAAQ;AAAA,UAC9B,aAAa,WAAW,QAAQ;AAAA,UAChC,UAAU,WAAW,QAAQ;AAAA,QACjC,CAAC;AACD,4BAAoB;AAAA,MACxB;AAEA,qBAAe,UAAU,sBAAsB,OAAO;AAAA,IAC1D;AAEA,mBAAe,UAAU,sBAAsB,OAAO;AAEtD,UAAM,eAAe,MAAM;AACvB,mBAAa;AACb,sBAAgB,QAAQ,MAAM;AAC9B,8BAAwB;AAAA,IAC5B;AAEA,WAAO,iBAAiB,UAAU,YAAY;AAG9C,UAAM,gBAAgB,YAAY,yBAAyB,GAAI;AAE/D,WAAO,MAAM;AACT,2BAAqB,eAAe,OAAO;AAC3C,aAAO,oBAAoB,UAAU,YAAY;AACjD,oBAAc,aAAa;AAC3B,2BAAqB,WAAW;AAChC,sBAAgB,WAAW;AAAA,IAC/B;AAAA,EACJ,GAAG,CAAC,SAAS,CAAC;AAEd,MAAI,CAAC,UAAW,QAAO;AAEvB,SACI,gBAAAD,KAAA,YACI,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACG,KAAK;AAAA,MACL,OAAO;AAAA,QACH,UAAU;AAAA;AAAA,QACV,KAAK;AAAA,QACL,MAAM;AAAA,QACN,eAAe;AAAA,QACf,QAAQ;AAAA,QACR,SAAS,YAAY,IAAI;AAAA,QACzB,YAAY;AAAA,QACZ,YAAY;AAAA,MAChB;AAAA,MACA,eAAY;AAAA;AAAA,EAChB,GAEJ;AAER;;;AMxSA,SAAS,aAAAE,YAAW,YAAAC,iBAAgB;AAiEpB,SAkDA,YAAAC,WAjDI,OAAAC,MADJ;AA/DD,SAAR,WAA4B,EAAE,cAAc,KAAK,GAA8B;AAClF,QAAM,EAAE,WAAW,aAAa,QAAQ,IAAI,YAAY;AACxD,QAAM,CAAC,aAAa,cAAc,IAAIF,UAAS,CAAC,WAAW;AAC3D,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAS,KAAK;AAE1C,EAAAD,WAAU,MAAM;AACZ,UAAM,gBAAgB,CAAC,MAAqB;AACxC,UAAI,EAAE,YAAY,EAAE,QAAQ,KAAK;AAC7B,oBAAY;AAAA,MAChB;AAAA,IACJ;AACA,WAAO,iBAAiB,WAAW,aAAa;AAChD,WAAO,MAAM,OAAO,oBAAoB,WAAW,aAAa;AAAA,EACpE,GAAG,CAAC,WAAW,CAAC;AAEhB,QAAM,kBAAkB,MAAM;AAC1B,QAAI,SAAS;AACT,YAAM,OAAO;AAAA,QACT,GAAG;AAAA,QACH,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,WAAW,UAAU;AAAA,QACrB,YAAY;AAAA,UACR,OAAO,OAAO;AAAA,UACd,QAAQ,OAAO;AAAA,QACnB;AAAA,MACJ;AACA,gBAAU,UAAU,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAC3D,gBAAU,IAAI;AACd,iBAAW,MAAM,UAAU,KAAK,GAAG,GAAI;AAAA,IAC3C;AAAA,EACJ;AAEA,MAAI,CAAC,UAAW,QAAO;AAEvB,SACI;AAAA,IAAC;AAAA;AAAA,MACG,iBAAc;AAAA,MACd,OAAO;AAAA,QACH,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,iBAAiB;AAAA,QACjB,gBAAgB;AAAA,QAChB,sBAAsB;AAAA,QACtB,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,SAAS,cAAc,SAAS;AAAA,QAChC,cAAc;AAAA,QACd,QAAQ;AAAA,QACR,UAAU,cAAc,SAAS;AAAA,QACjC,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,YAAY;AAAA,MAChB;AAAA,MACA;AAAA,6BAAC,SAAI,OAAO;AAAA,UACR,SAAS;AAAA,UACT,gBAAgB;AAAA,UAChB,YAAY;AAAA,UACZ,cAAc,cAAc,IAAI;AAAA,UAChC,KAAK;AAAA,QACT,GACI;AAAA,+BAAC,SAAI,OAAO,EAAE,YAAY,OAAO,OAAO,QAAQ,SAAS,QAAQ,YAAY,UAAU,KAAK,MAAM,GAC9F;AAAA,4BAAAG,KAAC,UAAK,OAAO,EAAE,UAAU,OAAO,GAAG,0BAAE;AAAA,YACrC,gBAAAA,KAAC,UAAK,OAAO;AAAA,cACT,YAAY;AAAA,cACZ,sBAAsB;AAAA,cACtB,qBAAqB;AAAA,cACrB,YAAY;AAAA,YAChB,GAAG,mBAAK;AAAA,aACZ;AAAA,UACA,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,KAAK,MAAM,GACtC;AAAA,4BAAAA;AAAA,cAAC;AAAA;AAAA,gBACG,SAAS,MAAM,eAAe,CAAC,WAAW;AAAA,gBAC1C,OAAO;AAAA,kBACH,YAAY;AAAA,kBACZ,QAAQ;AAAA,kBACR,OAAO;AAAA,kBACP,QAAQ;AAAA,kBACR,OAAO;AAAA,kBACP,QAAQ;AAAA,kBACR,SAAS;AAAA,kBACT,YAAY;AAAA,kBACZ,gBAAgB;AAAA,kBAChB,cAAc;AAAA,kBACd,UAAU;AAAA,gBACd;AAAA,gBAEC,wBAAc,WAAM;AAAA;AAAA,YACzB;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACG,SAAS;AAAA,gBACT,OAAO;AAAA,kBACH,YAAY;AAAA,kBACZ,QAAQ;AAAA,kBACR,OAAO;AAAA,kBACP,QAAQ;AAAA,kBACR,OAAO;AAAA,kBACP,QAAQ;AAAA,kBACR,SAAS;AAAA,kBACT,YAAY;AAAA,kBACZ,gBAAgB;AAAA,kBAChB,cAAc;AAAA,kBACd,UAAU;AAAA,gBACd;AAAA,gBACH;AAAA;AAAA,YAED;AAAA,aACJ;AAAA,WACJ;AAAA,QAEC,CAAC,eAAe,WACb,qBAAAD,WAAA,EACI;AAAA,+BAAC,SAAI,OAAO,EAAE,cAAc,QAAQ,eAAe,QAAQ,cAAc,mCAAmC,GACxG;AAAA,4BAAAC,KAAC,SAAI,OAAO,EAAE,OAAO,WAAW,cAAc,OAAO,UAAU,QAAQ,YAAY,OAAO,eAAe,SAAS,eAAe,YAAY,GAAG,yBAEhJ;AAAA,YACA,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,iBAAiB,cAAc,MAAM,GAChF;AAAA,8BAAAA,KAAC,UAAK,iBAAG;AAAA,cACT,gBAAAA,KAAC,UAAK,OAAO,EAAE,YAAY,QAAQ,OAAO,QAAQ,MAAM,KAAK,YAAY,QAAQ,MAAM,KAAK,YAAY,UAAU,GAC7G,kBAAQ,IAAI,QAAQ,CAAC,GAC1B;AAAA,eACJ;AAAA,YACA,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,iBAAiB,cAAc,MAAM,GAChF;AAAA,8BAAAA,KAAC,UAAK,wBAAU;AAAA,cAChB,qBAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,wBAAQ,UAAU,QAAQ,CAAC;AAAA,gBAAE;AAAA,iBAAE;AAAA,eAC9E;AAAA,YACA,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,gBAAgB,GAC3D;AAAA,8BAAAA,KAAC,UAAK,OAAO,EAAE,OAAO,QAAQ,UAAU,QAAQ,SAAS,KAAK,YAAY,UAAU,GAAG,qBAAO;AAAA,cAC9F,qBAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,wBAAQ,QAAQ,QAAQ,CAAC,KAAK;AAAA,gBAAE;AAAA,iBAAE;AAAA,eACjF;AAAA,aACJ;AAAA,UAEA,qBAAC,SAAI,OAAO,EAAE,cAAc,QAAQ,eAAe,QAAQ,cAAc,mCAAmC,GACxG;AAAA,4BAAAA,KAAC,SAAI,OAAO,EAAE,OAAO,WAAW,cAAc,OAAO,UAAU,QAAQ,YAAY,OAAO,eAAe,SAAS,eAAe,YAAY,GAAG,8BAEhJ;AAAA,YACA,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,qBAAqB,WAAW,KAAK,WAAW,GAC3E;AAAA,mCAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,gBAAgB,GAAG;AAAA,gCAAAA,KAAC,UAAK,mBAAK;AAAA,gBAAO;AAAA,gBAAC,qBAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,0BAAQ,WAAW,QAAQ,CAAC,KAAK;AAAA,kBAAE;AAAA,mBAAE;AAAA,iBAAO;AAAA,cAC5K,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,gBAAgB,GAAG;AAAA,gCAAAA,KAAC,UAAK,qBAAO;AAAA,gBAAO;AAAA,gBAAC,qBAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,0BAAQ,aAAa,QAAQ,CAAC,KAAK;AAAA,kBAAE;AAAA,mBAAE;AAAA,iBAAO;AAAA,cAChL,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,gBAAgB,GAAG;AAAA,gCAAAA,KAAC,UAAK,kBAAI;AAAA,gBAAO;AAAA,gBAAC,qBAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,0BAAQ,UAAU,QAAQ,CAAC,KAAK;AAAA,kBAAE;AAAA,mBAAE;AAAA,iBAAO;AAAA,cAC1K,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,gBAAgB,GAAG;AAAA,gCAAAA,KAAC,UAAK,kBAAI;AAAA,gBAAO;AAAA,gBAAC,qBAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,0BAAQ,SAAS,QAAQ,CAAC;AAAA,kBAAE;AAAA,mBAAE;AAAA,iBAAO;AAAA,cACpK,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,iBAAiB,YAAY,SAAS,GAAG;AAAA,gCAAAA,KAAC,UAAK,yBAAW;AAAA,gBAAO;AAAA,gBAAC,qBAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,0BAAQ,eAAe,QAAQ,CAAC;AAAA,kBAAE;AAAA,mBAAE;AAAA,iBAAO;AAAA,eAC3M;AAAA,aACJ;AAAA,UAEA,qBAAC,SAAI,OAAO,EAAE,cAAc,OAAO,GAC/B;AAAA,4BAAAA,KAAC,SAAI,OAAO,EAAE,OAAO,WAAW,cAAc,OAAO,UAAU,QAAQ,YAAY,OAAO,eAAe,SAAS,eAAe,YAAY,GAAG,oBAEhJ;AAAA,YACA,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,iBAAiB,cAAc,MAAM,GAChF;AAAA,8BAAAA,KAAC,UAAK,wBAAU;AAAA,cAChB,qBAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,wBAAQ;AAAA,gBAAW;AAAA,gBAAI,QAAQ;AAAA,iBAAU;AAAA,eACxF;AAAA,YACA,qBAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,gBAAgB,GAC3D;AAAA,8BAAAA,KAAC,UAAK,sBAAQ;AAAA,cACd,gBAAAA,KAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI,kBAAQ,cAAa;AAAA,eACpE;AAAA,aACJ;AAAA,UAEA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACG,SAAS;AAAA,cACT,OAAO;AAAA,gBACH,OAAO;AAAA,gBACP,SAAS;AAAA,gBACT,YAAY,SAAS,2BAA2B;AAAA,gBAChD,QAAQ,SAAS,qCAAqC;AAAA,gBACtD,OAAO,SAAS,YAAY;AAAA,gBAC5B,QAAQ;AAAA,gBACR,cAAc;AAAA,gBACd,UAAU;AAAA,gBACV,YAAY;AAAA,gBACZ,YAAY;AAAA,gBACZ,SAAS;AAAA,gBACT,YAAY;AAAA,gBACZ,gBAAgB;AAAA,gBAChB,KAAK;AAAA,cACT;AAAA,cAEC,mBAAS,kBAAa;AAAA;AAAA,UAC3B;AAAA,UAEA,gBAAAA,KAAC,SAAI,OAAO,EAAE,WAAW,QAAQ,UAAU,QAAQ,OAAO,WAAW,WAAW,SAAS,GAAG,+BAE5F;AAAA,WACJ;AAAA;AAAA;AAAA,EAER;AAER;","names":["useState","shouldAccumulate","jsx","useState","useEffect","useState","Fragment","jsx"]}
1
+ {"version":3,"sources":["../src/Snowfall.tsx","../src/SnowfallProvider.tsx","../src/utils/snowfall/constants.ts","../src/utils/snowfall/dom.ts","../src/utils/snowfall/physics.ts","../src/utils/snowfall/draw.ts","../src/DebugPanel.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { useSnowfall } from './SnowfallProvider';\nimport { Snowflake, SnowAccumulation } from './utils/snowfall/types';\nimport { getElementRects } from './utils/snowfall/dom';\nimport { createSnowflake, initializeAccumulation, meltAndSmoothAccumulation, updateSnowflakes } from './utils/snowfall/physics';\nimport { drawAccumulations, drawSideAccumulations, drawSnowflakes } from './utils/snowfall/draw';\n\nexport default function Snowfall() {\n const { isEnabled, physicsConfig, setMetrics } = useSnowfall();\n const isEnabledRef = useRef(isEnabled);\n const physicsConfigRef = useRef(physicsConfig);\n const setMetricsRef = useRef(setMetrics);\n const [isMounted, setIsMounted] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const snowflakesRef = useRef<Snowflake[]>([]);\n const accumulationRef = useRef<Map<Element, SnowAccumulation>>(new Map());\n const animationIdRef = useRef<number>(0);\n\n // Cache DPR to avoid reading it every frame (only changes on resize)\n // Initialize with safe default for SSR, actual value set in resizeCanvas\n const dprRef = useRef(1);\n\n // Performance metrics tracking\n const fpsFrames = useRef<number[]>([]);\n const metricsRef = useRef({\n scanTime: 0,\n rectUpdateTime: 0,\n frameTime: 0,\n rafGap: 0,\n clearTime: 0,\n physicsTime: 0,\n drawTime: 0,\n });\n\n useEffect(() => {\n requestAnimationFrame(() => setIsMounted(true));\n }, []);\n\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n physicsConfigRef.current = physicsConfig;\n }, [physicsConfig]);\n\n useEffect(() => {\n setMetricsRef.current = setMetrics;\n }, [setMetrics]);\n\n useEffect(() => {\n if (!isMounted) return;\n\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const resizeCanvas = () => {\n if (canvasRef.current) {\n // Use viewport dimensions for fixed canvas\n const newWidth = window.innerWidth;\n const newHeight = window.innerHeight;\n\n // Handle high DPI displays - cache DPR in ref for use in animation loop\n const dpr = window.devicePixelRatio || 1;\n dprRef.current = dpr;\n canvasRef.current.width = newWidth * dpr;\n canvasRef.current.height = newHeight * dpr;\n\n // Set CSS size\n canvasRef.current.style.width = `${newWidth}px`;\n canvasRef.current.style.height = `${newHeight}px`;\n }\n };\n resizeCanvas();\n\n const windowResizeObserver = new ResizeObserver(() => {\n resizeCanvas();\n });\n windowResizeObserver.observe(document.body);\n\n // Separate observer for snow accumulation surfaces\n const surfaceObserver = new ResizeObserver((entries) => {\n // Check if any accumulation element actually changed size\n let needsUpdate = false;\n for (const entry of entries) {\n if (entry.target.isConnected) {\n needsUpdate = true;\n break;\n }\n }\n if (needsUpdate) {\n // Re-run initialization to adapt to new sizes\n // We do NOT want to infinitely recurse, so initAccumulationWrapper\n // must be careful about disconnection if called from here.\n // Actually, we don't need to disconnect/reconnect here if the set of elements hasn't changed,\n // but getAccumulationSurfaces (called by init) might find new ones or drop old ones.\n // For simplicity, we just trigger the scan.\n initAccumulationWrapper();\n }\n });\n\n snowflakesRef.current = [];\n\n const initAccumulationWrapper = () => {\n const scanStart = performance.now();\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n\n // Sync observer with current surfaces\n surfaceObserver.disconnect();\n for (const [el] of accumulationRef.current) {\n surfaceObserver.observe(el);\n }\n\n metricsRef.current.scanTime = performance.now() - scanStart;\n };\n initAccumulationWrapper();\n\n // Delay visibility slightly to ensure smooth fade-in after canvas is ready\n requestAnimationFrame(() => {\n if (isMounted) setIsVisible(true);\n });\n\n let lastTime = 0;\n let lastMetricsUpdate = 0;\n // Holds current frame's element positions\n let elementRects: ReturnType<typeof getElementRects> = [];\n\n const animate = (currentTime: number) => {\n if (lastTime === 0) {\n lastTime = currentTime;\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n\n const deltaTime = Math.min(currentTime - lastTime, 50);\n\n // Always track FPS so we have data when panel opens\n const now = performance.now();\n fpsFrames.current.push(now);\n fpsFrames.current = fpsFrames.current.filter(t => now - t < 1000);\n\n // Track detailed performance metrics\n metricsRef.current.rafGap = currentTime - lastTime;\n\n lastTime = currentTime;\n const dt = deltaTime / 16.67;\n\n const frameStartTime = performance.now();\n\n // Time canvas clear\n const clearStart = performance.now();\n\n // Reset transform to clear the entire viewport-sized canvas\n // We use the cached dpr scaling, so we clear the logical width/height\n const dpr = dprRef.current;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);\n\n // Translate the context to Simulate Scrolling\n // We move the 'world' up by scrollY, so absolute coordinates draw in the correct place relative to viewport\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n ctx.translate(-scrollX, -scrollY);\n\n metricsRef.current.clearTime = performance.now() - clearStart;\n\n const snowflakes = snowflakesRef.current;\n\n // PERFORMANCE: Update element rects EVERY FRAME to track layout changes and animations\n // getBoundingClientRect() is necessary to handle moving/animating elements\n // getBoundingClientRect() is fast enough for the accumulation targets (< 50 elements)\n const rectStart = performance.now();\n elementRects = getElementRects(accumulationRef.current);\n metricsRef.current.rectUpdateTime = performance.now() - rectStart;\n\n // Time physics\n const physicsStart = performance.now();\n meltAndSmoothAccumulation(elementRects, physicsConfigRef.current, dt);\n updateSnowflakes(\n snowflakes,\n elementRects,\n physicsConfigRef.current,\n dt,\n document.documentElement.scrollWidth,\n document.documentElement.scrollHeight\n );\n metricsRef.current.physicsTime = performance.now() - physicsStart;\n\n // Draw Snowflakes (batched for performance)\n const drawStart = performance.now();\n drawSnowflakes(ctx, snowflakes);\n\n // Spawn new snowflakes with adaptive rate based on performance\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const currentFps = fpsFrames.current.length;\n\n // Adaptive spawn rate: reduce load when FPS is low to prevent death spirals\n // Low FPS (<40): 20% spawn rate | Normal FPS: 100% spawn rate\n const shouldSpawn = currentFps >= 40 || Math.random() < 0.2;\n\n if (shouldSpawn) {\n const isBackground = Math.random() < 0.4;\n snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));\n }\n }\n\n // Draw Accumulation\n // We draw accumulations in World Space (by passing scroll offset to draw functions)\n // This aligns perfectly with the translated context and world-space snowflakes.\n\n // Viewport culling: Filter to only visible elements before drawing\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const visibleRects = elementRects.filter(({ rect }) =>\n rect.right >= 0 && rect.left <= viewportWidth &&\n rect.bottom >= 0 && rect.top <= viewportHeight\n );\n\n // Only call draw functions if there are visible elements\n if (visibleRects.length > 0) {\n drawAccumulations(ctx, visibleRects, scrollX, scrollY);\n drawSideAccumulations(ctx, visibleRects, scrollX, scrollY);\n }\n\n metricsRef.current.drawTime = performance.now() - drawStart;\n metricsRef.current.frameTime = performance.now() - frameStartTime;\n\n // Update metrics every 500ms\n if (currentTime - lastMetricsUpdate > 500) {\n setMetricsRef.current({\n fps: fpsFrames.current.length,\n frameTime: metricsRef.current.frameTime,\n scanTime: metricsRef.current.scanTime,\n rectUpdateTime: metricsRef.current.rectUpdateTime,\n surfaceCount: accumulationRef.current.size,\n flakeCount: snowflakes.length,\n maxFlakes: physicsConfigRef.current.MAX_FLAKES,\n rafGap: metricsRef.current.rafGap,\n clearTime: metricsRef.current.clearTime,\n physicsTime: metricsRef.current.physicsTime,\n drawTime: metricsRef.current.drawTime,\n });\n lastMetricsUpdate = currentTime;\n }\n\n animationIdRef.current = requestAnimationFrame(animate);\n };\n\n animationIdRef.current = requestAnimationFrame(animate);\n\n const handleResize = () => {\n resizeCanvas();\n accumulationRef.current.clear();\n initAccumulationWrapper();\n };\n\n window.addEventListener('resize', handleResize);\n\n // Periodic surface scan every 5 seconds to detect DOM changes\n const checkInterval = setInterval(initAccumulationWrapper, 5000);\n\n return () => {\n cancelAnimationFrame(animationIdRef.current);\n window.removeEventListener('resize', handleResize);\n clearInterval(checkInterval);\n windowResizeObserver.disconnect();\n surfaceObserver.disconnect();\n };\n }, [isMounted]);\n\n if (!isMounted) return null;\n\n return (\n <>\n <canvas\n ref={canvasRef}\n style={{\n position: 'fixed', // FIXED position to eliminate scroll jitter\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n willChange: 'transform',\n }}\n aria-hidden=\"true\"\n />\n\n </>\n );\n}\n","'use client';\n\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n\nexport interface PhysicsConfig {\n MAX_FLAKES: number;\n MELT_SPEED: number;\n WIND_STRENGTH: number;\n ACCUMULATION: {\n SIDE_RATE: number;\n TOP_RATE: number;\n BOTTOM_RATE: number;\n };\n MAX_DEPTH: {\n TOP: number;\n BOTTOM: number;\n SIDE: number;\n };\n FLAKE_SIZE: {\n MIN: number;\n MAX: number;\n };\n MAX_SURFACES: number;\n}\n\nexport const DEFAULT_PHYSICS: PhysicsConfig = {\n MAX_FLAKES: 1000,\n MELT_SPEED: 0.00001,\n WIND_STRENGTH: 1.5,\n ACCUMULATION: {\n SIDE_RATE: 1,\n TOP_RATE: 5,\n BOTTOM_RATE: 5.0,\n },\n MAX_DEPTH: {\n TOP: 100,\n BOTTOM: 50,\n SIDE: 20,\n },\n FLAKE_SIZE: {\n MIN: 0.5,\n MAX: 1.6,\n },\n MAX_SURFACES: 15\n};\n\nexport interface PerformanceMetrics {\n fps: number;\n frameTime: number;\n scanTime: number;\n rectUpdateTime: number;\n surfaceCount: number;\n flakeCount: number;\n maxFlakes: number;\n // Detailed metrics\n rafGap: number; // Time between requestAnimationFrame calls\n clearTime: number; // Time to clear canvas\n physicsTime: number; // Time for physics updates\n drawTime: number; // Time to draw snowflakes and accumulation\n}\n\ninterface SnowfallContextType {\n isEnabled: boolean;\n toggleSnow: () => void;\n physicsConfig: PhysicsConfig;\n updatePhysicsConfig: (config: Partial<PhysicsConfig>) => void;\n resetPhysics: () => void;\n debugMode: boolean;\n toggleDebug: () => void;\n metrics: PerformanceMetrics | null;\n setMetrics: (metrics: PerformanceMetrics) => void;\n}\n\nconst SnowfallContext = createContext<SnowfallContextType | undefined>(undefined);\n\nexport function SnowfallProvider({ children, initialDebug = false }: { children: ReactNode; initialDebug?: boolean }) {\n const [isEnabled, setIsEnabled] = useState(true);\n const [physicsConfig, setPhysicsConfig] = useState<PhysicsConfig>(DEFAULT_PHYSICS);\n const [debugMode, setDebugMode] = useState(initialDebug);\n const [metrics, setMetrics] = useState<PerformanceMetrics | null>(null);\n\n const toggleSnow = () => {\n setIsEnabled((prev) => !prev);\n };\n\n const toggleDebug = () => {\n setDebugMode((prev) => !prev);\n };\n\n const updatePhysicsConfig = (config: Partial<PhysicsConfig>) => {\n setPhysicsConfig((prev) => ({\n ...prev,\n ...config,\n ACCUMULATION: {\n ...prev.ACCUMULATION,\n ...(config.ACCUMULATION || {}),\n },\n MAX_DEPTH: {\n ...prev.MAX_DEPTH,\n ...(config.MAX_DEPTH || {}),\n },\n FLAKE_SIZE: {\n ...prev.FLAKE_SIZE,\n ...(config.FLAKE_SIZE || {}),\n },\n }));\n };\n\n const resetPhysics = () => {\n setPhysicsConfig(DEFAULT_PHYSICS);\n };\n\n return (\n <SnowfallContext.Provider value={{\n isEnabled,\n toggleSnow,\n physicsConfig,\n updatePhysicsConfig,\n resetPhysics,\n debugMode,\n toggleDebug,\n metrics,\n setMetrics,\n }}>\n {children}\n </SnowfallContext.Provider>\n );\n}\n\nexport function useSnowfall() {\n const context = useContext(SnowfallContext);\n if (context === undefined) {\n throw new Error('useSnowfall must be used within a SnowfallProvider');\n }\n return context;\n}\n","export const ATTR_SNOWFALL = 'data-snowfall';\n\nexport const VAL_IGNORE = 'ignore';\nexport const VAL_TOP = 'top';\nexport const VAL_BOTTOM = 'bottom';\n\nexport const TAG_HEADER = 'header';\nexport const TAG_FOOTER = 'footer';\n\nexport const ROLE_BANNER = 'banner';\nexport const ROLE_CONTENTINFO = 'contentinfo';\n\n// Mathematical constants\nexport const TAU = Math.PI * 2; // Full circle in radians\n","import { SnowAccumulation, ElementSurface, SnowfallSurface } from './types';\nimport {\n ATTR_SNOWFALL, VAL_IGNORE, VAL_TOP, VAL_BOTTOM,\n TAG_HEADER, TAG_FOOTER, ROLE_BANNER, ROLE_CONTENTINFO\n} from './constants';\n\n// Headers: snow accumulates on BOTTOM edge\n// Footers: default to TOP surface (snow piles on top)\n// Use data-snowfall attributes to override this behavior\nconst BOTTOM_TAGS = [TAG_HEADER];\nconst BOTTOM_ROLES = [ROLE_BANNER];\n\nconst AUTO_DETECT_TAGS = [TAG_HEADER, TAG_FOOTER, 'article', 'section', 'aside', 'nav'];\nconst AUTO_DETECT_ROLES = [`[role=\"${ROLE_BANNER}\"]`, `[role=\"${ROLE_CONTENTINFO}\"]`, '[role=\"main\"]'];\nconst AUTO_DETECT_CLASSES = [\n '.card', '[class*=\"card\"]', '[class*=\"Card\"]',\n '[class*=\"bg-\"]', '[class*=\"shadow-\"]', '[class*=\"rounded-\"]'\n];\n\n// Helper to get element type (Top vs Bottom surface) based on tags/roles\n// This is used to determine if snow should sit ON TOP or hang from the BOTTOM.\n\nexport const getElementType = (el: Element): SnowfallSurface => {\n const tagName = el.tagName.toLowerCase();\n if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;\n\n const role = el.getAttribute('role');\n if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;\n\n // Default: snow accumulates on top of elements (natural physics)\n return VAL_TOP;\n};\n\nconst shouldAccumulate = (el: Element, precomputedStyle?: CSSStyleDeclaration): boolean => {\n if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;\n // Explicit opt-in\n if (el.hasAttribute(ATTR_SNOWFALL)) return true;\n\n const styles = precomputedStyle || window.getComputedStyle(el);\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n if (!isVisible) return false;\n\n // Check for visual prominence\n const bgColor = styles.backgroundColor;\n const hasBackground = bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent';\n const hasBorder = (parseFloat(styles.borderWidth) > 0 && styles.borderColor !== 'transparent' && styles.borderColor !== 'rgba(0, 0, 0, 0)') && styles.borderStyle !== 'none';\n const hasBoxShadow = styles.boxShadow !== 'none';\n const hasFilter = styles.filter !== 'none' && styles.filter.includes('drop-shadow');\n const hasBackdropFilter = styles.backdropFilter !== 'none';\n\n return hasBackground || hasBorder || hasBoxShadow || hasFilter || hasBackdropFilter;\n};\n\nexport const getAccumulationSurfaces = (maxSurfaces: number = 5): { el: Element; type: SnowfallSurface }[] => {\n // No explicit clearing needed as we don't cache styles persistently anymore.\n const surfaces: { el: Element; type: SnowfallSurface }[] = [];\n const seen = new Set<Element>();\n\n const candidates = document.querySelectorAll(\n [\n `[${ATTR_SNOWFALL}]`,\n ...AUTO_DETECT_TAGS,\n ...AUTO_DETECT_ROLES,\n ...AUTO_DETECT_CLASSES\n ].join(', ')\n );\n\n for (const el of candidates) {\n if (surfaces.length >= maxSurfaces) break;\n if (seen.has(el)) continue;\n\n // Manual override check first\n const manualOverride = el.getAttribute(ATTR_SNOWFALL);\n if (manualOverride === VAL_IGNORE) continue;\n\n // If manually opted in, skip some heuristic checks but keep basic visibility/size sanity\n const isManuallyIncluded = manualOverride !== null;\n const styles = window.getComputedStyle(el);\n\n // Visibility Check: Must be visible and opaque enough\n const isVisible = styles.display !== 'none' && styles.visibility !== 'hidden' && parseFloat(styles.opacity) > 0.1;\n\n if (!isVisible && !isManuallyIncluded) continue;\n\n const rect = el.getBoundingClientRect();\n const hasSize = rect.width >= 100 && rect.height >= 50;\n if (!hasSize && !isManuallyIncluded) continue;\n\n const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;\n const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());\n const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute('role') || '');\n const isBottomSurface = isBottomTag || isBottomRole || manualOverride === VAL_BOTTOM;\n\n if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) continue;\n\n if (shouldAccumulate(el, styles)) {\n let type: SnowfallSurface = getElementType(el);\n if (manualOverride === VAL_BOTTOM) type = VAL_BOTTOM;\n else if (manualOverride === VAL_TOP) type = VAL_TOP;\n\n surfaces.push({ el, type });\n seen.add(el);\n }\n }\n\n return surfaces;\n};\n\nexport const getElementRects = (accumulationMap: Map<Element, SnowAccumulation>): ElementSurface[] => {\n const elementRects: ElementSurface[] = [];\n for (const [el, acc] of accumulationMap.entries()) {\n if (!el.isConnected) continue;\n // PURE VIEWPORT RECT. No scroll math.\n const rect = el.getBoundingClientRect();\n elementRects.push({ el, rect, acc });\n }\n return elementRects;\n};\n","import { PhysicsConfig } from '../../SnowfallProvider';\nimport { Snowflake, SnowAccumulation, ElementSurface } from './types';\nimport { getAccumulationSurfaces } from './dom';\nimport { VAL_BOTTOM, TAU } from './constants';\n\n// Opacity buckets for batched rendering (reduce globalAlpha state changes)\nconst OPACITY_BUCKETS = [0.3, 0.5, 0.7, 0.9];\n\n/**\n * Quantize opacity to nearest bucket for efficient batching during draw.\n * This eliminates runtime bucketing overhead by snapping at creation time.\n */\nconst quantizeOpacity = (opacity: number): number => {\n return OPACITY_BUCKETS.reduce((prev, curr) =>\n Math.abs(curr - opacity) < Math.abs(prev - opacity) ? curr : prev\n );\n};\n\nexport const createSnowflake = (\n worldWidth: number,\n config: PhysicsConfig,\n isBackground = false\n): Snowflake => {\n // Two random calls per flake:\n // 1) horizontal position\n // 2) DNA seed for all other traits\n const x = Math.random() * worldWidth;\n const dna = Math.random();\n\n // Pseudo-random trait extraction from DNA\n const noise = {\n speed: (dna * 13) % 1,\n wind: (dna * 7) % 1,\n wobblePhase: (dna * 23) % 1,\n wobbleSpeed: (dna * 5) % 1\n };\n\n const { MIN, MAX } = config.FLAKE_SIZE;\n const sizeRatio = dna;\n\n // Background vs foreground tuning\n const profile = isBackground\n ? {\n sizeMin: MIN * 0.6,\n sizeRange: (MAX - MIN) * 0.4,\n speedBase: 0.2,\n speedScale: 0.3,\n noiseSpeedScale: 0.2,\n windScale: config.WIND_STRENGTH * 0.625,\n opacityBase: 0.2,\n opacityScale: 0.2,\n wobbleBase: 0.005,\n wobbleScale: 0.015\n }\n : {\n sizeMin: MIN,\n sizeRange: MAX - MIN,\n speedBase: 0.5,\n speedScale: 0.5,\n noiseSpeedScale: 0.3,\n windScale: config.WIND_STRENGTH,\n opacityBase: 0.5,\n opacityScale: 0.3,\n wobbleBase: 0.01,\n wobbleScale: 0.02\n };\n\n const radius = profile.sizeMin + sizeRatio * profile.sizeRange;\n const glowRadius = radius * 1.5;\n\n // Calculate opacity and quantize to bucket for efficient batched rendering\n const rawOpacity = profile.opacityBase + sizeRatio * profile.opacityScale;\n const opacity = quantizeOpacity(rawOpacity);\n\n // Pre-calculate glow opacity (also quantized for batching)\n const rawGlowOpacity = opacity * 0.2;\n const glowOpacity = quantizeOpacity(rawGlowOpacity);\n\n return {\n x: x,\n y: window.scrollY - 5,\n radius: radius,\n glowRadius: glowRadius,\n speed:\n radius * profile.speedScale +\n noise.speed * profile.noiseSpeedScale +\n profile.speedBase,\n wind: (noise.wind - 0.5) * profile.windScale,\n opacity: opacity,\n glowOpacity: glowOpacity,\n wobble: noise.wobblePhase * TAU,\n wobbleSpeed: noise.wobbleSpeed * profile.wobbleScale + profile.wobbleBase,\n sizeRatio: sizeRatio,\n isBackground: isBackground\n };\n};\n\n/**\n * Initialize max heights array for snow accumulation with edge tapering and smoothing.\n * Exported for benchmarking.\n */\nexport const initializeMaxHeights = (\n width: number,\n baseMax: number,\n borderRadius: number,\n isBottom: boolean = false\n): number[] => {\n let maxHeights = new Array(width);\n for (let i = 0; i < width; i++) {\n let edgeFactor = 1.0;\n if (!isBottom && borderRadius > 0) {\n if (i < borderRadius) {\n edgeFactor = Math.pow(i / borderRadius, 1.2);\n } else if (i > width - borderRadius) {\n edgeFactor = Math.pow((width - i) / borderRadius, 1.2);\n }\n }\n maxHeights[i] = baseMax * edgeFactor * (0.85 + Math.random() * 0.15);\n }\n\n const smoothPasses = 4;\n for (let p = 0; p < smoothPasses; p++) {\n const smoothed = [...maxHeights];\n for (let i = 1; i < width - 1; i++) {\n smoothed[i] = (maxHeights[i - 1] + maxHeights[i] + maxHeights[i + 1]) / 3;\n }\n maxHeights = smoothed;\n }\n\n return maxHeights;\n};\n\nconst calculateCurveOffsets = (width: number, borderRadius: number, isBottom: boolean): number[] => {\n const offsets = new Array(width).fill(0);\n if (borderRadius <= 0 || isBottom) return offsets;\n\n for (let x = 0; x < width; x++) {\n let offset = 0;\n if (x < borderRadius) {\n const dist = borderRadius - x;\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n } else if (x > width - borderRadius) {\n const dist = x - (width - borderRadius);\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n }\n offsets[x] = offset;\n }\n return offsets;\n};\n\nconst calculateGravityMultipliers = (height: number): number[] => {\n const multipliers = new Array(height);\n for (let i = 0; i < height; i++) {\n const ratio = i / height;\n multipliers[i] = Math.sqrt(ratio);\n }\n return multipliers;\n};\n\nexport const initializeAccumulation = (\n accumulationMap: Map<Element, SnowAccumulation>,\n config: PhysicsConfig\n) => {\n // Scan DOM for new valid surfaces\n const elements = getAccumulationSurfaces(config.MAX_SURFACES);\n // Prune disconnected elements\n for (const [el] of accumulationMap.entries()) {\n if (!el.isConnected) {\n accumulationMap.delete(el);\n }\n }\n\n elements.forEach(({ el, type }) => {\n const existing = accumulationMap.get(el);\n const rect = el.getBoundingClientRect();\n const width = Math.ceil(rect.width);\n const isBottom = type === VAL_BOTTOM;\n\n if (existing && existing.heights.length === width) {\n existing.type = type;\n if (existing.borderRadius !== undefined) {\n const styleBuffer = window.getComputedStyle(el);\n existing.borderRadius = parseFloat(styleBuffer.borderTopLeftRadius) || 0;\n existing.curveOffsets = calculateCurveOffsets(width, existing.borderRadius, isBottom);\n // Initialize gravity multipliers if height matches but they're missing\n if (existing.leftSide.length === Math.ceil(rect.height) && !existing.sideGravityMultipliers) {\n existing.sideGravityMultipliers = calculateGravityMultipliers(existing.leftSide.length);\n }\n }\n return;\n }\n\n const height = Math.ceil(rect.height);\n const baseMax = isBottom ? config.MAX_DEPTH.BOTTOM : config.MAX_DEPTH.TOP;\n\n const styles = window.getComputedStyle(el);\n const borderRadius = parseFloat(styles.borderTopLeftRadius) || 0;\n\n const maxHeights = initializeMaxHeights(width, baseMax, borderRadius, isBottom);\n\n accumulationMap.set(el, {\n heights: existing?.heights.length === width ? existing.heights : new Array(width).fill(0),\n maxHeights,\n leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),\n rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),\n maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,\n leftMax: existing?.leftSide.length === height ? existing.leftMax : 0,\n rightMax: existing?.rightSide.length === height ? existing.rightMax : 0,\n borderRadius,\n curveOffsets: calculateCurveOffsets(width, borderRadius, isBottom),\n sideGravityMultipliers: calculateGravityMultipliers(height),\n type\n });\n });\n};\n\n/**\n * Accumulate snow on a side array and return the new max height.\n * The caller should update acc.leftMax or acc.rightMax with the returned value.\n */\nexport const accumulateSide = (\n sideArray: number[],\n rectHeight: number,\n localY: number,\n maxSideHeight: number,\n borderRadius: number,\n config: PhysicsConfig,\n currentMax: number\n): number => {\n const spread = 4;\n const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);\n let newMax = currentMax;\n\n for (let dy = -spread; dy <= spread; dy++) {\n const y = localY + dy;\n if (y >= 0 && y < sideArray.length) {\n const inTop = y < borderRadius;\n const inBottom = y > rectHeight - borderRadius;\n if (borderRadius > 0 && (inTop || inBottom)) continue;\n\n const normalizedDist = Math.abs(dy) / spread;\n const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;\n const newHeight = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);\n sideArray[y] = newHeight;\n if (newHeight > newMax) newMax = newHeight;\n }\n }\n return newMax;\n};\n\nexport const updateSnowflakes = (\n snowflakes: Snowflake[],\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number,\n worldWidth: number,\n worldHeight: number\n) => {\n // Scroll used for World -> Viewport mapping for collision\n // Flakes are in World Space.\n // DOM Rects are in Viewport Space (relative to the window).\n // To check collision, we subtract scrollX/Y from Flake coordinates to get their Viewport position.\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (let i = snowflakes.length - 1; i >= 0; i--) {\n const flake = snowflakes[i];\n\n flake.wobble += flake.wobbleSpeed * dt;\n flake.x += (flake.wind + Math.sin(flake.wobble) * 0.5) * dt;\n flake.y += (flake.speed + Math.cos(flake.wobble * 0.5) * 0.1) * dt;\n\n let landed = false;\n\n for (const item of elementRects) {\n const { rect, acc } = item;\n const isBottom = acc.type === VAL_BOTTOM;\n\n // Flake is World Space. Rect is Viewport Space.\n // Map Flake to Viewport Relative for collision check vs Rect\n const flakeViewportX = flake.x - scrollX;\n const flakeViewportY = flake.y - scrollY;\n\n // Simple Collision Check\n const isInVerticalBounds = flakeViewportY >= rect.top && flakeViewportY <= rect.bottom;\n\n // Side collisions\n // Check if flake hits the left or right edge of the element\n if (!landed && acc.maxSideHeight > 0 && !isBottom) {\n if (isInVerticalBounds) {\n const localY = Math.floor(flakeViewportY - rect.top);\n const borderRadius = acc.borderRadius;\n\n const isInTopCorner = localY < borderRadius;\n const isInBottomCorner = localY > rect.height - borderRadius;\n const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);\n\n if (flakeViewportX >= rect.left - 5 && flakeViewportX < rect.left + 3) {\n if (!isCorner) {\n acc.leftMax = accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.leftMax);\n landed = true;\n }\n }\n\n if (!landed && flakeViewportX > rect.right - 3 && flakeViewportX <= rect.right + 5) {\n if (!isCorner) {\n acc.rightMax = accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.rightMax);\n landed = true;\n }\n }\n\n if (landed) break;\n }\n }\n\n // Top/Bottom accumulation\n // Check if flake hits the primary horizontal surface\n if (flakeViewportX >= rect.left && flakeViewportX <= rect.right) {\n const localX = Math.floor(flakeViewportX - rect.left);\n const currentHeight = acc.heights[localX] || 0;\n const maxHeight = acc.maxHeights[localX] || 5;\n\n const surfaceY = isBottom ? rect.bottom - currentHeight : rect.top - currentHeight;\n\n if (flakeViewportY >= surfaceY && flakeViewportY < surfaceY + 10 && currentHeight < maxHeight) {\n const shouldAccumulate = isBottom ? Math.random() < 0.15 : true;\n\n if (shouldAccumulate) {\n const baseSpread = Math.ceil(flake.radius);\n const spread = baseSpread + Math.floor(Math.random() * 2);\n const accumRate = isBottom ? config.ACCUMULATION.BOTTOM_RATE : config.ACCUMULATION.TOP_RATE;\n const centerOffset = Math.floor(Math.random() * 3) - 1;\n\n for (let dx = -spread; dx <= spread; dx++) {\n if (Math.random() < 0.15) continue;\n const idx = localX + dx + centerOffset;\n if (idx >= 0 && idx < acc.heights.length) {\n const dist = Math.abs(dx);\n const pixelMax = acc.maxHeights[idx] || 5;\n\n const normDist = dist / spread;\n const falloff = (Math.cos(normDist * Math.PI) + 1) / 2;\n const baseAdd = 0.3 * falloff;\n\n const randomFactor = 0.8 + Math.random() * 0.4;\n const addHeight = baseAdd * randomFactor * accumRate;\n\n if (acc.heights[idx] < pixelMax && addHeight > 0) {\n acc.heights[idx] = Math.min(pixelMax, acc.heights[idx] + addHeight);\n }\n }\n }\n\n if (isBottom) {\n landed = true;\n break;\n }\n }\n\n if (!isBottom) {\n landed = true;\n break;\n }\n }\n }\n }\n\n if (landed || flake.y > worldHeight + 10 || flake.x < -20 || flake.x > worldWidth + 20) {\n snowflakes.splice(i, 1);\n }\n }\n};\n\nexport const meltAndSmoothAccumulation = (\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number\n) => {\n for (const { acc } of elementRects) {\n const meltRate = config.MELT_SPEED * dt;\n const len = acc.heights.length;\n\n // Smooth\n if (len > 2) {\n for (let i = 1; i < len - 1; i++) {\n if (acc.heights[i] > 0.05) {\n const avg = (acc.heights[i - 1] + acc.heights[i + 1]) / 2;\n acc.heights[i] = acc.heights[i] * 0.99 + avg * 0.01;\n }\n }\n }\n\n // Melt\n for (let i = 0; i < acc.heights.length; i++) {\n if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);\n }\n // Melt sides and update max values\n let leftMax = 0;\n let rightMax = 0;\n for (let i = 0; i < acc.leftSide.length; i++) {\n if (acc.leftSide[i] > 0) {\n acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);\n if (acc.leftSide[i] > leftMax) leftMax = acc.leftSide[i];\n }\n if (acc.rightSide[i] > 0) {\n acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);\n if (acc.rightSide[i] > rightMax) rightMax = acc.rightSide[i];\n }\n }\n acc.leftMax = leftMax;\n acc.rightMax = rightMax;\n }\n};\n","import { Snowflake, ElementSurface } from './types';\nimport { VAL_BOTTOM, TAU } from './constants';\n\nconst ACC_FILL_STYLE = 'rgba(255, 255, 255, 0.95)';\nconst ACC_SHADOW_COLOR = 'rgba(200, 230, 255, 0.6)';\n\n\n/**\n * Single-pass snowflake rendering for optimal cache locality.\n * Flakes are tracked in World Space.\n * The Canvas Context is translated by (-scrollX, -scrollY), effectively viewing the World.\n * \n * PERFORMANCE: All glow/core values are pre-calculated at creation time (physics.ts),\n * enabling a simple single-pass render with excellent cache performance.\n */\nexport const drawSnowflakes = (ctx: CanvasRenderingContext2D, flakes: Snowflake[]) => {\n if (flakes.length === 0) return;\n\n ctx.fillStyle = '#FFFFFF';\n\n // Single pass: Draw glow (behind) then core (front) for each flake\n for (const flake of flakes) {\n // Draw glow effect first (behind the core) - skip for background flakes\n if (!flake.isBackground) {\n ctx.globalAlpha = flake.glowOpacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.glowRadius, 0, TAU);\n ctx.fill();\n }\n\n // Draw core on top\n ctx.globalAlpha = flake.opacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius, 0, TAU);\n ctx.fill();\n }\n\n // Reset alpha once at the end\n ctx.globalAlpha = 1.0;\n};\n\nexport const drawAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 4;\n ctx.shadowOffsetY = -1;\n // Explicitly reset alpha as we modified it in drawSnowflake\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n // Iterate over all accumulation surfaces to build the single path\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (!acc.heights.some(h => h > 0.1)) continue;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\n\n // Precompute world-space coordinates (hoist additions out of loops)\n const worldLeft = rect.left + scrollX;\n const worldBaseY = baseY + scrollY;\n\n let first = true;\n const step = 2;\n const len = acc.heights.length;\n\n // Draw the uneven top surface of the snow\n for (let x = 0; x < len; x += step) {\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n if (first) {\n ctx.moveTo(px, py);\n first = false;\n } else {\n ctx.lineTo(px, py);\n }\n }\n\n if ((len - 1) % step !== 0) {\n const x = len - 1;\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n // Draw the bottom edge (aligned with element border) to close the shape\n for (let x = len - 1; x >= 0; x -= step) {\n const px = worldLeft + x;\n const py = worldBaseY + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = worldLeft + startX;\n const startPy = worldBaseY + (acc.curveOffsets[startX] || 0);\n ctx.lineTo(startPx, startPy);\n }\n\n // Fill all accumulations in one pass to batch shadow rendering\n ctx.fill();\n\n ctx.shadowBlur = 0;\n ctx.shadowOffsetY = 0;\n};\n\nexport const drawSideAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 3;\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n const drawSide = (sideArray: number[], isLeft: boolean, multipliers: number[], rect: DOMRect, dx: number, dy: number) => {\n const baseX = isLeft ? rect.left : rect.right;\n\n // Precompute world-space coordinates\n const worldBaseX = baseX + dx;\n const worldTop = rect.top + dy;\n const worldBottom = rect.bottom + dy;\n\n ctx.moveTo(worldBaseX, worldTop);\n\n // Draw the uneven side profile\n for (let y = 0; y < sideArray.length; y += 2) {\n const width = sideArray[y] || 0;\n const nextY = Math.min(y + 2, sideArray.length - 1);\n const nextWidth = sideArray[nextY] || 0;\n\n const gravityMultiplier = multipliers[y] || 0;\n\n const py = worldTop + y;\n const px = (isLeft ? worldBaseX - (width * gravityMultiplier) : worldBaseX + (width * gravityMultiplier));\n const ny = worldTop + nextY;\n const nGravityMultiplier = multipliers[nextY] || 0;\n const nx = (isLeft ? worldBaseX - (nextWidth * nGravityMultiplier) : worldBaseX + (nextWidth * nGravityMultiplier));\n\n ctx.lineTo(px, py);\n ctx.lineTo(nx, ny);\n }\n\n // Close at the bottom\n ctx.lineTo(worldBaseX, worldBottom);\n };\n\n // Scan elements and append their side snow profiles to the current path for batched rendering\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (acc.maxSideHeight === 0) continue;\n\n const hasLeftSnow = acc.leftMax > 0.3;\n const hasRightSnow = acc.rightMax > 0.3;\n\n if (!hasLeftSnow && !hasRightSnow) continue;\n\n if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n }\n\n ctx.fill();\n\n ctx.shadowBlur = 0;\n};\n","'use client';\n\nimport { useSnowfall } from './SnowfallProvider';\nimport { useEffect, useState } from 'react';\n\nexport default function DebugPanel({ defaultOpen = true }: { defaultOpen?: boolean }) {\n const { debugMode, toggleDebug, metrics } = useSnowfall();\n const [isMinimized, setIsMinimized] = useState(!defaultOpen);\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.shiftKey && e.key === 'D') {\n toggleDebug();\n }\n };\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleDebug]);\n\n const copyToClipboard = () => {\n if (metrics) {\n const data = {\n ...metrics,\n timestamp: new Date().toISOString(),\n userAgent: navigator.userAgent,\n canvasSize: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n };\n navigator.clipboard.writeText(JSON.stringify(data, null, 2));\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n };\n\n if (!debugMode) return null;\n\n return (\n <div\n data-snowfall=\"top\"\n style={{\n position: 'fixed',\n bottom: '80px',\n left: '24px',\n backgroundColor: 'rgba(15, 23, 42, 0.75)',\n backdropFilter: 'blur(16px)',\n WebkitBackdropFilter: 'blur(16px)',\n color: '#e2e8f0',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',\n fontSize: '12px',\n padding: isMinimized ? '12px' : '20px',\n borderRadius: '16px',\n zIndex: 10000,\n minWidth: isMinimized ? 'auto' : '300px',\n maxWidth: '100%',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.2), 0 8px 10px -6px rgba(0, 0, 0, 0.2)',\n transition: 'all 0.2s ease',\n }}>\n <div style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: isMinimized ? 0 : '16px',\n gap: '16px'\n }}>\n <div style={{ fontWeight: '600', color: '#fff', display: 'flex', alignItems: 'center', gap: '8px' }}>\n <span style={{ fontSize: '14px' }}>❄️</span>\n <span style={{\n background: 'linear-gradient(to right, #60a5fa, #22d3ee)',\n WebkitBackgroundClip: 'text',\n WebkitTextFillColor: 'transparent',\n fontWeight: '700'\n }}>DEBUG</span>\n </div>\n <div style={{ display: 'flex', gap: '8px' }}>\n <button\n onClick={() => setIsMinimized(!isMinimized)}\n style={{\n background: 'rgba(255, 255, 255, 0.1)',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n color: '#fff',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '10px',\n }}\n >\n {isMinimized ? '▲' : '▼'}\n </button>\n <button\n onClick={toggleDebug}\n style={{\n background: 'rgba(239, 68, 68, 0.15)',\n border: '1px solid rgba(239, 68, 68, 0.2)',\n color: '#f87171',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '12px',\n }}\n >\n ✕\n </button>\n </div>\n </div>\n\n {!isMinimized && metrics && (\n <>\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Performance\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>FPS</span>\n <span style={{ fontWeight: 'bold', color: metrics.fps < 30 ? '#f87171' : metrics.fps < 50 ? '#facc15' : '#4ade80' }}>\n {metrics.fps.toFixed(1)}\n </span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Frame Time</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.frameTime.toFixed(2)}ms</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: metrics.rafGap && metrics.rafGap > 20 ? '#fbbf24' : 'inherit' }}>rAF Gap</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.rafGap?.toFixed(1) || 0}ms</span>\n </div>\n </div>\n\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Detailed Timings\n </div>\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '4px 12px' }}>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Clear</span> <span style={{ fontFamily: 'monospace' }}>{metrics.clearTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Physics</span> <span style={{ fontFamily: 'monospace' }}>{metrics.physicsTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Draw</span> <span style={{ fontFamily: 'monospace' }}>{metrics.drawTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Scan</span> <span style={{ fontFamily: 'monospace' }}>{metrics.scanTime.toFixed(2)}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between', gridColumn: 'span 2' }}><span>Rect Update</span> <span style={{ fontFamily: 'monospace' }}>{metrics.rectUpdateTime.toFixed(2)}ms</span></div>\n </div>\n </div>\n\n <div style={{ marginBottom: '16px' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Counts\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Snowflakes</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.flakeCount} / {metrics.maxFlakes}</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span>Surfaces</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.surfaceCount}</span>\n </div>\n </div>\n\n <button\n onClick={copyToClipboard}\n style={{\n width: '100%',\n padding: '10px',\n background: copied ? 'rgba(34, 197, 94, 0.2)' : 'rgba(255, 255, 255, 0.05)',\n border: copied ? '1px solid rgba(34, 197, 94, 0.5)' : '1px solid rgba(255, 255, 255, 0.1)',\n color: copied ? '#4ade80' : '#fff',\n cursor: 'pointer',\n borderRadius: '8px',\n fontSize: '11px',\n fontWeight: '600',\n transition: 'all 0.2s',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '6px'\n }}\n >\n {copied ? '✓ COPIED' : '📋 COPY METRICS'}\n </button>\n\n <div style={{ marginTop: '12px', fontSize: '10px', color: '#64748b', textAlign: 'center' }}>\n Shift+D to toggle\n </div>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["useState","shouldAccumulate","jsx","useState","useEffect","useState","Fragment","jsx"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hdcodedev/snowfall",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Realistic snowfall effect for React with physics-based accumulation on surfaces. Features melting, wind, and smart surface detection.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -61,4 +61,4 @@
61
61
  "typescript": "^5.7.2",
62
62
  "typescript-eslint": "^8.50.1"
63
63
  }
64
- }
64
+ }