@hdcodedev/snowfall 1.0.14 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,1231 +1,2 @@
1
- "use strict";
2
- "use client";
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
-
21
- // src/index.ts
22
- var index_exports = {};
23
- __export(index_exports, {
24
- DEFAULT_PHYSICS: () => DEFAULT_PHYSICS,
25
- DebugPanel: () => DebugPanel,
26
- Snowfall: () => Snowfall,
27
- SnowfallProvider: () => SnowfallProvider,
28
- useSnowfall: () => useSnowfall
29
- });
30
- module.exports = __toCommonJS(index_exports);
31
-
32
- // src/components/Snowfall.tsx
33
- var import_react5 = require("react");
34
-
35
- // src/components/SnowfallProvider.tsx
36
- var import_react = require("react");
37
- var import_jsx_runtime = require("react/jsx-runtime");
38
- var DEFAULT_PHYSICS = {
39
- MAX_FLAKES: 1e3,
40
- MELT_SPEED: 1e-5,
41
- WIND_STRENGTH: 1.5,
42
- ACCUMULATION: {
43
- SIDE_RATE: 1,
44
- TOP_RATE: 5,
45
- BOTTOM_RATE: 5
46
- },
47
- MAX_DEPTH: {
48
- TOP: 100,
49
- BOTTOM: 50,
50
- SIDE: 20
51
- },
52
- FLAKE_SIZE: {
53
- MIN: 0.5,
54
- MAX: 1.6
55
- },
56
- MAX_SURFACES: 15,
57
- COLLISION_CHECK_RATE: 0.3,
58
- // 30% of snowflakes check collisions per frame
59
- MAX_RENDER_DPR: 1.25
60
- };
61
- var SnowfallContext = (0, import_react.createContext)(void 0);
62
- function SnowfallProvider({ children, initialDebug = false, initialEnabled = true }) {
63
- const [isEnabled, setIsEnabled] = (0, import_react.useState)(initialEnabled);
64
- const [physicsConfig, setPhysicsConfig] = (0, import_react.useState)(DEFAULT_PHYSICS);
65
- const [debugMode, setDebugMode] = (0, import_react.useState)(initialDebug);
66
- const [metrics, setMetrics] = (0, import_react.useState)(null);
67
- const toggleSnow = () => {
68
- setIsEnabled((prev) => !prev);
69
- };
70
- const toggleDebug = () => {
71
- setDebugMode((prev) => !prev);
72
- };
73
- const updatePhysicsConfig = (config) => {
74
- setPhysicsConfig((prev) => ({
75
- ...prev,
76
- ...config,
77
- ACCUMULATION: {
78
- ...prev.ACCUMULATION,
79
- ...config.ACCUMULATION || {}
80
- },
81
- MAX_DEPTH: {
82
- ...prev.MAX_DEPTH,
83
- ...config.MAX_DEPTH || {}
84
- },
85
- FLAKE_SIZE: {
86
- ...prev.FLAKE_SIZE,
87
- ...config.FLAKE_SIZE || {}
88
- }
89
- }));
90
- };
91
- const resetPhysics = () => {
92
- setPhysicsConfig(DEFAULT_PHYSICS);
93
- };
94
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SnowfallContext.Provider, { value: {
95
- isEnabled,
96
- toggleSnow,
97
- physicsConfig,
98
- updatePhysicsConfig,
99
- resetPhysics,
100
- debugMode,
101
- toggleDebug,
102
- metrics,
103
- setMetrics
104
- }, children });
105
- }
106
- function useSnowfall() {
107
- const context = (0, import_react.useContext)(SnowfallContext);
108
- if (context === void 0) {
109
- throw new Error("useSnowfall must be used within a SnowfallProvider");
110
- }
111
- return context;
112
- }
113
-
114
- // src/core/constants.ts
115
- var ATTR_SNOWFALL = "data-snowfall";
116
- var VAL_IGNORE = "ignore";
117
- var VAL_TOP = "top";
118
- var VAL_BOTTOM = "bottom";
119
- var TAG_HEADER = "header";
120
- var TAG_FOOTER = "footer";
121
- var ROLE_BANNER = "banner";
122
- var ROLE_CONTENTINFO = "contentinfo";
123
- var TAU = Math.PI * 2;
124
-
125
- // src/core/dom.ts
126
- var BOTTOM_TAGS = [TAG_HEADER];
127
- var BOTTOM_ROLES = [ROLE_BANNER];
128
- var AUTO_DETECT_TAGS = [TAG_HEADER, TAG_FOOTER, "article", "section", "aside", "nav"];
129
- var AUTO_DETECT_ROLES = [`[role="${ROLE_BANNER}"]`, `[role="${ROLE_CONTENTINFO}"]`, '[role="main"]'];
130
- var AUTO_DETECT_CLASSES = [
131
- ".card",
132
- '[class*="card"]',
133
- '[class*="Card"]',
134
- '[class*="bg-"]',
135
- '[class*="shadow-"]',
136
- '[class*="rounded-"]'
137
- ];
138
- var getElementType = (el) => {
139
- const tagName = el.tagName.toLowerCase();
140
- if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;
141
- const role = el.getAttribute("role");
142
- if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;
143
- return VAL_TOP;
144
- };
145
- var shouldAccumulate = (el, precomputedStyle) => {
146
- if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;
147
- if (el.hasAttribute(ATTR_SNOWFALL)) return true;
148
- const styles = precomputedStyle || window.getComputedStyle(el);
149
- const isVisible = styles.display !== "none" && styles.visibility !== "hidden" && parseFloat(styles.opacity) > 0.1;
150
- if (!isVisible) return false;
151
- const bgColor = styles.backgroundColor;
152
- const hasBackground = bgColor !== "rgba(0, 0, 0, 0)" && bgColor !== "transparent";
153
- const hasBorder = parseFloat(styles.borderWidth) > 0 && styles.borderColor !== "transparent" && styles.borderColor !== "rgba(0, 0, 0, 0)" && styles.borderStyle !== "none";
154
- const hasBoxShadow = styles.boxShadow !== "none";
155
- const hasFilter = styles.filter !== "none" && styles.filter.includes("drop-shadow");
156
- const hasBackdropFilter = styles.backdropFilter !== "none";
157
- return hasBackground || hasBorder || hasBoxShadow || hasFilter || hasBackdropFilter;
158
- };
159
- var getAccumulationSurfaces = (maxSurfaces = 5) => {
160
- const surfaces = [];
161
- const seen = /* @__PURE__ */ new Set();
162
- const candidates = document.querySelectorAll(
163
- [
164
- `[${ATTR_SNOWFALL}]`,
165
- ...AUTO_DETECT_TAGS,
166
- ...AUTO_DETECT_ROLES,
167
- ...AUTO_DETECT_CLASSES
168
- ].join(", ")
169
- );
170
- for (const el of candidates) {
171
- if (surfaces.length >= maxSurfaces) break;
172
- if (seen.has(el)) continue;
173
- const manualOverride = el.getAttribute(ATTR_SNOWFALL);
174
- if (manualOverride === VAL_IGNORE) continue;
175
- const isManuallyIncluded = manualOverride !== null;
176
- const styles = window.getComputedStyle(el);
177
- const isVisible = styles.display !== "none" && styles.visibility !== "hidden" && parseFloat(styles.opacity) > 0.1;
178
- if (!isVisible && !isManuallyIncluded) continue;
179
- const rect = el.getBoundingClientRect();
180
- const hasSize = rect.width >= 100 && rect.height >= 50;
181
- if (!hasSize && !isManuallyIncluded) continue;
182
- const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;
183
- const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());
184
- const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute("role") || "");
185
- const isBottomSurface = isBottomTag || isBottomRole || manualOverride === VAL_BOTTOM;
186
- if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) continue;
187
- if (shouldAccumulate(el, styles)) {
188
- let type = getElementType(el);
189
- if (manualOverride === VAL_BOTTOM) type = VAL_BOTTOM;
190
- else if (manualOverride === VAL_TOP) type = VAL_TOP;
191
- surfaces.push({ el, type });
192
- seen.add(el);
193
- }
194
- }
195
- return surfaces;
196
- };
197
- var getElementRects = (accumulationMap) => {
198
- const elementRects = [];
199
- for (const [el, acc] of accumulationMap.entries()) {
200
- if (!el.isConnected) continue;
201
- const rect = el.getBoundingClientRect();
202
- elementRects.push({ el, rect, acc });
203
- }
204
- return elementRects;
205
- };
206
-
207
- // src/core/physics.ts
208
- var OPACITY_BUCKETS = [0.3, 0.5, 0.7, 0.9];
209
- var quantizeOpacity = (opacity) => {
210
- return OPACITY_BUCKETS.reduce(
211
- (prev, curr) => Math.abs(curr - opacity) < Math.abs(prev - opacity) ? curr : prev
212
- );
213
- };
214
- var createSnowflake = (worldWidth, config, isBackground = false) => {
215
- const x = Math.random() * worldWidth;
216
- const dna = Math.random();
217
- const noise = {
218
- speed: dna * 13 % 1,
219
- wind: dna * 7 % 1,
220
- wobblePhase: dna * 23 % 1,
221
- wobbleSpeed: dna * 5 % 1
222
- };
223
- const { MIN, MAX } = config.FLAKE_SIZE;
224
- const sizeRatio = dna;
225
- const profile = isBackground ? {
226
- sizeMin: MIN * 0.6,
227
- sizeRange: (MAX - MIN) * 0.4,
228
- speedBase: 0.2,
229
- speedScale: 0.3,
230
- noiseSpeedScale: 0.2,
231
- windScale: config.WIND_STRENGTH * 0.625,
232
- opacityBase: 0.2,
233
- opacityScale: 0.2,
234
- wobbleBase: 5e-3,
235
- wobbleScale: 0.015
236
- } : {
237
- sizeMin: MIN,
238
- sizeRange: MAX - MIN,
239
- speedBase: 0.5,
240
- speedScale: 0.5,
241
- noiseSpeedScale: 0.3,
242
- windScale: config.WIND_STRENGTH,
243
- opacityBase: 0.5,
244
- opacityScale: 0.3,
245
- wobbleBase: 0.01,
246
- wobbleScale: 0.02
247
- };
248
- const radius = profile.sizeMin + sizeRatio * profile.sizeRange;
249
- const glowRadius = radius * 1.5;
250
- const rawOpacity = profile.opacityBase + sizeRatio * profile.opacityScale;
251
- const opacity = quantizeOpacity(rawOpacity);
252
- const rawGlowOpacity = opacity * 0.2;
253
- const glowOpacity = quantizeOpacity(rawGlowOpacity);
254
- const initialWobble = noise.wobblePhase * TAU;
255
- return {
256
- x,
257
- y: window.scrollY - 5,
258
- radius,
259
- glowRadius,
260
- speed: radius * profile.speedScale + noise.speed * profile.noiseSpeedScale + profile.speedBase,
261
- wind: (noise.wind - 0.5) * profile.windScale,
262
- opacity,
263
- glowOpacity,
264
- wobble: initialWobble,
265
- wobbleSpeed: noise.wobbleSpeed * profile.wobbleScale + profile.wobbleBase,
266
- sizeRatio,
267
- isBackground
268
- };
269
- };
270
- var initializeMaxHeights = (width, baseMax, borderRadius, isBottom = false) => {
271
- let maxHeights = new Array(width);
272
- for (let i = 0; i < width; i++) {
273
- let edgeFactor = 1;
274
- if (!isBottom && borderRadius > 0) {
275
- if (i < borderRadius) {
276
- edgeFactor = Math.pow(i / borderRadius, 1.2);
277
- } else if (i > width - borderRadius) {
278
- edgeFactor = Math.pow((width - i) / borderRadius, 1.2);
279
- }
280
- }
281
- maxHeights[i] = baseMax * edgeFactor * (0.85 + Math.random() * 0.15);
282
- }
283
- const smoothPasses = 4;
284
- for (let p = 0; p < smoothPasses; p++) {
285
- const smoothed = [...maxHeights];
286
- for (let i = 1; i < width - 1; i++) {
287
- smoothed[i] = (maxHeights[i - 1] + maxHeights[i] + maxHeights[i + 1]) / 3;
288
- }
289
- maxHeights = smoothed;
290
- }
291
- return maxHeights;
292
- };
293
- var calculateCurveOffsets = (width, borderRadius, isBottom) => {
294
- const offsets = new Array(width).fill(0);
295
- if (borderRadius <= 0 || isBottom) return offsets;
296
- for (let x = 0; x < width; x++) {
297
- let offset = 0;
298
- if (x < borderRadius) {
299
- const dist = borderRadius - x;
300
- offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));
301
- } else if (x > width - borderRadius) {
302
- const dist = x - (width - borderRadius);
303
- offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));
304
- }
305
- offsets[x] = offset;
306
- }
307
- return offsets;
308
- };
309
- var calculateGravityMultipliers = (height) => {
310
- const multipliers = new Array(height);
311
- for (let i = 0; i < height; i++) {
312
- const ratio = i / height;
313
- multipliers[i] = Math.sqrt(ratio);
314
- }
315
- return multipliers;
316
- };
317
- var initializeAccumulation = (accumulationMap, config) => {
318
- const elements = getAccumulationSurfaces(config.MAX_SURFACES);
319
- for (const [el] of accumulationMap.entries()) {
320
- if (!el.isConnected) {
321
- accumulationMap.delete(el);
322
- }
323
- }
324
- elements.forEach(({ el, type }) => {
325
- const existing = accumulationMap.get(el);
326
- const rect = el.getBoundingClientRect();
327
- const width = Math.ceil(rect.width);
328
- const isBottom = type === VAL_BOTTOM;
329
- if (existing && existing.heights.length === width) {
330
- existing.type = type;
331
- if (existing.borderRadius !== void 0) {
332
- const styleBuffer = window.getComputedStyle(el);
333
- existing.borderRadius = parseFloat(styleBuffer.borderTopLeftRadius) || 0;
334
- existing.curveOffsets = calculateCurveOffsets(width, existing.borderRadius, isBottom);
335
- if (existing.leftSide.length === Math.ceil(rect.height) && !existing.sideGravityMultipliers) {
336
- existing.sideGravityMultipliers = calculateGravityMultipliers(existing.leftSide.length);
337
- }
338
- }
339
- return;
340
- }
341
- const height = Math.ceil(rect.height);
342
- const baseMax = isBottom ? config.MAX_DEPTH.BOTTOM : config.MAX_DEPTH.TOP;
343
- const styles = window.getComputedStyle(el);
344
- const borderRadius = parseFloat(styles.borderTopLeftRadius) || 0;
345
- const maxHeights = initializeMaxHeights(width, baseMax, borderRadius, isBottom);
346
- accumulationMap.set(el, {
347
- heights: existing?.heights.length === width ? existing.heights : new Array(width).fill(0),
348
- maxHeights,
349
- leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),
350
- rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),
351
- maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,
352
- leftMax: existing?.leftSide.length === height ? existing.leftMax : 0,
353
- rightMax: existing?.rightSide.length === height ? existing.rightMax : 0,
354
- borderRadius,
355
- curveOffsets: calculateCurveOffsets(width, borderRadius, isBottom),
356
- sideGravityMultipliers: calculateGravityMultipliers(height),
357
- type
358
- });
359
- });
360
- };
361
- var accumulateSide = (sideArray, rectHeight, localY, maxSideHeight, borderRadius, config, currentMax) => {
362
- const spread = 4;
363
- const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);
364
- let newMax = currentMax;
365
- for (let dy = -spread; dy <= spread; dy++) {
366
- const y = localY + dy;
367
- if (y >= 0 && y < sideArray.length) {
368
- const inTop = y < borderRadius;
369
- const inBottom = y > rectHeight - borderRadius;
370
- if (borderRadius > 0 && (inTop || inBottom)) continue;
371
- const normalizedDist = Math.abs(dy) / spread;
372
- const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;
373
- const newHeight = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);
374
- sideArray[y] = newHeight;
375
- if (newHeight > newMax) newMax = newHeight;
376
- }
377
- }
378
- return newMax;
379
- };
380
- var updateSnowflakePosition = (flake, dt) => {
381
- flake.wobble += flake.wobbleSpeed * dt;
382
- flake.x += (flake.wind + Math.sin(flake.wobble) * 0.5) * dt;
383
- flake.y += (flake.speed + Math.cos(flake.wobble * 0.5) * 0.1) * dt;
384
- };
385
- var checkSideCollision = (flakeViewportX, flakeViewportY, rect, acc, config) => {
386
- const isInVerticalBounds = flakeViewportY >= rect.top && flakeViewportY <= rect.bottom;
387
- if (!isInVerticalBounds || acc.maxSideHeight <= 0) {
388
- return false;
389
- }
390
- const localY = Math.floor(flakeViewportY - rect.top);
391
- const borderRadius = acc.borderRadius;
392
- const isInTopCorner = localY < borderRadius;
393
- const isInBottomCorner = localY > rect.height - borderRadius;
394
- const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);
395
- if (isCorner) {
396
- return false;
397
- }
398
- if (flakeViewportX >= rect.left - 5 && flakeViewportX < rect.left + 3) {
399
- acc.leftMax = accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.leftMax);
400
- return true;
401
- }
402
- if (flakeViewportX > rect.right - 3 && flakeViewportX <= rect.right + 5) {
403
- acc.rightMax = accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.rightMax);
404
- return true;
405
- }
406
- return false;
407
- };
408
- var checkSurfaceCollision = (flake, flakeViewportX, flakeViewportY, rect, acc, isBottom, config) => {
409
- if (flakeViewportX < rect.left || flakeViewportX > rect.right) {
410
- return false;
411
- }
412
- const localX = Math.floor(flakeViewportX - rect.left);
413
- const currentHeight = acc.heights[localX] || 0;
414
- const maxHeight = acc.maxHeights[localX] || 5;
415
- const surfaceY = isBottom ? rect.bottom - currentHeight : rect.top - currentHeight;
416
- if (flakeViewportY < surfaceY || flakeViewportY >= surfaceY + 10 || currentHeight >= maxHeight) {
417
- return false;
418
- }
419
- const shouldAccumulate2 = isBottom ? Math.random() < 0.15 : true;
420
- if (shouldAccumulate2) {
421
- const baseSpread = Math.ceil(flake.radius);
422
- const spread = baseSpread + Math.floor(Math.random() * 2);
423
- const accumRate = isBottom ? config.ACCUMULATION.BOTTOM_RATE : config.ACCUMULATION.TOP_RATE;
424
- const centerOffset = Math.floor(Math.random() * 3) - 1;
425
- for (let dx = -spread; dx <= spread; dx++) {
426
- if (Math.random() < 0.15) continue;
427
- const idx = localX + dx + centerOffset;
428
- if (idx >= 0 && idx < acc.heights.length) {
429
- const dist = Math.abs(dx);
430
- const pixelMax = acc.maxHeights[idx] || 5;
431
- const normDist = dist / spread;
432
- const falloff = (Math.cos(normDist * Math.PI) + 1) / 2;
433
- const baseAdd = 0.3 * falloff;
434
- const randomFactor = 0.8 + Math.random() * 0.4;
435
- const addHeight = baseAdd * randomFactor * accumRate;
436
- if (acc.heights[idx] < pixelMax && addHeight > 0) {
437
- acc.heights[idx] = Math.min(pixelMax, acc.heights[idx] + addHeight);
438
- }
439
- }
440
- }
441
- if (isBottom) {
442
- return true;
443
- }
444
- }
445
- return !isBottom;
446
- };
447
- var shouldRemoveSnowflake = (flake, landed, worldWidth, worldHeight) => {
448
- return landed || flake.y > worldHeight + 10 || flake.x < -20 || flake.x > worldWidth + 20;
449
- };
450
- var updateSnowflakes = (snowflakes, elementRects, config, dt, worldWidth, worldHeight) => {
451
- const scrollX = window.scrollX;
452
- const scrollY = window.scrollY;
453
- for (let i = snowflakes.length - 1; i >= 0; i--) {
454
- const flake = snowflakes[i];
455
- updateSnowflakePosition(flake, dt);
456
- let landed = false;
457
- const shouldCheckCollision = Math.random() < config.COLLISION_CHECK_RATE;
458
- if (shouldCheckCollision) {
459
- const flakeViewportX = flake.x - scrollX;
460
- const flakeViewportY = flake.y - scrollY;
461
- for (const item of elementRects) {
462
- const { rect, acc } = item;
463
- const isBottom = acc.type === VAL_BOTTOM;
464
- if (!landed && !isBottom) {
465
- landed = checkSideCollision(flakeViewportX, flakeViewportY, rect, acc, config);
466
- if (landed) break;
467
- }
468
- if (!landed) {
469
- landed = checkSurfaceCollision(flake, flakeViewportX, flakeViewportY, rect, acc, isBottom, config);
470
- if (landed) break;
471
- }
472
- }
473
- }
474
- if (shouldRemoveSnowflake(flake, landed, worldWidth, worldHeight)) {
475
- snowflakes.splice(i, 1);
476
- }
477
- }
478
- };
479
- var meltAndSmoothAccumulation = (elementRects, config, dt) => {
480
- for (const { acc } of elementRects) {
481
- const meltRate = config.MELT_SPEED * dt;
482
- const len = acc.heights.length;
483
- if (len > 2) {
484
- for (let i = 1; i < len - 1; i++) {
485
- if (acc.heights[i] > 0.05) {
486
- const avg = (acc.heights[i - 1] + acc.heights[i + 1]) / 2;
487
- acc.heights[i] = acc.heights[i] * 0.99 + avg * 0.01;
488
- }
489
- }
490
- }
491
- for (let i = 0; i < acc.heights.length; i++) {
492
- if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);
493
- }
494
- let leftMax = 0;
495
- let rightMax = 0;
496
- for (let i = 0; i < acc.leftSide.length; i++) {
497
- if (acc.leftSide[i] > 0) {
498
- acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);
499
- if (acc.leftSide[i] > leftMax) leftMax = acc.leftSide[i];
500
- }
501
- if (acc.rightSide[i] > 0) {
502
- acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);
503
- if (acc.rightSide[i] > rightMax) rightMax = acc.rightSide[i];
504
- }
505
- }
506
- acc.leftMax = leftMax;
507
- acc.rightMax = rightMax;
508
- }
509
- };
510
-
511
- // src/hooks/usePerformanceMetrics.ts
512
- var import_react2 = require("react");
513
- function usePerformanceMetrics() {
514
- const lastFpsSecondRef = (0, import_react2.useRef)(0);
515
- const framesInSecondRef = (0, import_react2.useRef)(0);
516
- const currentFpsRef = (0, import_react2.useRef)(0);
517
- const metricsRef = (0, import_react2.useRef)({
518
- scanTime: 0,
519
- rectUpdateTime: 0,
520
- frameTime: 0,
521
- rafGap: 0,
522
- clearTime: 0,
523
- physicsTime: 0,
524
- drawTime: 0
525
- });
526
- const updateFps = (0, import_react2.useCallback)((now) => {
527
- const currentSecond = Math.floor(now / 1e3);
528
- if (currentSecond !== lastFpsSecondRef.current) {
529
- currentFpsRef.current = framesInSecondRef.current;
530
- framesInSecondRef.current = 1;
531
- lastFpsSecondRef.current = currentSecond;
532
- } else {
533
- framesInSecondRef.current++;
534
- }
535
- }, []);
536
- const getCurrentFps = (0, import_react2.useCallback)(() => {
537
- return currentFpsRef.current || framesInSecondRef.current;
538
- }, []);
539
- const buildMetrics = (0, import_react2.useCallback)((surfaceCount, flakeCount, maxFlakes) => {
540
- return {
541
- fps: currentFpsRef.current || framesInSecondRef.current,
542
- frameTime: metricsRef.current.frameTime,
543
- scanTime: metricsRef.current.scanTime,
544
- rectUpdateTime: metricsRef.current.rectUpdateTime,
545
- surfaceCount,
546
- flakeCount,
547
- maxFlakes,
548
- rafGap: metricsRef.current.rafGap,
549
- clearTime: metricsRef.current.clearTime,
550
- physicsTime: metricsRef.current.physicsTime,
551
- drawTime: metricsRef.current.drawTime
552
- };
553
- }, []);
554
- return {
555
- metricsRef,
556
- updateFps,
557
- getCurrentFps,
558
- buildMetrics
559
- };
560
- }
561
-
562
- // src/hooks/useSnowfallCanvas.ts
563
- var import_react3 = require("react");
564
- function useSnowfallCanvas() {
565
- const canvasRef = (0, import_react3.useRef)(null);
566
- const dprRef = (0, import_react3.useRef)(1);
567
- const resizeCanvas = (0, import_react3.useCallback)((maxRenderDpr = Number.POSITIVE_INFINITY) => {
568
- if (canvasRef.current) {
569
- const newWidth = window.innerWidth;
570
- const newHeight = window.innerHeight;
571
- const dpr = Math.min(window.devicePixelRatio || 1, Math.max(1, maxRenderDpr));
572
- dprRef.current = dpr;
573
- canvasRef.current.width = newWidth * dpr;
574
- canvasRef.current.height = newHeight * dpr;
575
- canvasRef.current.style.width = `${newWidth}px`;
576
- canvasRef.current.style.height = `${newHeight}px`;
577
- }
578
- }, []);
579
- return {
580
- canvasRef,
581
- dprRef,
582
- resizeCanvas
583
- };
584
- }
585
-
586
- // src/hooks/useAnimationLoop.ts
587
- var import_react4 = require("react");
588
-
589
- // src/core/draw.ts
590
- var ACC_FILL_STYLE = "rgba(255, 255, 255, 0.95)";
591
- var ACC_SHADOW_COLOR = "rgba(200, 230, 255, 0.6)";
592
- var OPACITY_BUCKETS2 = [0.3, 0.5, 0.7, 0.9];
593
- var drawSnowflakes = (ctx, flakes) => {
594
- if (flakes.length === 0) return;
595
- ctx.fillStyle = "#FFFFFF";
596
- for (const alpha of OPACITY_BUCKETS2) {
597
- let hasPath = false;
598
- for (const flake of flakes) {
599
- if (flake.isBackground || flake.glowOpacity !== alpha) continue;
600
- if (!hasPath) {
601
- ctx.globalAlpha = alpha;
602
- ctx.beginPath();
603
- hasPath = true;
604
- }
605
- ctx.moveTo(flake.x + flake.glowRadius, flake.y);
606
- ctx.arc(flake.x, flake.y, flake.glowRadius, 0, TAU);
607
- }
608
- if (hasPath) {
609
- ctx.fill();
610
- }
611
- }
612
- for (const alpha of OPACITY_BUCKETS2) {
613
- let hasPath = false;
614
- for (const flake of flakes) {
615
- if (flake.opacity !== alpha) continue;
616
- if (!hasPath) {
617
- ctx.globalAlpha = alpha;
618
- ctx.beginPath();
619
- hasPath = true;
620
- }
621
- ctx.moveTo(flake.x + flake.radius, flake.y);
622
- ctx.arc(flake.x, flake.y, flake.radius, 0, TAU);
623
- }
624
- if (hasPath) {
625
- ctx.fill();
626
- }
627
- }
628
- ctx.globalAlpha = 1;
629
- };
630
- var drawAccumulations = (ctx, elementRects, scrollX, scrollY) => {
631
- ctx.fillStyle = ACC_FILL_STYLE;
632
- ctx.shadowColor = ACC_SHADOW_COLOR;
633
- ctx.shadowBlur = 4;
634
- ctx.shadowOffsetY = -1;
635
- ctx.globalAlpha = 1;
636
- ctx.beginPath();
637
- for (const item of elementRects) {
638
- const { rect, acc } = item;
639
- if (!acc.heights.some((h) => h > 0.1)) continue;
640
- const isBottom = acc.type === VAL_BOTTOM;
641
- const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;
642
- const worldLeft = rect.left + scrollX;
643
- const worldBaseY = baseY + scrollY;
644
- let first = true;
645
- const step = 2;
646
- const len = acc.heights.length;
647
- for (let x = 0; x < len; x += step) {
648
- const height = acc.heights[x] || 0;
649
- const px = worldLeft + x;
650
- const py = worldBaseY - height + (acc.curveOffsets[x] || 0);
651
- if (first) {
652
- ctx.moveTo(px, py);
653
- first = false;
654
- } else {
655
- ctx.lineTo(px, py);
656
- }
657
- }
658
- if ((len - 1) % step !== 0) {
659
- const x = len - 1;
660
- const height = acc.heights[x] || 0;
661
- const px = worldLeft + x;
662
- const py = worldBaseY - height + (acc.curveOffsets[x] || 0);
663
- ctx.lineTo(px, py);
664
- }
665
- for (let x = len - 1; x >= 0; x -= step) {
666
- const px = worldLeft + x;
667
- const py = worldBaseY + (acc.curveOffsets[x] || 0);
668
- ctx.lineTo(px, py);
669
- }
670
- const startX = 0;
671
- const startPx = worldLeft + startX;
672
- const startPy = worldBaseY + (acc.curveOffsets[startX] || 0);
673
- ctx.lineTo(startPx, startPy);
674
- }
675
- ctx.fill();
676
- ctx.shadowBlur = 0;
677
- ctx.shadowOffsetY = 0;
678
- };
679
- var drawSideAccumulations = (ctx, elementRects, scrollX, scrollY) => {
680
- ctx.fillStyle = ACC_FILL_STYLE;
681
- ctx.shadowColor = ACC_SHADOW_COLOR;
682
- ctx.shadowBlur = 3;
683
- ctx.globalAlpha = 1;
684
- ctx.beginPath();
685
- const drawSide = (sideArray, isLeft, multipliers, rect, dx, dy) => {
686
- const baseX = isLeft ? rect.left : rect.right;
687
- const worldBaseX = baseX + dx;
688
- const worldTop = rect.top + dy;
689
- const worldBottom = rect.bottom + dy;
690
- ctx.moveTo(worldBaseX, worldTop);
691
- for (let y = 0; y < sideArray.length; y += 2) {
692
- const width = sideArray[y] || 0;
693
- const nextY = Math.min(y + 2, sideArray.length - 1);
694
- const nextWidth = sideArray[nextY] || 0;
695
- const gravityMultiplier = multipliers[y] || 0;
696
- const py = worldTop + y;
697
- const px = isLeft ? worldBaseX - width * gravityMultiplier : worldBaseX + width * gravityMultiplier;
698
- const ny = worldTop + nextY;
699
- const nGravityMultiplier = multipliers[nextY] || 0;
700
- const nx = isLeft ? worldBaseX - nextWidth * nGravityMultiplier : worldBaseX + nextWidth * nGravityMultiplier;
701
- ctx.lineTo(px, py);
702
- ctx.lineTo(nx, ny);
703
- }
704
- ctx.lineTo(worldBaseX, worldBottom);
705
- };
706
- for (const item of elementRects) {
707
- const { rect, acc } = item;
708
- if (acc.maxSideHeight === 0) continue;
709
- const hasLeftSnow = acc.leftMax > 0.3;
710
- const hasRightSnow = acc.rightMax > 0.3;
711
- if (!hasLeftSnow && !hasRightSnow) continue;
712
- if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, scrollX, scrollY);
713
- if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, scrollX, scrollY);
714
- }
715
- ctx.fill();
716
- ctx.shadowBlur = 0;
717
- };
718
-
719
- // src/hooks/useAnimationLoop.ts
720
- function useAnimationLoop(params) {
721
- const animationIdRef = (0, import_react4.useRef)(0);
722
- const animateRef = (0, import_react4.useRef)(() => {
723
- });
724
- const lastTimeRef = (0, import_react4.useRef)(0);
725
- const lastMetricsUpdateRef = (0, import_react4.useRef)(0);
726
- const elementRectsRef = (0, import_react4.useRef)([]);
727
- const visibleRectsRef = (0, import_react4.useRef)([]);
728
- const dirtyRectsRef = (0, import_react4.useRef)(true);
729
- const animate = (0, import_react4.useCallback)((currentTime) => {
730
- const {
731
- canvasRef,
732
- dprRef,
733
- snowflakesRef,
734
- accumulationRef,
735
- isEnabledRef,
736
- physicsConfigRef,
737
- metricsRef,
738
- updateFps,
739
- getCurrentFps,
740
- buildMetrics,
741
- setMetricsRef
742
- } = params;
743
- const canvas = canvasRef.current;
744
- if (!canvas) {
745
- animationIdRef.current = requestAnimationFrame(animateRef.current);
746
- return;
747
- }
748
- const ctx = canvas.getContext("2d");
749
- if (!ctx) {
750
- animationIdRef.current = requestAnimationFrame(animateRef.current);
751
- return;
752
- }
753
- if (lastTimeRef.current === 0) {
754
- lastTimeRef.current = currentTime;
755
- animationIdRef.current = requestAnimationFrame(animateRef.current);
756
- return;
757
- }
758
- const deltaTime = Math.min(currentTime - lastTimeRef.current, 50);
759
- const now = performance.now();
760
- updateFps(now);
761
- metricsRef.current.rafGap = currentTime - lastTimeRef.current;
762
- lastTimeRef.current = currentTime;
763
- const dt = deltaTime / 16.67;
764
- const frameStartTime = now;
765
- const clearStart = now;
766
- const dpr = dprRef.current;
767
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
768
- ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);
769
- const scrollX = window.scrollX;
770
- const scrollY = window.scrollY;
771
- ctx.translate(-scrollX, -scrollY);
772
- metricsRef.current.clearTime = performance.now() - clearStart;
773
- const snowflakes = snowflakesRef.current;
774
- if (dirtyRectsRef.current) {
775
- const rectStart = performance.now();
776
- elementRectsRef.current = getElementRects(accumulationRef.current);
777
- metricsRef.current.rectUpdateTime = performance.now() - rectStart;
778
- dirtyRectsRef.current = false;
779
- }
780
- const physicsStart = performance.now();
781
- meltAndSmoothAccumulation(elementRectsRef.current, physicsConfigRef.current, dt);
782
- const docEl = document.documentElement;
783
- const worldWidth = docEl.scrollWidth;
784
- const worldHeight = docEl.scrollHeight;
785
- updateSnowflakes(
786
- snowflakes,
787
- elementRectsRef.current,
788
- physicsConfigRef.current,
789
- dt,
790
- worldWidth,
791
- worldHeight
792
- );
793
- metricsRef.current.physicsTime = performance.now() - physicsStart;
794
- const drawStart = performance.now();
795
- drawSnowflakes(ctx, snowflakes);
796
- if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {
797
- const minFlakeFloor = Math.min(80, physicsConfigRef.current.MAX_FLAKES);
798
- const shouldForceSpawn = snowflakes.length < minFlakeFloor;
799
- const currentFps = getCurrentFps();
800
- const isVisible = document.visibilityState === "visible";
801
- const isUnderFpsThreshold = isVisible && currentFps > 0 && currentFps < 40;
802
- const shouldSpawn = shouldForceSpawn || !isUnderFpsThreshold || Math.random() < 0.2;
803
- if (shouldSpawn) {
804
- const isBackground = Math.random() < 0.4;
805
- snowflakes.push(createSnowflake(worldWidth, physicsConfigRef.current, isBackground));
806
- }
807
- }
808
- const viewportWidth = window.innerWidth;
809
- const viewportHeight = window.innerHeight;
810
- const visibleRects = visibleRectsRef.current;
811
- visibleRects.length = 0;
812
- for (const item of elementRectsRef.current) {
813
- const { rect } = item;
814
- if (rect.right >= 0 && rect.left <= viewportWidth && rect.bottom >= 0 && rect.top <= viewportHeight) {
815
- visibleRects.push(item);
816
- }
817
- }
818
- if (visibleRects.length > 0) {
819
- drawAccumulations(ctx, visibleRects, scrollX, scrollY);
820
- drawSideAccumulations(ctx, visibleRects, scrollX, scrollY);
821
- }
822
- metricsRef.current.drawTime = performance.now() - drawStart;
823
- metricsRef.current.frameTime = performance.now() - frameStartTime;
824
- if (currentTime - lastMetricsUpdateRef.current > 500) {
825
- setMetricsRef.current(buildMetrics(
826
- accumulationRef.current.size,
827
- snowflakes.length,
828
- physicsConfigRef.current.MAX_FLAKES
829
- ));
830
- lastMetricsUpdateRef.current = currentTime;
831
- }
832
- animationIdRef.current = requestAnimationFrame(animateRef.current);
833
- }, [params]);
834
- (0, import_react4.useEffect)(() => {
835
- animateRef.current = animate;
836
- }, [animate]);
837
- const start = (0, import_react4.useCallback)(() => {
838
- lastTimeRef.current = 0;
839
- lastMetricsUpdateRef.current = 0;
840
- animationIdRef.current = requestAnimationFrame(animateRef.current);
841
- }, []);
842
- const stop = (0, import_react4.useCallback)(() => {
843
- cancelAnimationFrame(animationIdRef.current);
844
- }, []);
845
- const markRectsDirty = (0, import_react4.useCallback)(() => {
846
- dirtyRectsRef.current = true;
847
- }, []);
848
- return {
849
- start,
850
- stop,
851
- markRectsDirty
852
- };
853
- }
854
-
855
- // src/components/Snowfall.tsx
856
- var import_jsx_runtime2 = require("react/jsx-runtime");
857
- function Snowfall() {
858
- const { isEnabled, physicsConfig, setMetrics } = useSnowfall();
859
- const isEnabledRef = (0, import_react5.useRef)(isEnabled);
860
- const physicsConfigRef = (0, import_react5.useRef)(physicsConfig);
861
- const setMetricsRef = (0, import_react5.useRef)(setMetrics);
862
- const [isMounted, setIsMounted] = (0, import_react5.useState)(false);
863
- const [isVisible, setIsVisible] = (0, import_react5.useState)(false);
864
- const snowflakesRef = (0, import_react5.useRef)([]);
865
- const accumulationRef = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
866
- const { canvasRef, dprRef, resizeCanvas } = useSnowfallCanvas();
867
- const { metricsRef, updateFps, getCurrentFps, buildMetrics } = usePerformanceMetrics();
868
- const { start: startAnimation, stop: stopAnimation, markRectsDirty } = useAnimationLoop({
869
- canvasRef,
870
- dprRef,
871
- snowflakesRef,
872
- accumulationRef,
873
- isEnabledRef,
874
- physicsConfigRef,
875
- metricsRef,
876
- updateFps,
877
- getCurrentFps,
878
- buildMetrics,
879
- setMetricsRef
880
- });
881
- (0, import_react5.useEffect)(() => {
882
- requestAnimationFrame(() => setIsMounted(true));
883
- }, []);
884
- (0, import_react5.useEffect)(() => {
885
- isEnabledRef.current = isEnabled;
886
- }, [isEnabled]);
887
- (0, import_react5.useEffect)(() => {
888
- physicsConfigRef.current = physicsConfig;
889
- if (isMounted) {
890
- resizeCanvas(physicsConfig.MAX_RENDER_DPR);
891
- }
892
- }, [isMounted, physicsConfig, resizeCanvas]);
893
- (0, import_react5.useEffect)(() => {
894
- setMetricsRef.current = setMetrics;
895
- }, [setMetrics]);
896
- (0, import_react5.useEffect)(() => {
897
- if (!isMounted) return;
898
- const canvas = canvasRef.current;
899
- if (!canvas) return;
900
- const ctx = canvas.getContext("2d");
901
- if (!ctx) return;
902
- resizeCanvas(physicsConfigRef.current.MAX_RENDER_DPR);
903
- snowflakesRef.current = [];
904
- let isUnmounted = false;
905
- let scheduledInitFrame = 0;
906
- const surfaceObserver = new ResizeObserver((entries) => {
907
- let needsUpdate = false;
908
- for (const entry of entries) {
909
- if (entry.target.isConnected) {
910
- needsUpdate = true;
911
- break;
912
- }
913
- }
914
- if (needsUpdate) {
915
- scheduleAccumulationInit();
916
- }
917
- });
918
- const initAccumulationWrapper = () => {
919
- if (isUnmounted) return;
920
- const scanStart = performance.now();
921
- initializeAccumulation(accumulationRef.current, physicsConfigRef.current);
922
- surfaceObserver.disconnect();
923
- for (const [el] of accumulationRef.current) {
924
- surfaceObserver.observe(el);
925
- }
926
- metricsRef.current.scanTime = performance.now() - scanStart;
927
- markRectsDirty();
928
- };
929
- const scheduleAccumulationInit = () => {
930
- if (scheduledInitFrame !== 0 || isUnmounted) return;
931
- scheduledInitFrame = requestAnimationFrame(() => {
932
- scheduledInitFrame = 0;
933
- initAccumulationWrapper();
934
- });
935
- };
936
- initAccumulationWrapper();
937
- requestAnimationFrame(() => {
938
- if (!isUnmounted && isMounted) setIsVisible(true);
939
- });
940
- startAnimation();
941
- const handleResize = () => {
942
- resizeCanvas(physicsConfigRef.current.MAX_RENDER_DPR);
943
- accumulationRef.current.clear();
944
- initAccumulationWrapper();
945
- markRectsDirty();
946
- };
947
- window.addEventListener("resize", handleResize);
948
- const mutationObserver = new MutationObserver((mutations) => {
949
- let hasStructuralChange = false;
950
- for (const mutation of mutations) {
951
- if (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0) {
952
- hasStructuralChange = true;
953
- break;
954
- }
955
- }
956
- if (hasStructuralChange) {
957
- scheduleAccumulationInit();
958
- }
959
- });
960
- mutationObserver.observe(document.body, {
961
- childList: true,
962
- subtree: true
963
- });
964
- return () => {
965
- isUnmounted = true;
966
- if (scheduledInitFrame !== 0) {
967
- cancelAnimationFrame(scheduledInitFrame);
968
- }
969
- stopAnimation();
970
- window.removeEventListener("resize", handleResize);
971
- mutationObserver.disconnect();
972
- surfaceObserver.disconnect();
973
- };
974
- }, [isMounted]);
975
- if (!isMounted) return null;
976
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
977
- "canvas",
978
- {
979
- ref: canvasRef,
980
- style: {
981
- position: "fixed",
982
- // FIXED position to eliminate scroll jitter
983
- top: 0,
984
- left: 0,
985
- pointerEvents: "none",
986
- zIndex: 9999,
987
- opacity: isVisible ? 1 : 0,
988
- transition: "opacity 0.3s ease-in",
989
- willChange: "transform"
990
- },
991
- "aria-hidden": "true"
992
- }
993
- ) });
994
- }
995
-
996
- // src/components/DebugPanel.tsx
997
- var import_react6 = require("react");
998
- var import_jsx_runtime3 = require("react/jsx-runtime");
999
- function DebugPanel({ defaultOpen = true }) {
1000
- const { debugMode, toggleDebug, metrics } = useSnowfall();
1001
- const [isMinimized, setIsMinimized] = (0, import_react6.useState)(!defaultOpen);
1002
- const [copied, setCopied] = (0, import_react6.useState)(false);
1003
- (0, import_react6.useEffect)(() => {
1004
- const handleKeyDown = (e) => {
1005
- if (e.shiftKey && e.key === "D") {
1006
- toggleDebug();
1007
- }
1008
- };
1009
- window.addEventListener("keydown", handleKeyDown);
1010
- return () => window.removeEventListener("keydown", handleKeyDown);
1011
- }, [toggleDebug]);
1012
- const copyToClipboard = () => {
1013
- if (metrics) {
1014
- const data = {
1015
- ...metrics,
1016
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1017
- userAgent: navigator.userAgent,
1018
- canvasSize: {
1019
- width: window.innerWidth,
1020
- height: window.innerHeight
1021
- }
1022
- };
1023
- navigator.clipboard.writeText(JSON.stringify(data, null, 2));
1024
- setCopied(true);
1025
- setTimeout(() => setCopied(false), 2e3);
1026
- }
1027
- };
1028
- if (!debugMode) return null;
1029
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1030
- "div",
1031
- {
1032
- "data-snowfall": "top",
1033
- style: {
1034
- position: "fixed",
1035
- bottom: "80px",
1036
- left: "24px",
1037
- backgroundColor: "rgba(15, 23, 42, 0.75)",
1038
- backdropFilter: "blur(16px)",
1039
- WebkitBackdropFilter: "blur(16px)",
1040
- color: "#e2e8f0",
1041
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
1042
- fontSize: "12px",
1043
- padding: isMinimized ? "12px" : "20px",
1044
- borderRadius: "16px",
1045
- zIndex: 1e4,
1046
- minWidth: isMinimized ? "auto" : "300px",
1047
- maxWidth: "100%",
1048
- border: "1px solid rgba(255, 255, 255, 0.1)",
1049
- boxShadow: "0 20px 25px -5px rgba(0, 0, 0, 0.2), 0 8px 10px -6px rgba(0, 0, 0, 0.2)",
1050
- transition: "all 0.2s ease"
1051
- },
1052
- children: [
1053
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
1054
- display: "flex",
1055
- justifyContent: "space-between",
1056
- alignItems: "center",
1057
- marginBottom: isMinimized ? 0 : "16px",
1058
- gap: "16px"
1059
- }, children: [
1060
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontWeight: "600", color: "#fff", display: "flex", alignItems: "center", gap: "8px" }, children: [
1061
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontSize: "14px" }, children: "\u2744\uFE0F" }),
1062
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: {
1063
- background: "linear-gradient(to right, #60a5fa, #22d3ee)",
1064
- WebkitBackgroundClip: "text",
1065
- WebkitTextFillColor: "transparent",
1066
- fontWeight: "700"
1067
- }, children: "DEBUG" })
1068
- ] }),
1069
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [
1070
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1071
- "button",
1072
- {
1073
- onClick: () => setIsMinimized(!isMinimized),
1074
- style: {
1075
- background: "rgba(255, 255, 255, 0.1)",
1076
- border: "1px solid rgba(255, 255, 255, 0.1)",
1077
- color: "#fff",
1078
- cursor: "pointer",
1079
- width: "24px",
1080
- height: "24px",
1081
- display: "flex",
1082
- alignItems: "center",
1083
- justifyContent: "center",
1084
- borderRadius: "6px",
1085
- fontSize: "10px"
1086
- },
1087
- children: isMinimized ? "\u25B2" : "\u25BC"
1088
- }
1089
- ),
1090
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1091
- "button",
1092
- {
1093
- onClick: toggleDebug,
1094
- style: {
1095
- background: "rgba(239, 68, 68, 0.15)",
1096
- border: "1px solid rgba(239, 68, 68, 0.2)",
1097
- color: "#f87171",
1098
- cursor: "pointer",
1099
- width: "24px",
1100
- height: "24px",
1101
- display: "flex",
1102
- alignItems: "center",
1103
- justifyContent: "center",
1104
- borderRadius: "6px",
1105
- fontSize: "12px"
1106
- },
1107
- children: "\u2715"
1108
- }
1109
- )
1110
- ] })
1111
- ] }),
1112
- !isMinimized && metrics && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1113
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginBottom: "12px", paddingBottom: "12px", borderBottom: "1px solid rgba(255,255,255,0.08)" }, children: [
1114
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { color: "#94a3b8", marginBottom: "8px", fontSize: "11px", fontWeight: "600", letterSpacing: "0.5px", textTransform: "uppercase" }, children: "Performance" }),
1115
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: "4px" }, children: [
1116
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "FPS" }),
1117
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontWeight: "bold", color: metrics.fps < 30 ? "#f87171" : metrics.fps < 50 ? "#facc15" : "#4ade80" }, children: metrics.fps.toFixed(1) })
1118
- ] }),
1119
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: "4px" }, children: [
1120
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Frame Time" }),
1121
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { fontFamily: "monospace" }, children: [
1122
- metrics.frameTime.toFixed(2),
1123
- "ms"
1124
- ] })
1125
- ] }),
1126
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
1127
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: metrics.rafGap && metrics.rafGap > 20 ? "#fbbf24" : "inherit" }, children: "rAF Gap" }),
1128
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { fontFamily: "monospace" }, children: [
1129
- metrics.rafGap?.toFixed(1) || 0,
1130
- "ms"
1131
- ] })
1132
- ] })
1133
- ] }),
1134
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginBottom: "12px", paddingBottom: "12px", borderBottom: "1px solid rgba(255,255,255,0.08)" }, children: [
1135
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { color: "#94a3b8", marginBottom: "8px", fontSize: "11px", fontWeight: "600", letterSpacing: "0.5px", textTransform: "uppercase" }, children: "Detailed Timings" }),
1136
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "4px 12px" }, children: [
1137
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
1138
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Clear" }),
1139
- " ",
1140
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { fontFamily: "monospace" }, children: [
1141
- metrics.clearTime?.toFixed(2) || 0,
1142
- "ms"
1143
- ] })
1144
- ] }),
1145
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
1146
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Physics" }),
1147
- " ",
1148
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { fontFamily: "monospace" }, children: [
1149
- metrics.physicsTime?.toFixed(2) || 0,
1150
- "ms"
1151
- ] })
1152
- ] }),
1153
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
1154
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Draw" }),
1155
- " ",
1156
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { fontFamily: "monospace" }, children: [
1157
- metrics.drawTime?.toFixed(2) || 0,
1158
- "ms"
1159
- ] })
1160
- ] }),
1161
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
1162
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Scan" }),
1163
- " ",
1164
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { fontFamily: "monospace" }, children: [
1165
- metrics.scanTime.toFixed(2),
1166
- "ms"
1167
- ] })
1168
- ] }),
1169
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", gridColumn: "span 2" }, children: [
1170
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Rect Update" }),
1171
- " ",
1172
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { fontFamily: "monospace" }, children: [
1173
- metrics.rectUpdateTime.toFixed(2),
1174
- "ms"
1175
- ] })
1176
- ] })
1177
- ] })
1178
- ] }),
1179
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginBottom: "16px" }, children: [
1180
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { color: "#94a3b8", marginBottom: "8px", fontSize: "11px", fontWeight: "600", letterSpacing: "0.5px", textTransform: "uppercase" }, children: "Counts" }),
1181
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: "4px" }, children: [
1182
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Snowflakes" }),
1183
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { fontFamily: "monospace" }, children: [
1184
- metrics.flakeCount,
1185
- " / ",
1186
- metrics.maxFlakes
1187
- ] })
1188
- ] }),
1189
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
1190
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Surfaces" }),
1191
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontFamily: "monospace" }, children: metrics.surfaceCount })
1192
- ] })
1193
- ] }),
1194
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1195
- "button",
1196
- {
1197
- onClick: copyToClipboard,
1198
- style: {
1199
- width: "100%",
1200
- padding: "10px",
1201
- background: copied ? "rgba(34, 197, 94, 0.2)" : "rgba(255, 255, 255, 0.05)",
1202
- border: copied ? "1px solid rgba(34, 197, 94, 0.5)" : "1px solid rgba(255, 255, 255, 0.1)",
1203
- color: copied ? "#4ade80" : "#fff",
1204
- cursor: "pointer",
1205
- borderRadius: "8px",
1206
- fontSize: "11px",
1207
- fontWeight: "600",
1208
- transition: "all 0.2s",
1209
- display: "flex",
1210
- alignItems: "center",
1211
- justifyContent: "center",
1212
- gap: "6px"
1213
- },
1214
- children: copied ? "\u2713 COPIED" : "\u{1F4CB} COPY METRICS"
1215
- }
1216
- ),
1217
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginTop: "12px", fontSize: "10px", color: "#64748b", textAlign: "center" }, children: "Shift+D to toggle" })
1218
- ] })
1219
- ]
1220
- }
1221
- );
1222
- }
1223
- // Annotate the CommonJS export names for ESM import in node:
1224
- 0 && (module.exports = {
1225
- DEFAULT_PHYSICS,
1226
- DebugPanel,
1227
- Snowfall,
1228
- SnowfallProvider,
1229
- useSnowfall
1230
- });
1
+ "use strict";"use client";var ne=Object.defineProperty;var tt=Object.getOwnPropertyDescriptor;var nt=Object.getOwnPropertyNames;var ot=Object.prototype.hasOwnProperty;var rt=(e,t)=>{for(var r in t)ne(e,r,{get:t[r],enumerable:!0})},st=(e,t,r,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of nt(t))!ot.call(e,n)&&n!==r&&ne(e,n,{get:()=>t[n],enumerable:!(s=tt(t,n))||s.enumerable});return e};var it=e=>st(ne({},"__esModule",{value:!0}),e);var yt={};rt(yt,{DEFAULT_PHYSICS:()=>j,Snowfall:()=>le,SnowfallProvider:()=>be,useSnowfall:()=>ee});module.exports=it(yt);var _=require("react");var x=require("react"),ge=require("react/jsx-runtime"),j={MAX_FLAKES:1e3,MELT_SPEED:1e-5,WIND_STRENGTH:1.5,ACCUMULATION:{SIDE_RATE:1,TOP_RATE:5,BOTTOM_RATE:5},MAX_DEPTH:{TOP:100,BOTTOM:50,SIDE:20},FLAKE_SIZE:{MIN:.5,MAX:1.6},MAX_SURFACES:15,COLLISION_CHECK_RATE:.3,MAX_RENDER_DPR:1.25},de=(0,x.createContext)(void 0);function be({children:e,initialDebug:t=!1,initialEnabled:r=!0}){let[s,n]=(0,x.useState)(r),[i,c]=(0,x.useState)(j),[o,u]=(0,x.useState)(t),[l,m]=(0,x.useState)(null),b=(0,x.useCallback)(()=>{n(p=>!p)},[]),h=(0,x.useCallback)(()=>{u(p=>!p)},[]),d=(0,x.useCallback)(p=>{c(g=>({...g,...p,ACCUMULATION:{...g.ACCUMULATION,...p.ACCUMULATION||{}},MAX_DEPTH:{...g.MAX_DEPTH,...p.MAX_DEPTH||{}},FLAKE_SIZE:{...g.FLAKE_SIZE,...p.FLAKE_SIZE||{}}}))},[]),a=(0,x.useCallback)(()=>{c(j)},[]),f=(0,x.useMemo)(()=>({isEnabled:s,toggleSnow:b,physicsConfig:i,updatePhysicsConfig:d,resetPhysics:a,debugMode:o,toggleDebug:h,metrics:l,setMetrics:m}),[s,b,i,d,a,o,h,l,m]);return(0,ge.jsx)(de.Provider,{value:f,children:e})}function ee(){let e=(0,x.useContext)(de);if(e===void 0)throw new Error("useSnowfall must be used within a SnowfallProvider");return e}var W="data-snowfall",oe="ignore";var v="bottom",re="header",pe="footer",se="banner",Se="contentinfo",y=Math.PI*2,T=4;var Me=[re],we=[se],ct=[re,pe,"article","section","aside","nav"],at=[`[role="${se}"]`,`[role="${Se}"]`,'[role="main"]'],lt=[".card",'[class*="card"]','[class*="Card"]','[class*="bg-"]','[class*="shadow-"]','[class*="rounded-"]'],ut=e=>{let t=e.tagName.toLowerCase();if(Me.includes(t))return v;let r=e.getAttribute("role");return r&&we.includes(r)?v:"top"},ft=(e,t)=>{if(e.getAttribute(W)===oe)return!1;if(e.hasAttribute(W))return!0;let r=t||window.getComputedStyle(e);if(!(r.display!=="none"&&r.visibility!=="hidden"&&parseFloat(r.opacity)>.1))return!1;let n=r.backgroundColor,i=n!=="rgba(0, 0, 0, 0)"&&n!=="transparent",c=parseFloat(r.borderWidth)>0&&r.borderColor!=="transparent"&&r.borderColor!=="rgba(0, 0, 0, 0)"&&r.borderStyle!=="none",o=r.boxShadow!=="none",u=r.filter!=="none"&&r.filter.includes("drop-shadow"),l=r.backdropFilter!=="none";return i||c||o||u||l},Te=(e=5)=>{let t=[],r=new Set,s=document.querySelectorAll([`[${W}]`,...ct,...at,...lt].join(", "));for(let n of s){if(t.length>=e)break;if(r.has(n))continue;let i=n.getAttribute(W);if(i===oe)continue;let c=i!==null,o=window.getComputedStyle(n);if(!(o.display!=="none"&&o.visibility!=="hidden"&&parseFloat(o.opacity)>.1)&&!c)continue;let l=n.getBoundingClientRect();if(!(l.width>=100&&l.height>=50)&&!c)continue;let b=l.top<=10&&l.height>=window.innerHeight*.9,h=Me.includes(n.tagName.toLowerCase()),d=we.includes(n.getAttribute("role")||"");if(!(b&&!(h||d||i===v)&&!c)&&ft(n,o)){let f=ut(n);i===v?f=v:i==="top"&&(f="top"),t.push({el:n,type:f}),r.add(n)}}return t},Ae=e=>{let t=[];for(let[r,s]of e.entries()){if(!r.isConnected)continue;let n=r.getBoundingClientRect();t.push({el:r,rect:n,acc:s,hasChanged:!1})}return t};var z=512,ve=z-1,Oe=z/y,Fe=new Float64Array(z),Le=new Float64Array(z);for(let e=0;e<z;e++){let t=e/z*y;Fe[e]=Math.sin(t),Le[e]=Math.cos(t)}var mt=e=>{let t=e<0?e%y+y:e%y;return Fe[(t*Oe|0)&ve]},ht=e=>{let t=e<0?e%y+y:e%y;return Le[(t*Oe|0)&ve]},G=64,Pe=new Float64Array(G);for(let e=0;e<G;e++){let t=e/(G-1);Pe[e]=(Math.cos(t*Math.PI)+1)/2}var Ie=e=>{let t=e*(G-1)+.5|0;return Pe[t<0?0:t>=G?G-1:t]},ae=e=>e/T|0,He=1024,ce=new Map,De=()=>{ce.clear()},dt=e=>e*He|0,bt=(e,t,r)=>{let n=((t?He:0)+dt(e)).toString(36),i=ce.get(n);if(i)return i;let{MIN:c,MAX:o}=r.FLAKE_SIZE,u=t?{sizeMin:c*.6,sizeRange:(o-c)*.4,speedBase:.2,speedScale:.3,noiseSpeedScale:.2,windScale:r.WIND_STRENGTH*.625,wobbleBase:.005,wobbleScale:.015}:{sizeMin:c,sizeRange:o-c,speedBase:.5,speedScale:.5,noiseSpeedScale:.3,windScale:r.WIND_STRENGTH,wobbleBase:.01,wobbleScale:.02},l=u.sizeMin+e*u.sizeRange,m=e*13%1,b=e*7%1,h=e*5%1;return i={radius:l,speed:l*u.speedScale+m*u.noiseSpeedScale+u.speedBase,wind:(b-.5)*u.windScale,wobbleSpeed:h*u.wobbleScale+u.wobbleBase},ce.set(n,i),i},Ne=(e,t,r=!1,s=0)=>{let n=Math.random()*e,i=Math.random(),c=bt(i,r,t),o=i*23%1;return{x:n,y:s-5,wobble:o*y,visual:c}},gt=(e,t,r,s=!1)=>{let n=Math.ceil(e/T),i=new Float32Array(n);for(let u=0;u<n;u++){let l=u*T+T/2,m=1;!s&&r>0&&(l<r?m=Math.pow(l/r,1.2):l>e-r&&(m=Math.pow((e-l)/r,1.2))),i[u]=t*m*(.85+Math.random()*.15)}let c=new Float32Array(n),o=4;for(let u=0;u<o;u++){c[0]=i[0],c[n-1]=i[n-1];for(let l=1;l<n-1;l++)c[l]=(i[l-1]+i[l]+i[l+1])/3;i.set(c)}return i},Ee=(e,t,r)=>{let s=Math.ceil(e/T),n=new Float32Array(s);if(t<=0||r)return n;for(let i=0;i<s;i++){let c=i*T+T/2,o=0;if(c<t){let u=t-c;o=t-Math.sqrt(Math.max(0,t*t-u*u))}else if(c>e-t){let u=c-(e-t);o=t-Math.sqrt(Math.max(0,t*t-u*u))}n[i]=o}return n},Ce=e=>{let t=Math.ceil(e/T),r=new Float32Array(t);for(let s=0;s<t;s++){let i=(s*T+T/2)/e;r[s]=Math.sqrt(i)}return r},Be=(e,t)=>{let r=Te(t.MAX_SURFACES);for(let[s]of e.entries())s.isConnected||e.delete(s);for(let{el:s,type:n}of r){let i=s.getBoundingClientRect(),c=window.getComputedStyle(s),o=e.get(s),u=Math.ceil(i.width),l=n===v,m=Math.ceil(u/T);if(o&&o.heights.length===m){if(o.type=n,o.borderRadius!==void 0){o.borderRadius=parseFloat(c.borderTopLeftRadius)||0,o.curveOffsets=Ee(u,o.borderRadius,l);let p=Math.ceil(Math.ceil(i.height)/T);o.leftSide.length===p&&!o.sideGravityMultipliers.length&&(o.sideGravityMultipliers=Ce(Math.ceil(i.height)))}continue}let b=Math.ceil(i.height),h=l?t.MAX_DEPTH.BOTTOM:t.MAX_DEPTH.TOP,d=parseFloat(c.borderTopLeftRadius)||0,a=Math.ceil(b/T),f=gt(u,h,d,l);e.set(s,{heights:o?.heights.length===m?o.heights:new Float32Array(m),maxHeights:f,leftSide:o?.leftSide.length===a?o.leftSide:new Float32Array(a),rightSide:o?.rightSide.length===a?o.rightSide:new Float32Array(a),maxSideHeight:l?0:t.MAX_DEPTH.SIDE,leftMax:o?.leftSide.length===a?o.leftMax:0,rightMax:o?.rightSide.length===a?o.rightMax:0,maxHeight:o?.maxHeight||0,borderRadius:d,curveOffsets:Ee(u,d,l),sideGravityMultipliers:Ce(b),type:n,_smoothTemp:o?._smoothTemp??new Float32Array(0),dirtyMin:o?.dirtyMin??m,dirtyMax:o?.dirtyMax??0,bucketSize:T,_cacheCanvas:o?._cacheCanvas??null,_cacheCtx:o?._cacheCtx??null,_cacheMaxHeight:o?._cacheMaxHeight??-1})}},pt=(e,t)=>{t<e.dirtyMin&&(e.dirtyMin=t),t>e.dirtyMax&&(e.dirtyMax=t)},xe=(e,t,r,s,n,i,c)=>{let o=Math.max(1,Math.ceil(4/T)),u=ae(r),l=i*(.8+Math.random()*.4),m=c;for(let b=-o;b<=o;b++){let h=u+b;if(h>=0&&h<e.length){let d=h*T+T/2,a=d<n,f=d>t-n;if(n>0&&(a||f))continue;let p=Math.abs(b)/o,g=Ie(p),S=Math.min(s,e[h]+l*g);e[h]=S,S>m&&(m=S)}}return m},St=(e,t)=>{let{visual:r}=e;e.wobble+=r.wobbleSpeed*t,e.x+=(r.wind+mt(e.wobble)*.5)*t,e.y+=(r.speed+ht(e.wobble*.5)*.1)*t},Mt=(e,t,r,s,n)=>{if(!(t>=r.top&&t<=r.bottom)||s.maxSideHeight<=0)return!1;let c=Math.floor(t-r.top),o=s.borderRadius,l=ae(c)*T+T/2,m=l<o,b=l>r.height-o;return o>0&&(m||b)?!1:e>=r.left-5&&e<r.left+3?(s.leftMax=xe(s.leftSide,r.height,c,s.maxSideHeight,o,n,s.leftMax),!0):e>r.right-3&&e<=r.right+5?(s.rightMax=xe(s.rightSide,r.height,c,s.maxSideHeight,o,n,s.rightMax),!0):!1},wt=(e,t,r,s,n,i,c)=>{if(t<s.left||t>s.right)return!1;let o=Math.floor(t-s.left),u=ae(o);if(u<0||u>=n.heights.length)return!1;let l=n.heights[u],m=n.maxHeights[u],b=i?s.bottom-l:s.top-l;if(r<b||r>=b+10||l>=m||i&&Math.random()>=.15)return!1;let h=Math.max(1,Math.ceil(e.visual.radius/T)),d=Math.random(),a=Math.random(),f=h+(d<.5?0:1),p=i?c.ACCUMULATION.BOTTOM_RATE:c.ACCUMULATION.TOP_RATE,g=(a*3|0)-1;for(let S=-f;S<=f;S++){if(Math.random()<.15)continue;let M=u+S+g;if(M>=0&&M<n.heights.length){let E=Math.abs(S),H=n.maxHeights[M],R=E/f,C=.3*Ie(R),F=.8+(d+S*.1)%1*.4,k=C*F*p;if(n.heights[M]<H&&k>0){let D=Math.min(H,n.heights[M]+k);n.heights[M]=D,D>n.maxHeight&&(n.maxHeight=D),pt(n,M)}}}return!0},Tt=(e,t,r,s)=>t||e.y>s+10||e.x<-20||e.x>r+20,At=e=>{let t=1/0,r=-1/0,s=1/0,n=-1/0;for(let i=0;i<e.length;i++){let c=e[i].rect;c.top<t&&(t=c.top),c.bottom>r&&(r=c.bottom),c.left<s&&(s=c.left),c.right>n&&(n=c.right)}return{top:t,bottom:r,left:s,right:n}},_e=60,Re=10,ke=(e,t,r,s,n,i,c,o,u=0)=>{let l=Math.max(1,Math.round(1/r.COLLISION_CHECK_RATE)),m=r.ACCUMULATION.SIDE_RATE,b=At(t),h=b.top-_e,d=b.bottom+_e,a=b.left-Re,f=b.right+Re,p=e.length;for(;p-- >0;){let g=e[p];St(g,s);let S=!1;if(p%l===u%l){let M=g.x-c,E=g.y-o;if(E>=h&&E<=d&&M>=a&&M<=f)for(let R of t){let{rect:O,acc:C}=R,F=C.type===v;if(!S&&!F&&(S=Mt(M,E,O,C,m),S)){R.hasChanged=!0;break}if(!S&&(S=wt(g,M,E,O,C,F,r),S)){R.hasChanged=!0;break}}}if(Tt(g,S,n,i)){let M=e.length-1;p<M&&(e[p]=e[M]),e.length=M}}},Et=4,ye=3,Ct=6,Ue=(e,t,r,s=0)=>{let n=s%ye===0,i=s%Et===0,c=n?t.MELT_SPEED*r*ye:0;for(let{acc:o}of e){let u=o.heights.length;if(o.dirtyMin>o.dirtyMax)continue;let l=Math.max(1,o.dirtyMin),m=Math.min(u-2,o.dirtyMax),b=m-l+1,h=i&&b>=Ct,d=0;if(h&&c>0)for(let a=l;a<=m;a++){let f=o.heights[a];if(f>.05){let p=(o.heights[a-1]+o.heights[a+1])/2,g=f*.99+p*.01;g-=c,g<0&&(g=0),o.heights[a]=g,g>d&&(d=g)}else f>0&&(o.heights[a]=0)}else if(c>0)for(let a=l;a<=m;a++){let f=o.heights[a];if(f>0){let p=f-c,g=p>0?p:0;o.heights[a]=g,g>d&&(d=g)}}else if(h)for(let a=l;a<=m;a++){let f=o.heights[a];if(f>.05){let p=(o.heights[a-1]+o.heights[a+1])/2,g=f*.99+p*.01;o.heights[a]=g,g>d&&(d=g)}}else for(let a=l;a<=m;a++)o.heights[a]>d&&(d=o.heights[a]);for(let a=0;a<Math.min(l,u);a++)o.heights[a]>d&&(d=o.heights[a]);for(let a=Math.max(m+1,0);a<u;a++)o.heights[a]>d&&(d=o.heights[a]);if(o.maxHeight=d,n&&(o.leftMax>0||o.rightMax>0)){let a=0,f=0,p=o.leftSide.length;for(let g=0;g<p;g++){let S=o.leftSide[g];if(S>0){let E=S>c?S-c:0;o.leftSide[g]=E,E>a&&(a=E)}let M=o.rightSide[g];if(M>0){let E=M>c?M-c:0;o.rightSide[g]=E,E>f&&(f=E)}}o.leftMax=a,o.rightMax=f}if(n){let a=o.dirtyMin,f=o.dirtyMax;for(;a<=f&&o.heights[a]<=.05;)a++;for(;f>=a&&o.heights[f]<=.05;)f--;o.dirtyMin=a,o.dirtyMax=f}}};var P=require("react");function Xe(){let e=(0,P.useRef)(0),t=(0,P.useRef)(0),r=(0,P.useRef)(0),s=(0,P.useRef)({scanTime:0,rectUpdateTime:0,frameTime:0,rafGap:0,clearTime:0,physicsTime:0,drawTime:0}),n=(0,P.useCallback)(o=>{let u=Math.floor(o/1e3);u!==e.current?(r.current=t.current,t.current=1,e.current=u):t.current++},[]),i=(0,P.useCallback)(()=>r.current||t.current,[]),c=(0,P.useCallback)((o,u,l)=>({fps:r.current||t.current,frameTime:s.current.frameTime,scanTime:s.current.scanTime,rectUpdateTime:s.current.rectUpdateTime,surfaceCount:o,flakeCount:u,maxFlakes:l,rafGap:s.current.rafGap,clearTime:s.current.clearTime,physicsTime:s.current.physicsTime,drawTime:s.current.drawTime}),[]);return{metricsRef:s,updateFps:n,getCurrentFps:i,buildMetrics:c}}var q=require("react");function Ge(){let e=(0,q.useRef)(null),t=(0,q.useRef)(1),r=(0,q.useCallback)((s=Number.POSITIVE_INFINITY)=>{if(e.current){let n=window.innerWidth,i=window.innerHeight,c=Math.min(window.devicePixelRatio||1,Math.max(1,s));t.current=c,e.current.width=n*c,e.current.height=i*c,e.current.style.width=`${n}px`,e.current.style.height=`${i}px`}},[]);return{canvasRef:e,dprRef:t,resizeCanvas:r}}var w=require("react");var Ve="rgba(255, 255, 255, 0.95)",xt=.5,Y=3,ze=(e,t,r,s,n,i,c,o)=>{let l=(r?n.left:n.right)+i,m=n.top+c,b=n.bottom+c,h=r?-1:1,d=t.length*o;e.moveTo(l,m);for(let f=0;f<d;f+=Y){let p=I(t,f,o),g=I(s,f,o);e.lineTo(l+p*g*h,m+f)}let a=d-1;if(a%Y!==0){let f=I(t,a,o),p=I(s,a,o);e.lineTo(l+f*p*h,m+a)}e.lineTo(l,b)},Ke=(e,t)=>{if(t.length!==0){e.globalAlpha=1,e.fillStyle="#FFFFFF",e.beginPath();for(let r=0,s=t.length;r<s;r++){let n=t[r],i=n.visual.radius;i<2?e.rect(n.x-i,n.y-i,i*2,i*2):(e.moveTo(n.x+i,n.y),e.arc(n.x,n.y,i,0,y))}e.fill()}},I=(e,t,r)=>{let s=t/r,n=s|0;if(n>=e.length-1)return e[e.length-1]||0;let i=s-n;return e[n]*(1-i)+e[n+1]*i},_t=(e,t,r)=>{let s=Math.ceil(r)+10;if(e._cacheCanvas&&e._cacheCtx)(e._cacheCanvas.width<t||e._cacheCanvas.height<s)&&(e._cacheCanvas.width=t,e._cacheCanvas.height=s);else{let n=document.createElement("canvas");n.width=t,n.height=s;let i=n.getContext("2d");if(!i)throw new Error("Failed to create cache canvas context");e._cacheCanvas=n,e._cacheCtx=i}return e._cacheCtx},Rt=(e,t,r)=>{let s=e.maxHeight,n=_t(e,t,s),i=e._cacheCanvas,c=i.width,o=i.height;n.clearRect(0,0,c,o);let u=e.bucketSize,l=Math.ceil(s)+5;n.beginPath();let m=!0;for(let h=0;h<t;h+=Y){let d=I(e.heights,h,u),a=I(e.curveOffsets,h,u),f=l-d+a;m?(n.moveTo(h,f),m=!1):n.lineTo(h,f)}let b=t-1;if(b%Y!==0){let h=I(e.heights,b,u),d=I(e.curveOffsets,b,u);n.lineTo(b,l-h+d)}for(let h=t-1;h>=0;h-=Y){let d=I(e.curveOffsets,h,u);n.lineTo(h,l+d)}n.closePath(),n.fillStyle=Ve,n.shadowColor="rgba(200, 230, 255, 0.6)",n.shadowBlur=4,n.shadowOffsetY=r?1:-1,n.fill(),n.shadowColor="transparent",n.shadowBlur=0,n.shadowOffsetY=0,e._cacheMaxHeight=s},We=(e,t,r,s)=>{e.globalAlpha=1;for(let n of t){let{rect:i,acc:c}=n;if(c.maxHeight<=.1)continue;let o=c.type===v,u=c.heights.length*c.bucketSize;(!c._cacheCanvas||Math.abs(c.maxHeight-c._cacheMaxHeight)>xt)&&Rt(c,u,o);let l=c._cacheCanvas;if(!l)continue;let m=Math.ceil(c._cacheMaxHeight)+5,b=i.left+r;if(o){let h=i.bottom+s-m;e.drawImage(l,b,h)}else{let h=i.top+s-m;e.drawImage(l,b,h)}}},qe=(e,t,r,s)=>{e.globalAlpha=1,e.beginPath();let n=!1;for(let i of t){let{rect:c,acc:o}=i;if(o.maxSideHeight===0)continue;let u=o.leftMax>.3,l=o.rightMax>.3;!u&&!l||(u&&(ze(e,o.leftSide,!0,o.sideGravityMultipliers,c,r,s,o.bucketSize),n=!0),l&&(ze(e,o.rightSide,!1,o.sideGravityMultipliers,c,r,s,o.bucketSize),n=!0))}n&&(e.fillStyle=Ve,e.fill())};function Ye(e){let t=(0,w.useRef)(0),r=(0,w.useRef)(()=>{}),s=(0,w.useRef)(0),n=(0,w.useRef)(0),i=(0,w.useRef)([]),c=(0,w.useRef)([]),o=(0,w.useRef)(!0),u=(0,w.useRef)(0),l=(0,w.useRef)({width:0,height:0}),m=(0,w.useRef)({width:0,height:0}),b=(0,w.useRef)(null),h=(0,w.useRef)(1),d=(0,w.useRef)(e);(0,w.useEffect)(()=>{d.current=e});let a=(0,w.useCallback)(S=>{let{canvasRef:M,dprRef:E,snowflakesRef:H,accumulationRef:R,isEnabledRef:O,physicsConfigRef:C,metricsRef:F,updateFps:k,getCurrentFps:D,buildMetrics:Z,setMetricsRef:$}=d.current,L=M.current;if(!L){t.current=requestAnimationFrame(r.current);return}let A=b.current||L.getContext("2d");if(!A){t.current=requestAnimationFrame(r.current);return}if(b.current||(b.current=A),s.current===0){s.current=S,t.current=requestAnimationFrame(r.current);return}let N=Math.min(S-s.current,50);k(S),F.current.rafGap=S-s.current,s.current=S;let te=N/16.67,B=u.current++,ue=E.current,fe=h.current;A.setTransform(ue,0,0,ue,0,0),A.clearRect(0,0,L.width*fe,L.height*fe);let J=window.scrollX,V=window.scrollY;A.translate(-J,-V);let me=l.current.width,$e=l.current.height,Je=m.current.width,Qe=m.current.height,U=H.current;if(o.current&&(i.current=Ae(R.current),o.current=!1),Ue(i.current,C.current,te,B),ke(U,i.current,C.current,te,me,$e,J,V,B),Ke(A,U),O.current&&U.length<C.current.MAX_FLAKES){let Q=Math.min(80,C.current.MAX_FLAKES),X=U.length<Q,he=D(),je=document.visibilityState==="visible"&&he>0&&he<40;if(X||!je||Math.random()<.2){let et=Math.random()<.4;U.push(Ne(me,C.current,et,V))}}let K=c.current;K.length=0;for(let Q of i.current){let{rect:X}=Q;X.right>=0&&X.left<=Je&&X.bottom>=0&&X.top<=Qe&&K.push(Q)}K.length>0&&(We(A,K,J,V),qe(A,K,J,V)),S-n.current>500&&($.current(Z(R.current.size,U.length,C.current.MAX_FLAKES)),n.current=S),t.current=requestAnimationFrame(r.current)},[]);(0,w.useEffect)(()=>{r.current=a},[a]);let f=(0,w.useCallback)(()=>{s.current=0,n.current=0,t.current=requestAnimationFrame(r.current)},[]),p=(0,w.useCallback)(()=>{cancelAnimationFrame(t.current)},[]),g=(0,w.useCallback)(()=>{o.current=!0,l.current.width=document.documentElement.scrollWidth,l.current.height=document.documentElement.scrollHeight,m.current.width=window.innerWidth,m.current.height=window.innerHeight,h.current=1/(d.current.dprRef.current||1)},[]);return{start:f,stop:p,markRectsDirty:g}}var Ze=require("react/jsx-runtime");function le(){let{isEnabled:e,physicsConfig:t,setMetrics:r}=ee(),s=(0,_.useRef)(e),n=(0,_.useRef)(t),i=(0,_.useRef)(r),[c,o]=(0,_.useState)(!1),[u,l]=(0,_.useState)(!1),m=(0,_.useRef)([]),b=(0,_.useRef)(new Map),{canvasRef:h,dprRef:d,resizeCanvas:a}=Ge(),{metricsRef:f,updateFps:p,getCurrentFps:g,buildMetrics:S}=Xe(),{start:M,stop:E,markRectsDirty:H}=Ye({canvasRef:h,dprRef:d,snowflakesRef:m,accumulationRef:b,isEnabledRef:s,physicsConfigRef:n,metricsRef:f,updateFps:p,getCurrentFps:g,buildMetrics:S,setMetricsRef:i});return(0,_.useEffect)(()=>{requestAnimationFrame(()=>o(!0))},[]),(0,_.useEffect)(()=>{s.current=e},[e]),(0,_.useEffect)(()=>{n.current=t,De(),m.current=[],c&&a(t.MAX_RENDER_DPR)},[c,t,a]),(0,_.useEffect)(()=>{i.current=r},[r]),(0,_.useEffect)(()=>{if(!c)return;let R=h.current;if(!R)return;a(n.current.MAX_RENDER_DPR),m.current=[];let O=!1,C=0,F=new ResizeObserver(L=>{let A=!1;for(let N of L)if(N.target.isConnected){A=!0;break}A&&D()}),k=()=>{if(O)return;let L=performance.now();Be(b.current,n.current),F.disconnect();for(let[A]of b.current)F.observe(A);f.current.scanTime=performance.now()-L,H()},D=()=>{C!==0||O||(C=requestAnimationFrame(()=>{C=0,k()}))};k(),requestAnimationFrame(()=>{!O&&c&&l(!0)}),M();let Z=()=>{a(n.current.MAX_RENDER_DPR),H()};window.addEventListener("resize",Z);let $=new MutationObserver(L=>{let A=!1;for(let N of L)if(!(N.type!=="childList"||N.target===R)){for(let B of N.addedNodes)if(B.nodeType===Node.ELEMENT_NODE&&B!==R){A=!0;break}if(A)break;for(let B of N.removedNodes)if(B.nodeType===Node.ELEMENT_NODE&&B!==R){A=!0;break}if(A)break}A&&D()});return $.observe(document.body,{childList:!0,subtree:!0}),()=>{O=!0,C!==0&&cancelAnimationFrame(C),E(),window.removeEventListener("resize",Z),$.disconnect(),F.disconnect()}},[c]),c?(0,Ze.jsx)("canvas",{ref:h,style:{position:"fixed",top:0,left:0,pointerEvents:"none",zIndex:9999,opacity:u?1:0,transition:u?void 0:"opacity 0.3s ease-in",willChange:u?void 0:"opacity"},"aria-hidden":"true"}):null}0&&(module.exports={DEFAULT_PHYSICS,Snowfall,SnowfallProvider,useSnowfall});
1231
2
  //# sourceMappingURL=index.js.map