@hdcodedev/snowfall 1.0.7 → 1.0.8

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
@@ -202,6 +202,12 @@ var getElementRects = (accumulationMap) => {
202
202
  };
203
203
 
204
204
  // src/utils/snowfall/physics.ts
205
+ var OPACITY_BUCKETS = [0.3, 0.5, 0.7, 0.9];
206
+ var quantizeOpacity = (opacity) => {
207
+ return OPACITY_BUCKETS.reduce(
208
+ (prev, curr) => Math.abs(curr - opacity) < Math.abs(prev - opacity) ? curr : prev
209
+ );
210
+ };
205
211
  var createSnowflake = (worldWidth, config, isBackground = false) => {
206
212
  const x = Math.random() * worldWidth;
207
213
  const dna = Math.random();
@@ -237,13 +243,20 @@ var createSnowflake = (worldWidth, config, isBackground = false) => {
237
243
  wobbleScale: 0.02
238
244
  };
239
245
  const radius = profile.sizeMin + sizeRatio * profile.sizeRange;
246
+ const glowRadius = radius * 1.5;
247
+ const rawOpacity = profile.opacityBase + sizeRatio * profile.opacityScale;
248
+ const opacity = quantizeOpacity(rawOpacity);
249
+ const rawGlowOpacity = opacity * 0.2;
250
+ const glowOpacity = quantizeOpacity(rawGlowOpacity);
240
251
  return {
241
252
  x,
242
253
  y: window.scrollY - 5,
243
254
  radius,
255
+ glowRadius,
244
256
  speed: radius * profile.speedScale + noise.speed * profile.noiseSpeedScale + profile.speedBase,
245
257
  wind: (noise.wind - 0.5) * profile.windScale,
246
- opacity: profile.opacityBase + sizeRatio * profile.opacityScale,
258
+ opacity,
259
+ glowOpacity,
247
260
  wobble: noise.wobblePhase * TAU,
248
261
  wobbleSpeed: noise.wobbleSpeed * profile.wobbleScale + profile.wobbleBase,
249
262
  sizeRatio,
@@ -465,43 +478,40 @@ var drawSnowflakes = (ctx, flakes) => {
465
478
  if (flakes.length === 0) return;
466
479
  ctx.fillStyle = "#FFFFFF";
467
480
  for (const flake of flakes) {
481
+ if (!flake.isBackground) {
482
+ ctx.globalAlpha = flake.glowOpacity;
483
+ ctx.beginPath();
484
+ ctx.arc(flake.x, flake.y, flake.glowRadius, 0, TAU);
485
+ ctx.fill();
486
+ }
468
487
  ctx.globalAlpha = flake.opacity;
469
488
  ctx.beginPath();
470
489
  ctx.arc(flake.x, flake.y, flake.radius, 0, TAU);
471
490
  ctx.fill();
472
491
  }
473
- for (const flake of flakes) {
474
- if (flake.isBackground) continue;
475
- ctx.globalAlpha = flake.opacity * 0.2;
476
- ctx.beginPath();
477
- ctx.arc(flake.x, flake.y, flake.radius * 1.5, 0, TAU);
478
- ctx.fill();
479
- }
480
492
  ctx.globalAlpha = 1;
481
493
  };
482
- var drawAccumulations = (ctx, elementRects) => {
494
+ var drawAccumulations = (ctx, elementRects, scrollX, scrollY) => {
483
495
  ctx.fillStyle = ACC_FILL_STYLE;
484
496
  ctx.shadowColor = ACC_SHADOW_COLOR;
485
497
  ctx.shadowBlur = 4;
486
498
  ctx.shadowOffsetY = -1;
487
499
  ctx.globalAlpha = 1;
488
- const currentScrollX = window.scrollX;
489
- const currentScrollY = window.scrollY;
490
500
  ctx.beginPath();
491
501
  for (const item of elementRects) {
492
502
  const { rect, acc } = item;
493
503
  if (!acc.heights.some((h) => h > 0.1)) continue;
494
- const dx = currentScrollX;
495
- const dy = currentScrollY;
496
504
  const isBottom = acc.type === VAL_BOTTOM;
497
505
  const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;
506
+ const worldLeft = rect.left + scrollX;
507
+ const worldBaseY = baseY + scrollY;
498
508
  let first = true;
499
509
  const step = 2;
500
510
  const len = acc.heights.length;
501
511
  for (let x = 0; x < len; x += step) {
502
512
  const height = acc.heights[x] || 0;
503
- const px = rect.left + x + dx;
504
- const py = baseY - height + (acc.curveOffsets[x] || 0) + dy;
513
+ const px = worldLeft + x;
514
+ const py = worldBaseY - height + (acc.curveOffsets[x] || 0);
505
515
  if (first) {
506
516
  ctx.moveTo(px, py);
507
517
  first = false;
@@ -512,49 +522,50 @@ var drawAccumulations = (ctx, elementRects) => {
512
522
  if ((len - 1) % step !== 0) {
513
523
  const x = len - 1;
514
524
  const height = acc.heights[x] || 0;
515
- const px = rect.left + x + dx;
516
- const py = baseY - height + (acc.curveOffsets[x] || 0) + dy;
525
+ const px = worldLeft + x;
526
+ const py = worldBaseY - height + (acc.curveOffsets[x] || 0);
517
527
  ctx.lineTo(px, py);
518
528
  }
519
529
  for (let x = len - 1; x >= 0; x -= step) {
520
- const px = rect.left + x + dx;
521
- const py = baseY + (acc.curveOffsets[x] || 0) + dy;
530
+ const px = worldLeft + x;
531
+ const py = worldBaseY + (acc.curveOffsets[x] || 0);
522
532
  ctx.lineTo(px, py);
523
533
  }
524
534
  const startX = 0;
525
- const startPx = rect.left + startX + dx;
526
- const startPy = baseY + (acc.curveOffsets[startX] || 0) + dy;
535
+ const startPx = worldLeft + startX;
536
+ const startPy = worldBaseY + (acc.curveOffsets[startX] || 0);
527
537
  ctx.lineTo(startPx, startPy);
528
538
  }
529
539
  ctx.fill();
530
540
  ctx.shadowBlur = 0;
531
541
  ctx.shadowOffsetY = 0;
532
542
  };
533
- var drawSideAccumulations = (ctx, elementRects) => {
543
+ var drawSideAccumulations = (ctx, elementRects, scrollX, scrollY) => {
534
544
  ctx.fillStyle = ACC_FILL_STYLE;
535
545
  ctx.shadowColor = ACC_SHADOW_COLOR;
536
546
  ctx.shadowBlur = 3;
537
547
  ctx.globalAlpha = 1;
538
- const currentScrollX = window.scrollX;
539
- const currentScrollY = window.scrollY;
540
548
  ctx.beginPath();
541
549
  const drawSide = (sideArray, isLeft, multipliers, rect, dx, dy) => {
542
550
  const baseX = isLeft ? rect.left : rect.right;
543
- ctx.moveTo(baseX + dx, rect.top + dy);
551
+ const worldBaseX = baseX + dx;
552
+ const worldTop = rect.top + dy;
553
+ const worldBottom = rect.bottom + dy;
554
+ ctx.moveTo(worldBaseX, worldTop);
544
555
  for (let y = 0; y < sideArray.length; y += 2) {
545
556
  const width = sideArray[y] || 0;
546
557
  const nextY = Math.min(y + 2, sideArray.length - 1);
547
558
  const nextWidth = sideArray[nextY] || 0;
548
559
  const gravityMultiplier = multipliers[y] || 0;
549
- const py = rect.top + y + dy;
550
- const px = (isLeft ? baseX - width * gravityMultiplier : baseX + width * gravityMultiplier) + dx;
551
- const ny = rect.top + nextY + dy;
560
+ const py = worldTop + y;
561
+ const px = isLeft ? worldBaseX - width * gravityMultiplier : worldBaseX + width * gravityMultiplier;
562
+ const ny = worldTop + nextY;
552
563
  const nGravityMultiplier = multipliers[nextY] || 0;
553
- const nx = (isLeft ? baseX - nextWidth * nGravityMultiplier : baseX + nextWidth * nGravityMultiplier) + dx;
564
+ const nx = isLeft ? worldBaseX - nextWidth * nGravityMultiplier : worldBaseX + nextWidth * nGravityMultiplier;
554
565
  ctx.lineTo(px, py);
555
566
  ctx.lineTo(nx, ny);
556
567
  }
557
- ctx.lineTo(baseX + dx, rect.bottom + dy);
568
+ ctx.lineTo(worldBaseX, worldBottom);
558
569
  };
559
570
  for (const item of elementRects) {
560
571
  const { rect, acc } = item;
@@ -562,10 +573,8 @@ var drawSideAccumulations = (ctx, elementRects) => {
562
573
  const hasLeftSnow = acc.leftSide.some((h) => h > 0.3);
563
574
  const hasRightSnow = acc.rightSide.some((h) => h > 0.3);
564
575
  if (!hasLeftSnow && !hasRightSnow) continue;
565
- const dx = currentScrollX;
566
- const dy = currentScrollY;
567
- if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, dx, dy);
568
- if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, dx, dy);
576
+ if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, scrollX, scrollY);
577
+ if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, scrollX, scrollY);
569
578
  }
570
579
  ctx.fill();
571
580
  ctx.shadowBlur = 0;
@@ -584,6 +593,7 @@ function Snowfall() {
584
593
  const snowflakesRef = (0, import_react2.useRef)([]);
585
594
  const accumulationRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
586
595
  const animationIdRef = (0, import_react2.useRef)(0);
596
+ const dprRef = (0, import_react2.useRef)(1);
587
597
  const fpsFrames = (0, import_react2.useRef)([]);
588
598
  const metricsRef = (0, import_react2.useRef)({
589
599
  scanTime: 0,
@@ -617,6 +627,7 @@ function Snowfall() {
617
627
  const newWidth = window.innerWidth;
618
628
  const newHeight = window.innerHeight;
619
629
  const dpr = window.devicePixelRatio || 1;
630
+ dprRef.current = dpr;
620
631
  canvasRef.current.width = newWidth * dpr;
621
632
  canvasRef.current.height = newHeight * dpr;
622
633
  canvasRef.current.style.width = `${newWidth}px`;
@@ -672,7 +683,7 @@ function Snowfall() {
672
683
  const dt = deltaTime / 16.67;
673
684
  const frameStartTime = performance.now();
674
685
  const clearStart = performance.now();
675
- const dpr = window.devicePixelRatio || 1;
686
+ const dpr = dprRef.current;
676
687
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
677
688
  ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);
678
689
  const scrollX = window.scrollX;
@@ -697,11 +708,22 @@ function Snowfall() {
697
708
  const drawStart = performance.now();
698
709
  drawSnowflakes(ctx, snowflakes);
699
710
  if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {
700
- const isBackground = Math.random() < 0.4;
701
- snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));
711
+ const currentFps = fpsFrames.current.length;
712
+ const shouldSpawn = currentFps >= 40 || Math.random() < 0.2;
713
+ if (shouldSpawn) {
714
+ const isBackground = Math.random() < 0.4;
715
+ snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));
716
+ }
717
+ }
718
+ const viewportWidth = window.innerWidth;
719
+ const viewportHeight = window.innerHeight;
720
+ const visibleRects = elementRects.filter(
721
+ ({ rect }) => rect.right >= 0 && rect.left <= viewportWidth && rect.bottom >= 0 && rect.top <= viewportHeight
722
+ );
723
+ if (visibleRects.length > 0) {
724
+ drawAccumulations(ctx, visibleRects, scrollX, scrollY);
725
+ drawSideAccumulations(ctx, visibleRects, scrollX, scrollY);
702
726
  }
703
- drawAccumulations(ctx, elementRects);
704
- drawSideAccumulations(ctx, elementRects);
705
727
  metricsRef.current.drawTime = performance.now() - drawStart;
706
728
  metricsRef.current.frameTime = performance.now() - frameStartTime;
707
729
  if (currentTime - lastMetricsUpdate > 500) {
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 // 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\n const dpr = window.devicePixelRatio || 1;\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 dpr scaling, so we clear the logical width/height\n const dpr = window.devicePixelRatio || 1;\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\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const isBackground = Math.random() < 0.4;\n // createSnowflake uses window.scrollY, so it creates flakes in world space\n snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));\n }\n\n // Draw Accumulation\n // We draw accumulations in World Space (by adding scroll offset in draw.ts)\n // This aligns perfectly with the translated context and world-space snowflakes.\n drawAccumulations(ctx, elementRects);\n drawSideAccumulations(ctx, elementRects);\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\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\n return {\n x: x,\n y: window.scrollY - 5,\n radius: radius,\n speed:\n radius * profile.speedScale +\n noise.speed * profile.noiseSpeedScale +\n profile.speedBase,\n wind: (noise.wind - 0.5) * profile.windScale,\n opacity: profile.opacityBase + sizeRatio * profile.opacityScale,\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 * Batched snowflake rendering for optimal performance.\n * Flakes are tracked in World Space.\n * The Canvas Context is translated by (-scrollX, -scrollY), effectively viewing the World.\n */\nexport const drawSnowflakes = (ctx: CanvasRenderingContext2D, flakes: Snowflake[]) => {\n if (flakes.length === 0) return;\n\n // Hoist fillStyle outside the loop - set once for all flakes\n ctx.fillStyle = '#FFFFFF';\n\n // First pass: Draw all snowflake cores\n for (const flake of flakes) {\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 // Second pass: Draw glow effects (skip for background flakes - they're smaller/fainter)\n for (const flake of flakes) {\n if (flake.isBackground) continue;\n\n ctx.globalAlpha = flake.opacity * 0.2;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius * 1.5, 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) => {\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 const currentScrollX = window.scrollX;\n const currentScrollY = window.scrollY;\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 // Convert Viewport Rect to World Space for Drawing (match Context)\n const dx = currentScrollX;\n const dy = currentScrollY;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\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 = rect.left + x + dx;\n const py = baseY - height + (acc.curveOffsets[x] || 0) + dy;\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 = rect.left + x + dx;\n const py = baseY - height + (acc.curveOffsets[x] || 0) + dy;\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 = rect.left + x + dx;\n const py = baseY + (acc.curveOffsets[x] || 0) + dy;\n ctx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = rect.left + startX + dx;\n const startPy = baseY + (acc.curveOffsets[startX] || 0) + dy;\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) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 3;\n ctx.globalAlpha = 1.0;\n\n const currentScrollX = window.scrollX;\n const currentScrollY = window.scrollY;\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 ctx.moveTo(baseX + dx, rect.top + dy);\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 = rect.top + y + dy;\n const px = (isLeft ? baseX - (width * gravityMultiplier) : baseX + (width * gravityMultiplier)) + dx;\n const ny = rect.top + nextY + dy;\n const nGravityMultiplier = multipliers[nextY] || 0;\n const nx = (isLeft ? baseX - (nextWidth * nGravityMultiplier) : baseX + (nextWidth * nGravityMultiplier)) + dx;\n\n ctx.lineTo(px, py);\n ctx.lineTo(nx, ny);\n }\n\n // Close at the bottom\n ctx.lineTo(baseX + dx, rect.bottom + dy);\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 const dx = currentScrollX;\n const dy = currentScrollY;\n\n if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, dx, dy);\n if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, dx, dy);\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 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":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,IAAAA,gBAA4C;;;ACA5C,mBAAsE;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,sBAAkB,4BAA+C,MAAS;AAEzE,SAAS,iBAAiB,EAAE,UAAU,eAAe,MAAM,GAAoD;AAClH,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,IAAI;AAC/C,QAAM,CAAC,eAAe,gBAAgB,QAAI,uBAAwB,eAAe;AACjF,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,YAAY;AACvD,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAoC,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,4CAAC,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,cAAU,yBAAW,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,IAAAC,sBAAA;AAhRO,SAAR,WAA4B;AAC/B,QAAM,EAAE,WAAW,eAAe,WAAW,IAAI,YAAY;AAC7D,QAAM,mBAAe,sBAAO,SAAS;AACrC,QAAM,uBAAmB,sBAAO,aAAa;AAC7C,QAAM,oBAAgB,sBAAO,UAAU;AACvC,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,KAAK;AAChD,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,KAAK;AAEhD,QAAM,gBAAY,sBAA0B,IAAI;AAChD,QAAM,oBAAgB,sBAAoB,CAAC,CAAC;AAC5C,QAAM,sBAAkB,sBAAuC,oBAAI,IAAI,CAAC;AACxE,QAAM,qBAAiB,sBAAe,CAAC;AAIvC,QAAM,aAAS,sBAAO,CAAC;AAGvB,QAAM,gBAAY,sBAAiB,CAAC,CAAC;AACrC,QAAM,iBAAa,sBAAO;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,+BAAU,MAAM;AACZ,0BAAsB,MAAM,aAAa,IAAI,CAAC;AAAA,EAClD,GAAG,CAAC,CAAC;AAEL,+BAAU,MAAM;AACZ,iBAAa,UAAU;AAAA,EAC3B,GAAG,CAAC,SAAS,CAAC;AAEd,+BAAU,MAAM;AACZ,qBAAiB,UAAU;AAAA,EAC/B,GAAG,CAAC,aAAa,CAAC;AAElB,+BAAU,MAAM;AACZ,kBAAc,UAAU;AAAA,EAC5B,GAAG,CAAC,UAAU,CAAC;AAEf,+BAAU,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,6EACI;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,IAAAC,gBAAoC;AAiEpB,IAAAC,sBAAA;AA/DD,SAAR,WAA4B,EAAE,cAAc,KAAK,GAA8B;AAClF,QAAM,EAAE,WAAW,aAAa,QAAQ,IAAI,YAAY;AACxD,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,CAAC,WAAW;AAC3D,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAS,KAAK;AAE1C,+BAAU,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,sDAAC,SAAI,OAAO;AAAA,UACR,SAAS;AAAA,UACT,gBAAgB;AAAA,UAChB,YAAY;AAAA,UACZ,cAAc,cAAc,IAAI;AAAA,UAChC,KAAK;AAAA,QACT,GACI;AAAA,wDAAC,SAAI,OAAO,EAAE,YAAY,OAAO,OAAO,QAAQ,SAAS,QAAQ,YAAY,UAAU,KAAK,MAAM,GAC9F;AAAA,yDAAC,UAAK,OAAO,EAAE,UAAU,OAAO,GAAG,0BAAE;AAAA,YACrC,6CAAC,UAAK,OAAO;AAAA,cACT,YAAY;AAAA,cACZ,sBAAsB;AAAA,cACtB,qBAAqB;AAAA,cACrB,YAAY;AAAA,YAChB,GAAG,mBAAK;AAAA,aACZ;AAAA,UACA,8CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,KAAK,MAAM,GACtC;AAAA;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;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,8EACI;AAAA,wDAAC,SAAI,OAAO,EAAE,cAAc,QAAQ,eAAe,QAAQ,cAAc,mCAAmC,GACxG;AAAA,yDAAC,SAAI,OAAO,EAAE,OAAO,WAAW,cAAc,OAAO,UAAU,QAAQ,YAAY,OAAO,eAAe,SAAS,eAAe,YAAY,GAAG,yBAEhJ;AAAA,YACA,8CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,iBAAiB,cAAc,MAAM,GAChF;AAAA,2DAAC,UAAK,iBAAG;AAAA,cACT,6CAAC,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,8CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,iBAAiB,cAAc,MAAM,GAChF;AAAA,2DAAC,UAAK,wBAAU;AAAA,cAChB,8CAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,wBAAQ,UAAU,QAAQ,CAAC;AAAA,gBAAE;AAAA,iBAAE;AAAA,eAC9E;AAAA,YACA,8CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,gBAAgB,GAC3D;AAAA,2DAAC,UAAK,OAAO,EAAE,OAAO,QAAQ,UAAU,QAAQ,SAAS,KAAK,YAAY,UAAU,GAAG,qBAAO;AAAA,cAC9F,8CAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,wBAAQ,QAAQ,QAAQ,CAAC,KAAK;AAAA,gBAAE;AAAA,iBAAE;AAAA,eACjF;AAAA,aACJ;AAAA,UAEA,8CAAC,SAAI,OAAO,EAAE,cAAc,QAAQ,eAAe,QAAQ,cAAc,mCAAmC,GACxG;AAAA,yDAAC,SAAI,OAAO,EAAE,OAAO,WAAW,cAAc,OAAO,UAAU,QAAQ,YAAY,OAAO,eAAe,SAAS,eAAe,YAAY,GAAG,8BAEhJ;AAAA,YACA,8CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,qBAAqB,WAAW,KAAK,WAAW,GAC3E;AAAA,4DAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,gBAAgB,GAAG;AAAA,6DAAC,UAAK,mBAAK;AAAA,gBAAO;AAAA,gBAAC,8CAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,0BAAQ,WAAW,QAAQ,CAAC,KAAK;AAAA,kBAAE;AAAA,mBAAE;AAAA,iBAAO;AAAA,cAC5K,8CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,gBAAgB,GAAG;AAAA,6DAAC,UAAK,qBAAO;AAAA,gBAAO;AAAA,gBAAC,8CAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,0BAAQ,aAAa,QAAQ,CAAC,KAAK;AAAA,kBAAE;AAAA,mBAAE;AAAA,iBAAO;AAAA,cAChL,8CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,gBAAgB,GAAG;AAAA,6DAAC,UAAK,kBAAI;AAAA,gBAAO;AAAA,gBAAC,8CAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,0BAAQ,UAAU,QAAQ,CAAC,KAAK;AAAA,kBAAE;AAAA,mBAAE;AAAA,iBAAO;AAAA,cAC1K,8CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,gBAAgB,GAAG;AAAA,6DAAC,UAAK,kBAAI;AAAA,gBAAO;AAAA,gBAAC,8CAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,0BAAQ,SAAS,QAAQ,CAAC;AAAA,kBAAE;AAAA,mBAAE;AAAA,iBAAO;AAAA,cACpK,8CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,iBAAiB,YAAY,SAAS,GAAG;AAAA,6DAAC,UAAK,yBAAW;AAAA,gBAAO;AAAA,gBAAC,8CAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,0BAAQ,eAAe,QAAQ,CAAC;AAAA,kBAAE;AAAA,mBAAE;AAAA,iBAAO;AAAA,eAC3M;AAAA,aACJ;AAAA,UAEA,8CAAC,SAAI,OAAO,EAAE,cAAc,OAAO,GAC/B;AAAA,yDAAC,SAAI,OAAO,EAAE,OAAO,WAAW,cAAc,OAAO,UAAU,QAAQ,YAAY,OAAO,eAAe,SAAS,eAAe,YAAY,GAAG,oBAEhJ;AAAA,YACA,8CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,iBAAiB,cAAc,MAAM,GAChF;AAAA,2DAAC,UAAK,wBAAU;AAAA,cAChB,8CAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI;AAAA,wBAAQ;AAAA,gBAAW;AAAA,gBAAI,QAAQ;AAAA,iBAAU;AAAA,eACxF;AAAA,YACA,8CAAC,SAAI,OAAO,EAAE,SAAS,QAAQ,gBAAgB,gBAAgB,GAC3D;AAAA,2DAAC,UAAK,sBAAQ;AAAA,cACd,6CAAC,UAAK,OAAO,EAAE,YAAY,YAAY,GAAI,kBAAQ,cAAa;AAAA,eACpE;AAAA,aACJ;AAAA,UAEA;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,6CAAC,SAAI,OAAO,EAAE,WAAW,QAAQ,UAAU,QAAQ,OAAO,WAAW,WAAW,SAAS,GAAG,+BAE5F;AAAA,WACJ;AAAA;AAAA;AAAA,EAER;AAER;","names":["import_react","shouldAccumulate","import_jsx_runtime","import_react","import_jsx_runtime"]}
package/dist/index.mjs CHANGED
@@ -173,6 +173,12 @@ var getElementRects = (accumulationMap) => {
173
173
  };
174
174
 
175
175
  // src/utils/snowfall/physics.ts
176
+ var OPACITY_BUCKETS = [0.3, 0.5, 0.7, 0.9];
177
+ var quantizeOpacity = (opacity) => {
178
+ return OPACITY_BUCKETS.reduce(
179
+ (prev, curr) => Math.abs(curr - opacity) < Math.abs(prev - opacity) ? curr : prev
180
+ );
181
+ };
176
182
  var createSnowflake = (worldWidth, config, isBackground = false) => {
177
183
  const x = Math.random() * worldWidth;
178
184
  const dna = Math.random();
@@ -208,13 +214,20 @@ var createSnowflake = (worldWidth, config, isBackground = false) => {
208
214
  wobbleScale: 0.02
209
215
  };
210
216
  const radius = profile.sizeMin + sizeRatio * profile.sizeRange;
217
+ const glowRadius = radius * 1.5;
218
+ const rawOpacity = profile.opacityBase + sizeRatio * profile.opacityScale;
219
+ const opacity = quantizeOpacity(rawOpacity);
220
+ const rawGlowOpacity = opacity * 0.2;
221
+ const glowOpacity = quantizeOpacity(rawGlowOpacity);
211
222
  return {
212
223
  x,
213
224
  y: window.scrollY - 5,
214
225
  radius,
226
+ glowRadius,
215
227
  speed: radius * profile.speedScale + noise.speed * profile.noiseSpeedScale + profile.speedBase,
216
228
  wind: (noise.wind - 0.5) * profile.windScale,
217
- opacity: profile.opacityBase + sizeRatio * profile.opacityScale,
229
+ opacity,
230
+ glowOpacity,
218
231
  wobble: noise.wobblePhase * TAU,
219
232
  wobbleSpeed: noise.wobbleSpeed * profile.wobbleScale + profile.wobbleBase,
220
233
  sizeRatio,
@@ -436,43 +449,40 @@ var drawSnowflakes = (ctx, flakes) => {
436
449
  if (flakes.length === 0) return;
437
450
  ctx.fillStyle = "#FFFFFF";
438
451
  for (const flake of flakes) {
452
+ if (!flake.isBackground) {
453
+ ctx.globalAlpha = flake.glowOpacity;
454
+ ctx.beginPath();
455
+ ctx.arc(flake.x, flake.y, flake.glowRadius, 0, TAU);
456
+ ctx.fill();
457
+ }
439
458
  ctx.globalAlpha = flake.opacity;
440
459
  ctx.beginPath();
441
460
  ctx.arc(flake.x, flake.y, flake.radius, 0, TAU);
442
461
  ctx.fill();
443
462
  }
444
- for (const flake of flakes) {
445
- if (flake.isBackground) continue;
446
- ctx.globalAlpha = flake.opacity * 0.2;
447
- ctx.beginPath();
448
- ctx.arc(flake.x, flake.y, flake.radius * 1.5, 0, TAU);
449
- ctx.fill();
450
- }
451
463
  ctx.globalAlpha = 1;
452
464
  };
453
- var drawAccumulations = (ctx, elementRects) => {
465
+ var drawAccumulations = (ctx, elementRects, scrollX, scrollY) => {
454
466
  ctx.fillStyle = ACC_FILL_STYLE;
455
467
  ctx.shadowColor = ACC_SHADOW_COLOR;
456
468
  ctx.shadowBlur = 4;
457
469
  ctx.shadowOffsetY = -1;
458
470
  ctx.globalAlpha = 1;
459
- const currentScrollX = window.scrollX;
460
- const currentScrollY = window.scrollY;
461
471
  ctx.beginPath();
462
472
  for (const item of elementRects) {
463
473
  const { rect, acc } = item;
464
474
  if (!acc.heights.some((h) => h > 0.1)) continue;
465
- const dx = currentScrollX;
466
- const dy = currentScrollY;
467
475
  const isBottom = acc.type === VAL_BOTTOM;
468
476
  const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;
477
+ const worldLeft = rect.left + scrollX;
478
+ const worldBaseY = baseY + scrollY;
469
479
  let first = true;
470
480
  const step = 2;
471
481
  const len = acc.heights.length;
472
482
  for (let x = 0; x < len; x += step) {
473
483
  const height = acc.heights[x] || 0;
474
- const px = rect.left + x + dx;
475
- const py = baseY - height + (acc.curveOffsets[x] || 0) + dy;
484
+ const px = worldLeft + x;
485
+ const py = worldBaseY - height + (acc.curveOffsets[x] || 0);
476
486
  if (first) {
477
487
  ctx.moveTo(px, py);
478
488
  first = false;
@@ -483,49 +493,50 @@ var drawAccumulations = (ctx, elementRects) => {
483
493
  if ((len - 1) % step !== 0) {
484
494
  const x = len - 1;
485
495
  const height = acc.heights[x] || 0;
486
- const px = rect.left + x + dx;
487
- const py = baseY - height + (acc.curveOffsets[x] || 0) + dy;
496
+ const px = worldLeft + x;
497
+ const py = worldBaseY - height + (acc.curveOffsets[x] || 0);
488
498
  ctx.lineTo(px, py);
489
499
  }
490
500
  for (let x = len - 1; x >= 0; x -= step) {
491
- const px = rect.left + x + dx;
492
- const py = baseY + (acc.curveOffsets[x] || 0) + dy;
501
+ const px = worldLeft + x;
502
+ const py = worldBaseY + (acc.curveOffsets[x] || 0);
493
503
  ctx.lineTo(px, py);
494
504
  }
495
505
  const startX = 0;
496
- const startPx = rect.left + startX + dx;
497
- const startPy = baseY + (acc.curveOffsets[startX] || 0) + dy;
506
+ const startPx = worldLeft + startX;
507
+ const startPy = worldBaseY + (acc.curveOffsets[startX] || 0);
498
508
  ctx.lineTo(startPx, startPy);
499
509
  }
500
510
  ctx.fill();
501
511
  ctx.shadowBlur = 0;
502
512
  ctx.shadowOffsetY = 0;
503
513
  };
504
- var drawSideAccumulations = (ctx, elementRects) => {
514
+ var drawSideAccumulations = (ctx, elementRects, scrollX, scrollY) => {
505
515
  ctx.fillStyle = ACC_FILL_STYLE;
506
516
  ctx.shadowColor = ACC_SHADOW_COLOR;
507
517
  ctx.shadowBlur = 3;
508
518
  ctx.globalAlpha = 1;
509
- const currentScrollX = window.scrollX;
510
- const currentScrollY = window.scrollY;
511
519
  ctx.beginPath();
512
520
  const drawSide = (sideArray, isLeft, multipliers, rect, dx, dy) => {
513
521
  const baseX = isLeft ? rect.left : rect.right;
514
- ctx.moveTo(baseX + dx, rect.top + dy);
522
+ const worldBaseX = baseX + dx;
523
+ const worldTop = rect.top + dy;
524
+ const worldBottom = rect.bottom + dy;
525
+ ctx.moveTo(worldBaseX, worldTop);
515
526
  for (let y = 0; y < sideArray.length; y += 2) {
516
527
  const width = sideArray[y] || 0;
517
528
  const nextY = Math.min(y + 2, sideArray.length - 1);
518
529
  const nextWidth = sideArray[nextY] || 0;
519
530
  const gravityMultiplier = multipliers[y] || 0;
520
- const py = rect.top + y + dy;
521
- const px = (isLeft ? baseX - width * gravityMultiplier : baseX + width * gravityMultiplier) + dx;
522
- const ny = rect.top + nextY + dy;
531
+ const py = worldTop + y;
532
+ const px = isLeft ? worldBaseX - width * gravityMultiplier : worldBaseX + width * gravityMultiplier;
533
+ const ny = worldTop + nextY;
523
534
  const nGravityMultiplier = multipliers[nextY] || 0;
524
- const nx = (isLeft ? baseX - nextWidth * nGravityMultiplier : baseX + nextWidth * nGravityMultiplier) + dx;
535
+ const nx = isLeft ? worldBaseX - nextWidth * nGravityMultiplier : worldBaseX + nextWidth * nGravityMultiplier;
525
536
  ctx.lineTo(px, py);
526
537
  ctx.lineTo(nx, ny);
527
538
  }
528
- ctx.lineTo(baseX + dx, rect.bottom + dy);
539
+ ctx.lineTo(worldBaseX, worldBottom);
529
540
  };
530
541
  for (const item of elementRects) {
531
542
  const { rect, acc } = item;
@@ -533,10 +544,8 @@ var drawSideAccumulations = (ctx, elementRects) => {
533
544
  const hasLeftSnow = acc.leftSide.some((h) => h > 0.3);
534
545
  const hasRightSnow = acc.rightSide.some((h) => h > 0.3);
535
546
  if (!hasLeftSnow && !hasRightSnow) continue;
536
- const dx = currentScrollX;
537
- const dy = currentScrollY;
538
- if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, dx, dy);
539
- if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, dx, dy);
547
+ if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, scrollX, scrollY);
548
+ if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, scrollX, scrollY);
540
549
  }
541
550
  ctx.fill();
542
551
  ctx.shadowBlur = 0;
@@ -555,6 +564,7 @@ function Snowfall() {
555
564
  const snowflakesRef = useRef([]);
556
565
  const accumulationRef = useRef(/* @__PURE__ */ new Map());
557
566
  const animationIdRef = useRef(0);
567
+ const dprRef = useRef(1);
558
568
  const fpsFrames = useRef([]);
559
569
  const metricsRef = useRef({
560
570
  scanTime: 0,
@@ -588,6 +598,7 @@ function Snowfall() {
588
598
  const newWidth = window.innerWidth;
589
599
  const newHeight = window.innerHeight;
590
600
  const dpr = window.devicePixelRatio || 1;
601
+ dprRef.current = dpr;
591
602
  canvasRef.current.width = newWidth * dpr;
592
603
  canvasRef.current.height = newHeight * dpr;
593
604
  canvasRef.current.style.width = `${newWidth}px`;
@@ -643,7 +654,7 @@ function Snowfall() {
643
654
  const dt = deltaTime / 16.67;
644
655
  const frameStartTime = performance.now();
645
656
  const clearStart = performance.now();
646
- const dpr = window.devicePixelRatio || 1;
657
+ const dpr = dprRef.current;
647
658
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
648
659
  ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);
649
660
  const scrollX = window.scrollX;
@@ -668,11 +679,22 @@ function Snowfall() {
668
679
  const drawStart = performance.now();
669
680
  drawSnowflakes(ctx, snowflakes);
670
681
  if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {
671
- const isBackground = Math.random() < 0.4;
672
- snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));
682
+ const currentFps = fpsFrames.current.length;
683
+ const shouldSpawn = currentFps >= 40 || Math.random() < 0.2;
684
+ if (shouldSpawn) {
685
+ const isBackground = Math.random() < 0.4;
686
+ snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));
687
+ }
688
+ }
689
+ const viewportWidth = window.innerWidth;
690
+ const viewportHeight = window.innerHeight;
691
+ const visibleRects = elementRects.filter(
692
+ ({ rect }) => rect.right >= 0 && rect.left <= viewportWidth && rect.bottom >= 0 && rect.top <= viewportHeight
693
+ );
694
+ if (visibleRects.length > 0) {
695
+ drawAccumulations(ctx, visibleRects, scrollX, scrollY);
696
+ drawSideAccumulations(ctx, visibleRects, scrollX, scrollY);
673
697
  }
674
- drawAccumulations(ctx, elementRects);
675
- drawSideAccumulations(ctx, elementRects);
676
698
  metricsRef.current.drawTime = performance.now() - drawStart;
677
699
  metricsRef.current.frameTime = performance.now() - frameStartTime;
678
700
  if (currentTime - lastMetricsUpdate > 500) {
@@ -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 // 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\n const dpr = window.devicePixelRatio || 1;\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 dpr scaling, so we clear the logical width/height\n const dpr = window.devicePixelRatio || 1;\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\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const isBackground = Math.random() < 0.4;\n // createSnowflake uses window.scrollY, so it creates flakes in world space\n snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));\n }\n\n // Draw Accumulation\n // We draw accumulations in World Space (by adding scroll offset in draw.ts)\n // This aligns perfectly with the translated context and world-space snowflakes.\n drawAccumulations(ctx, elementRects);\n drawSideAccumulations(ctx, elementRects);\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\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\n return {\n x: x,\n y: window.scrollY - 5,\n radius: radius,\n speed:\n radius * profile.speedScale +\n noise.speed * profile.noiseSpeedScale +\n profile.speedBase,\n wind: (noise.wind - 0.5) * profile.windScale,\n opacity: profile.opacityBase + sizeRatio * profile.opacityScale,\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 * Batched snowflake rendering for optimal performance.\n * Flakes are tracked in World Space.\n * The Canvas Context is translated by (-scrollX, -scrollY), effectively viewing the World.\n */\nexport const drawSnowflakes = (ctx: CanvasRenderingContext2D, flakes: Snowflake[]) => {\n if (flakes.length === 0) return;\n\n // Hoist fillStyle outside the loop - set once for all flakes\n ctx.fillStyle = '#FFFFFF';\n\n // First pass: Draw all snowflake cores\n for (const flake of flakes) {\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 // Second pass: Draw glow effects (skip for background flakes - they're smaller/fainter)\n for (const flake of flakes) {\n if (flake.isBackground) continue;\n\n ctx.globalAlpha = flake.opacity * 0.2;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius * 1.5, 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) => {\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 const currentScrollX = window.scrollX;\n const currentScrollY = window.scrollY;\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 // Convert Viewport Rect to World Space for Drawing (match Context)\n const dx = currentScrollX;\n const dy = currentScrollY;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\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 = rect.left + x + dx;\n const py = baseY - height + (acc.curveOffsets[x] || 0) + dy;\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 = rect.left + x + dx;\n const py = baseY - height + (acc.curveOffsets[x] || 0) + dy;\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 = rect.left + x + dx;\n const py = baseY + (acc.curveOffsets[x] || 0) + dy;\n ctx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = rect.left + startX + dx;\n const startPy = baseY + (acc.curveOffsets[startX] || 0) + dy;\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) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 3;\n ctx.globalAlpha = 1.0;\n\n const currentScrollX = window.scrollX;\n const currentScrollY = window.scrollY;\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 ctx.moveTo(baseX + dx, rect.top + dy);\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 = rect.top + y + dy;\n const px = (isLeft ? baseX - (width * gravityMultiplier) : baseX + (width * gravityMultiplier)) + dx;\n const ny = rect.top + nextY + dy;\n const nGravityMultiplier = multipliers[nextY] || 0;\n const nx = (isLeft ? baseX - (nextWidth * nGravityMultiplier) : baseX + (nextWidth * nGravityMultiplier)) + dx;\n\n ctx.lineTo(px, py);\n ctx.lineTo(nx, ny);\n }\n\n // Close at the bottom\n ctx.lineTo(baseX + dx, rect.bottom + dy);\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 const dx = currentScrollX;\n const dy = currentScrollY;\n\n if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, dx, dy);\n if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, dx, dy);\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"]}
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":"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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.7",
3
+ "version": "1.0.8",
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",