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