@xdy-npm/react-particle-backgrounds 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1103 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var Particles = require('@tsparticles/react');
5
+ var slim = require('@tsparticles/slim');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var Particles__default = /*#__PURE__*/_interopDefault(Particles);
11
+
12
+ // src/components/ParticlesBackground.tsx
13
+
14
+ // src/themes/base.ts
15
+ var baseConfig = {
16
+ fullScreen: {
17
+ enable: true,
18
+ zIndex: 0
19
+ },
20
+ background: {
21
+ color: { value: "transparent" }
22
+ },
23
+ fpsLimit: 120,
24
+ detectRetina: true
25
+ };
26
+ var DEFAULT_COLORS = [
27
+ "#ffb3d9",
28
+ "#f43f5e",
29
+ "#a78bfa",
30
+ "#3b82f6",
31
+ "#10b981",
32
+ "#f59e0b",
33
+ "#ff6b6b",
34
+ "#8b5cf6",
35
+ "#06b6d4",
36
+ "#fb7185"
37
+ ];
38
+
39
+ // src/themes/starline.ts
40
+ var starlineTheme = {
41
+ id: "starline",
42
+ name: "Star Links",
43
+ icon: "\u2728",
44
+ description: "Classic particle linking effect",
45
+ backgroundGradient: "linear-gradient(180deg, #0f172a 0%, #1e293b 50%, #334155 100%)",
46
+ options: (isDark) => {
47
+ const colors = DEFAULT_COLORS;
48
+ return {
49
+ ...baseConfig,
50
+ interactivity: {
51
+ detectsOn: "window",
52
+ events: {
53
+ onClick: { enable: true, mode: "push" },
54
+ onHover: { enable: true, mode: "grab" },
55
+ resize: { enable: true }
56
+ },
57
+ modes: {
58
+ push: { quantity: 6 },
59
+ grab: {
60
+ distance: 200,
61
+ links: { opacity: 1, color: colors }
62
+ }
63
+ }
64
+ },
65
+ particles: {
66
+ color: { value: colors },
67
+ links: {
68
+ color: colors,
69
+ distance: 150,
70
+ enable: true,
71
+ opacity: 0.5,
72
+ width: 1
73
+ },
74
+ move: {
75
+ direction: "none",
76
+ enable: true,
77
+ outModes: { default: "out" },
78
+ random: false,
79
+ speed: 2,
80
+ straight: false
81
+ },
82
+ number: {
83
+ density: { enable: true, width: 1920, height: 1080 },
84
+ value: 100
85
+ },
86
+ opacity: { value: 0.7 },
87
+ shape: { type: "circle" },
88
+ size: { value: { min: 2, max: 6 } },
89
+ shadow: {
90
+ blur: 8,
91
+ color: { value: colors },
92
+ enable: true,
93
+ offset: { x: 0, y: 0 }
94
+ }
95
+ }
96
+ };
97
+ }
98
+ };
99
+
100
+ // src/themes/snow.ts
101
+ var snowTheme = {
102
+ id: "snow",
103
+ name: "Snowfall",
104
+ icon: "\u2744\uFE0F",
105
+ description: "Romantic falling snowflakes",
106
+ backgroundGradient: "linear-gradient(180deg, #1e3a5f 0%, #2d4a6b 50%, #3d5a7b 100%)",
107
+ options: (isDark) => ({
108
+ ...baseConfig,
109
+ interactivity: {
110
+ detectsOn: "window",
111
+ events: {
112
+ onHover: { enable: true, mode: "repulse" },
113
+ resize: { enable: true }
114
+ },
115
+ modes: {
116
+ repulse: { distance: 100, duration: 0.4 }
117
+ }
118
+ },
119
+ particles: {
120
+ color: { value: isDark ? "#ffffff" : "#87CEEB" },
121
+ move: {
122
+ direction: "bottom",
123
+ enable: true,
124
+ outModes: { default: "out" },
125
+ speed: { min: 1, max: 3 },
126
+ straight: false,
127
+ drift: { min: -0.5, max: 0.5 }
128
+ },
129
+ number: {
130
+ density: { enable: true, width: 1920, height: 1080 },
131
+ value: 80
132
+ },
133
+ opacity: {
134
+ value: { min: 0.3, max: 0.8 },
135
+ animation: { enable: true, speed: 1, minimumValue: 0.3, sync: false }
136
+ },
137
+ shape: { type: "circle" },
138
+ size: { value: { min: 2, max: 6 } },
139
+ wobble: {
140
+ enable: true,
141
+ distance: 10,
142
+ speed: { min: -5, max: 5 }
143
+ },
144
+ shadow: {
145
+ blur: 5,
146
+ color: { value: isDark ? "#ffffff" : "#87CEEB" },
147
+ enable: true,
148
+ offset: { x: 0, y: 0 }
149
+ }
150
+ }
151
+ })
152
+ };
153
+
154
+ // src/themes/bubble.ts
155
+ var bubbleTheme = {
156
+ id: "bubble",
157
+ name: "Bubbles",
158
+ icon: "\u{1FAE7}",
159
+ description: "Dreamy rising bubbles",
160
+ backgroundGradient: "linear-gradient(180deg, #0a2647 0%, #144272 50%, #205295 100%)",
161
+ options: (isDark) => ({
162
+ ...baseConfig,
163
+ interactivity: {
164
+ detectsOn: "window",
165
+ events: {
166
+ onClick: { enable: true, mode: "pop" },
167
+ onHover: { enable: true, mode: "bubble" },
168
+ resize: { enable: true }
169
+ },
170
+ modes: {
171
+ bubble: { distance: 200, size: 15, duration: 2, opacity: 0.8 },
172
+ pop: {}
173
+ }
174
+ },
175
+ particles: {
176
+ color: {
177
+ value: isDark ? ["#00d9ff", "#00ff9d", "#ff00e6", "#ffee00"] : ["#ffb3d9", "#ff91c7", "#ffc0e5", "#ffd6e8"]
178
+ },
179
+ move: {
180
+ direction: "top",
181
+ enable: true,
182
+ outModes: { default: "out" },
183
+ speed: { min: 1, max: 2 },
184
+ straight: false
185
+ },
186
+ number: {
187
+ density: { enable: true, width: 1920, height: 1080 },
188
+ value: 50
189
+ },
190
+ opacity: {
191
+ value: { min: 0.2, max: 0.6 },
192
+ animation: { enable: true, speed: 0.5, minimumValue: 0.1, sync: false }
193
+ },
194
+ shape: { type: "circle" },
195
+ size: {
196
+ value: { min: 5, max: 15 },
197
+ animation: { enable: true, speed: 3, minimumValue: 3, sync: false }
198
+ },
199
+ stroke: {
200
+ width: 1,
201
+ color: { value: isDark ? "rgba(255,255,255,0.3)" : "rgba(0,0,0,0.1)" }
202
+ },
203
+ shadow: {
204
+ blur: 10,
205
+ color: { value: isDark ? "#ffb3d9" : "#ff91c7" },
206
+ enable: true,
207
+ offset: { x: 0, y: 0 }
208
+ }
209
+ }
210
+ })
211
+ };
212
+
213
+ // src/themes/stars.ts
214
+ var starsTheme = {
215
+ id: "stars",
216
+ name: "Twinkling Stars",
217
+ icon: "\u2B50",
218
+ description: "Sparkling starry sky",
219
+ backgroundGradient: "linear-gradient(180deg, #000000 0%, #1a1a2e 50%, #16213e 100%)",
220
+ options: (isDark) => ({
221
+ ...baseConfig,
222
+ interactivity: {
223
+ detectsOn: "window",
224
+ events: {
225
+ onClick: { enable: true, mode: "push" },
226
+ onHover: { enable: true, mode: "connect" },
227
+ resize: { enable: true }
228
+ },
229
+ modes: {
230
+ push: { quantity: 3 },
231
+ connect: { distance: 100, links: { opacity: 0.3 }, radius: 150 }
232
+ }
233
+ },
234
+ particles: {
235
+ color: {
236
+ value: isDark ? ["#ffffff", "#ffffd4", "#ffecd2", "#d4f1ff"] : ["#ffd700", "#ffb347", "#ff6b6b", "#4ecdc4"]
237
+ },
238
+ move: {
239
+ direction: "none",
240
+ enable: true,
241
+ outModes: { default: "out" },
242
+ random: true,
243
+ speed: 0.5,
244
+ straight: false
245
+ },
246
+ number: {
247
+ density: { enable: true, width: 1920, height: 1080 },
248
+ value: 120
249
+ },
250
+ opacity: {
251
+ value: { min: 0.2, max: 1 },
252
+ animation: {
253
+ enable: true,
254
+ speed: 1,
255
+ minimumValue: 0.1,
256
+ sync: false
257
+ }
258
+ },
259
+ shape: { type: "star", options: { star: { sides: 5 } } },
260
+ size: { value: { min: 1, max: 4 } },
261
+ twinkle: {
262
+ lines: { enable: false },
263
+ particles: {
264
+ enable: true,
265
+ frequency: 0.05,
266
+ opacity: 1,
267
+ color: { value: isDark ? "#ffffff" : "#ffd700" }
268
+ }
269
+ },
270
+ shadow: {
271
+ blur: 6,
272
+ color: { value: isDark ? "#ffffff" : "#ffd700" },
273
+ enable: true,
274
+ offset: { x: 0, y: 0 }
275
+ }
276
+ }
277
+ })
278
+ };
279
+
280
+ // src/themes/firefly.ts
281
+ var fireflyTheme = {
282
+ id: "firefly",
283
+ name: "Fireflies",
284
+ icon: "\u{1FAB2}",
285
+ description: "Warm glowing firefly effect",
286
+ backgroundGradient: "linear-gradient(180deg, #1a1a1a 0%, #2d2d2d 50%, #1a1a1a 100%)",
287
+ options: (isDark) => ({
288
+ ...baseConfig,
289
+ interactivity: {
290
+ detectsOn: "window",
291
+ events: {
292
+ onHover: { enable: true, mode: "slow" },
293
+ resize: { enable: true }
294
+ },
295
+ modes: {
296
+ slow: { factor: 3, radius: 200 }
297
+ }
298
+ },
299
+ particles: {
300
+ color: {
301
+ value: isDark ? ["#ffff00", "#adff2f", "#7fff00", "#00ff7f"] : ["#ffc107", "#ff9800", "#ff5722", "#4caf50"]
302
+ },
303
+ move: {
304
+ direction: "none",
305
+ enable: true,
306
+ outModes: { default: "bounce" },
307
+ random: true,
308
+ speed: 1,
309
+ straight: false,
310
+ trail: {
311
+ enable: true,
312
+ length: 5,
313
+ fill: { color: "transparent" }
314
+ }
315
+ },
316
+ number: {
317
+ density: { enable: true, width: 1920, height: 1080 },
318
+ value: 40
319
+ },
320
+ opacity: {
321
+ value: { min: 0.3, max: 1 },
322
+ animation: {
323
+ enable: true,
324
+ speed: 2,
325
+ minimumValue: 0.1,
326
+ sync: false
327
+ }
328
+ },
329
+ shape: { type: "circle" },
330
+ size: { value: { min: 2, max: 5 } },
331
+ shadow: {
332
+ blur: 15,
333
+ color: { value: isDark ? "#adff2f" : "#ffc107" },
334
+ enable: true,
335
+ offset: { x: 0, y: 0 }
336
+ }
337
+ }
338
+ })
339
+ };
340
+
341
+ // src/themes/geometry.ts
342
+ var geometryTheme = {
343
+ id: "geometry",
344
+ name: "Geometry",
345
+ icon: "\u{1F537}",
346
+ description: "Floating abstract geometric shapes",
347
+ backgroundGradient: "linear-gradient(180deg, #0f0c29 0%, #302b63 50%, #24243e 100%)",
348
+ options: (isDark) => ({
349
+ ...baseConfig,
350
+ interactivity: {
351
+ detectsOn: "window",
352
+ events: {
353
+ onClick: { enable: true, mode: "push" },
354
+ onHover: { enable: true, mode: "repulse" },
355
+ resize: { enable: true }
356
+ },
357
+ modes: {
358
+ push: { quantity: 2 },
359
+ repulse: { distance: 150, duration: 0.4 }
360
+ }
361
+ },
362
+ particles: {
363
+ color: {
364
+ value: ["#ffb3d9", "#ff91c7", "#ffc0e5", "#ffd6e8"]
365
+ },
366
+ move: {
367
+ direction: "none",
368
+ enable: true,
369
+ outModes: { default: "bounce" },
370
+ random: false,
371
+ speed: 1.5,
372
+ straight: false
373
+ },
374
+ number: {
375
+ density: { enable: true, width: 1920, height: 1080 },
376
+ value: 30
377
+ },
378
+ opacity: {
379
+ value: { min: 0.3, max: 0.7 }
380
+ },
381
+ rotate: {
382
+ value: { min: 0, max: 360 },
383
+ direction: "random",
384
+ animation: { enable: true, speed: 5 }
385
+ },
386
+ shape: {
387
+ type: ["triangle", "square", "polygon"],
388
+ options: {
389
+ polygon: { sides: 6 }
390
+ }
391
+ },
392
+ size: { value: { min: 10, max: 25 } },
393
+ stroke: {
394
+ width: 1,
395
+ color: { value: isDark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.2)" }
396
+ },
397
+ shadow: {
398
+ blur: 10,
399
+ color: { value: isDark ? "#ffb3d9" : "#ff91c7" },
400
+ enable: true,
401
+ offset: { x: 2, y: 2 }
402
+ }
403
+ }
404
+ })
405
+ };
406
+
407
+ // src/themes/wave.ts
408
+ var waveTheme = {
409
+ id: "wave",
410
+ name: "Particle Ocean",
411
+ icon: "\u{1F30A}",
412
+ description: "3D particle wave effect (requires three.js)",
413
+ isThreeJS: true,
414
+ backgroundGradient: "linear-gradient(180deg, #000000 0%, #0a1628 50%, #0d1f3c 100%)",
415
+ options: () => ({
416
+ ...baseConfig,
417
+ particles: {
418
+ number: { value: 0 }
419
+ }
420
+ })
421
+ };
422
+
423
+ // src/themes/index.ts
424
+ var particleThemes = [
425
+ starlineTheme,
426
+ snowTheme,
427
+ bubbleTheme,
428
+ starsTheme,
429
+ fireflyTheme,
430
+ geometryTheme,
431
+ waveTheme
432
+ ];
433
+ var getThemeById = (id) => {
434
+ return particleThemes.find((theme) => theme.id === id) || starlineTheme;
435
+ };
436
+ var DEFAULT_THEME_ID = "starline";
437
+ var ParticleContext = react.createContext(null);
438
+ var STORAGE_KEY = "rpb-theme-id";
439
+ var ParticleProvider = ({
440
+ defaultTheme = DEFAULT_THEME_ID,
441
+ isDark: isDarkProp = true,
442
+ persist = true,
443
+ children
444
+ }) => {
445
+ const [themeId, setThemeId] = react.useState(() => {
446
+ if (persist && typeof window !== "undefined") {
447
+ return localStorage.getItem(STORAGE_KEY) || defaultTheme;
448
+ }
449
+ return defaultTheme;
450
+ });
451
+ const [isDark, setDark] = react.useState(isDarkProp);
452
+ react.useEffect(() => {
453
+ setDark(isDarkProp);
454
+ }, [isDarkProp]);
455
+ const setTheme = react.useCallback((id) => {
456
+ setThemeId(id);
457
+ if (persist && typeof window !== "undefined") {
458
+ localStorage.setItem(STORAGE_KEY, id);
459
+ }
460
+ }, [persist]);
461
+ return /* @__PURE__ */ jsxRuntime.jsx(ParticleContext.Provider, { value: { themeId, isDark, setTheme, setDark }, children });
462
+ };
463
+ var useParticleTheme = () => {
464
+ const ctx = react.useContext(ParticleContext);
465
+ if (!ctx) {
466
+ throw new Error("useParticleTheme must be used within a <ParticleProvider>");
467
+ }
468
+ return ctx;
469
+ };
470
+ var useParticleThemeOptional = () => {
471
+ return react.useContext(ParticleContext);
472
+ };
473
+ var ParticleWave = ({
474
+ background = "linear-gradient(180deg, #000000 0%, #0a1628 50%, #0d1f3c 100%)",
475
+ className,
476
+ style
477
+ }) => {
478
+ const containerRef = react.useRef(null);
479
+ const animationRef = react.useRef();
480
+ const hasThree = react.useRef(true);
481
+ react.useEffect(() => {
482
+ let THREE;
483
+ try {
484
+ THREE = globalThis.require ? globalThis.require("three") : (() => {
485
+ throw new Error("no require");
486
+ })();
487
+ } catch (e) {
488
+ hasThree.current = false;
489
+ console.warn('[react-particle-backgrounds] "three" is not installed. The wave theme requires it as a peer dependency.');
490
+ return;
491
+ }
492
+ if (!containerRef.current) return;
493
+ const container = containerRef.current;
494
+ const width = container.clientWidth;
495
+ const height = container.clientHeight;
496
+ const scene = new THREE.Scene();
497
+ scene.background = null;
498
+ const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1e3);
499
+ camera.position.set(0, 50, 100);
500
+ camera.lookAt(0, 0, 0);
501
+ const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
502
+ renderer.setSize(width, height);
503
+ renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
504
+ container.appendChild(renderer.domElement);
505
+ const particleCount = 15e3;
506
+ const waveWidth = 200;
507
+ const waveDepth = 100;
508
+ const geometry = new THREE.BufferGeometry();
509
+ const positions = new Float32Array(particleCount * 3);
510
+ const colors = new Float32Array(particleCount * 3);
511
+ const sizes = new Float32Array(particleCount);
512
+ for (let i = 0; i < particleCount; i++) {
513
+ const i3 = i * 3;
514
+ positions[i3] = (Math.random() - 0.5) * waveWidth;
515
+ positions[i3 + 1] = 0;
516
+ positions[i3 + 2] = (Math.random() - 0.5) * waveDepth;
517
+ const colorChoice = Math.random();
518
+ if (colorChoice < 0.5) {
519
+ colors[i3] = 0;
520
+ colors[i3 + 1] = 0.8 + Math.random() * 0.2;
521
+ colors[i3 + 2] = 1;
522
+ } else if (colorChoice < 0.8) {
523
+ colors[i3] = 0.1;
524
+ colors[i3 + 1] = 0.3 + Math.random() * 0.3;
525
+ colors[i3 + 2] = 0.9 + Math.random() * 0.1;
526
+ } else {
527
+ colors[i3] = 0.8 + Math.random() * 0.2;
528
+ colors[i3 + 1] = 0.9 + Math.random() * 0.1;
529
+ colors[i3 + 2] = 1;
530
+ }
531
+ sizes[i] = Math.random() * 2 + 0.5;
532
+ }
533
+ geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
534
+ geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
535
+ geometry.setAttribute("size", new THREE.BufferAttribute(sizes, 1));
536
+ const vertexShader = `
537
+ attribute float size;
538
+ varying vec3 vColor;
539
+ void main() {
540
+ vColor = color;
541
+ vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
542
+ gl_PointSize = size * (300.0 / -mvPosition.z);
543
+ gl_Position = projectionMatrix * mvPosition;
544
+ }
545
+ `;
546
+ const fragmentShader = `
547
+ varying vec3 vColor;
548
+ void main() {
549
+ float dist = length(gl_PointCoord - vec2(0.5));
550
+ if (dist > 0.5) discard;
551
+ float alpha = 1.0 - smoothstep(0.0, 0.5, dist);
552
+ float glow = exp(-dist * 3.0);
553
+ vec3 finalColor = vColor + glow * 0.5;
554
+ gl_FragColor = vec4(finalColor, alpha * 0.8);
555
+ }
556
+ `;
557
+ const material = new THREE.ShaderMaterial({
558
+ vertexShader,
559
+ fragmentShader,
560
+ transparent: true,
561
+ vertexColors: true,
562
+ blending: THREE.AdditiveBlending,
563
+ depthWrite: false
564
+ });
565
+ const particles = new THREE.Points(geometry, material);
566
+ scene.add(particles);
567
+ const bokehCount = 100;
568
+ const bokehGeometry = new THREE.BufferGeometry();
569
+ const bokehPositions = new Float32Array(bokehCount * 3);
570
+ const bokehColors = new Float32Array(bokehCount * 3);
571
+ const bokehSizes = new Float32Array(bokehCount);
572
+ for (let i = 0; i < bokehCount; i++) {
573
+ const i3 = i * 3;
574
+ bokehPositions[i3] = (Math.random() - 0.5) * waveWidth * 1.5;
575
+ bokehPositions[i3 + 1] = Math.random() * -30 - 10;
576
+ bokehPositions[i3 + 2] = (Math.random() - 0.5) * waveDepth * 2;
577
+ bokehColors[i3] = 0.2;
578
+ bokehColors[i3 + 1] = 0.5 + Math.random() * 0.3;
579
+ bokehColors[i3 + 2] = 1;
580
+ bokehSizes[i] = Math.random() * 15 + 8;
581
+ }
582
+ bokehGeometry.setAttribute("position", new THREE.BufferAttribute(bokehPositions, 3));
583
+ bokehGeometry.setAttribute("color", new THREE.BufferAttribute(bokehColors, 3));
584
+ bokehGeometry.setAttribute("size", new THREE.BufferAttribute(bokehSizes, 1));
585
+ const bokehMaterial = new THREE.ShaderMaterial({
586
+ vertexShader,
587
+ fragmentShader: `
588
+ varying vec3 vColor;
589
+ void main() {
590
+ float dist = length(gl_PointCoord - vec2(0.5));
591
+ if (dist > 0.5) discard;
592
+ float alpha = 1.0 - smoothstep(0.2, 0.5, dist);
593
+ gl_FragColor = vec4(vColor, alpha * 0.3);
594
+ }
595
+ `,
596
+ transparent: true,
597
+ vertexColors: true,
598
+ blending: THREE.AdditiveBlending,
599
+ depthWrite: false
600
+ });
601
+ const bokehParticles = new THREE.Points(bokehGeometry, bokehMaterial);
602
+ scene.add(bokehParticles);
603
+ const dropCount = 150;
604
+ const trailLength = 12;
605
+ const rainDrops = [];
606
+ for (let i = 0; i < dropCount; i++) {
607
+ const shouldActivate = Math.random() < 0.1;
608
+ const x = (Math.random() - 0.5) * waveWidth * 0.9;
609
+ const z = (Math.random() - 0.5) * waveDepth * 0.7;
610
+ const waveHeight = Math.sin(x * 0.05) * 8 + Math.sin(z * 0.08) * 5;
611
+ const startHeight = shouldActivate ? waveHeight + Math.random() * 40 : -200;
612
+ rainDrops.push({
613
+ x,
614
+ z,
615
+ y: startHeight,
616
+ velocity: shouldActivate ? 0.5 + Math.random() * 0.7 : 0,
617
+ active: shouldActivate,
618
+ maxHeight: waveHeight + 80 + Math.random() * 120,
619
+ trail: new Array(trailLength).fill(startHeight),
620
+ opacity: shouldActivate ? 1 : 0
621
+ });
622
+ }
623
+ const trailPointCount = dropCount * trailLength;
624
+ const dropTrailGeometry = new THREE.BufferGeometry();
625
+ const dropTrailPositions = new Float32Array(trailPointCount * 3);
626
+ const dropTrailColors = new Float32Array(trailPointCount * 3);
627
+ const dropTrailSizes = new Float32Array(trailPointCount);
628
+ for (let i = 0; i < dropCount; i++) {
629
+ for (let j = 0; j < trailLength; j++) {
630
+ dropTrailSizes[i * trailLength + j] = 4 * (1 - j / trailLength) * 0.5;
631
+ }
632
+ }
633
+ dropTrailGeometry.setAttribute("position", new THREE.BufferAttribute(dropTrailPositions, 3));
634
+ dropTrailGeometry.setAttribute("color", new THREE.BufferAttribute(dropTrailColors, 3));
635
+ dropTrailGeometry.setAttribute("size", new THREE.BufferAttribute(dropTrailSizes, 1));
636
+ const dropTrailMaterial = new THREE.ShaderMaterial({
637
+ vertexShader,
638
+ fragmentShader: `
639
+ varying vec3 vColor;
640
+ void main() {
641
+ float dist = length(gl_PointCoord - vec2(0.5));
642
+ if (dist > 0.5) discard;
643
+ float alpha = 1.0 - smoothstep(0.0, 0.5, dist);
644
+ gl_FragColor = vec4(vColor, alpha * 0.8);
645
+ }
646
+ `,
647
+ transparent: true,
648
+ vertexColors: true,
649
+ blending: THREE.AdditiveBlending,
650
+ depthWrite: false
651
+ });
652
+ const dropTrailParticles = new THREE.Points(dropTrailGeometry, dropTrailMaterial);
653
+ scene.add(dropTrailParticles);
654
+ const dropHeadGeometry = new THREE.BufferGeometry();
655
+ const dropHeadPositions = new Float32Array(dropCount * 3);
656
+ const dropHeadColors = new Float32Array(dropCount * 3);
657
+ const dropHeadSizes = new Float32Array(dropCount);
658
+ for (let i = 0; i < dropCount; i++) {
659
+ dropHeadSizes[i] = (6 + Math.random() * 4) * 0.5;
660
+ }
661
+ dropHeadGeometry.setAttribute("position", new THREE.BufferAttribute(dropHeadPositions, 3));
662
+ dropHeadGeometry.setAttribute("color", new THREE.BufferAttribute(dropHeadColors, 3));
663
+ dropHeadGeometry.setAttribute("size", new THREE.BufferAttribute(dropHeadSizes, 1));
664
+ const dropHeadMaterial = new THREE.ShaderMaterial({
665
+ vertexShader,
666
+ fragmentShader: `
667
+ varying vec3 vColor;
668
+ void main() {
669
+ float dist = length(gl_PointCoord - vec2(0.5));
670
+ if (dist > 0.5) discard;
671
+ float alpha = 1.0 - smoothstep(0.0, 0.5, dist);
672
+ float glow = exp(-dist * 2.0);
673
+ vec3 finalColor = vColor + glow * 0.8;
674
+ gl_FragColor = vec4(finalColor, alpha);
675
+ }
676
+ `,
677
+ transparent: true,
678
+ vertexColors: true,
679
+ blending: THREE.AdditiveBlending,
680
+ depthWrite: false
681
+ });
682
+ const dropHeadParticles = new THREE.Points(dropHeadGeometry, dropHeadMaterial);
683
+ scene.add(dropHeadParticles);
684
+ let time = 0;
685
+ let lastSpawnTime = 0;
686
+ const getWaveHeight = (x, z, t) => Math.sin(x * 0.05 + t) * 8 + Math.sin(z * 0.08 + t * 0.8) * 5 + Math.sin((x + z) * 0.03 + t * 1.2) * 3;
687
+ const activateDrop = (drop) => {
688
+ drop.x = (Math.random() - 0.5) * waveWidth * 0.9;
689
+ drop.z = (Math.random() - 0.5) * waveDepth * 0.7;
690
+ const wh = getWaveHeight(drop.x, drop.z, time);
691
+ drop.y = wh;
692
+ drop.velocity = 0.5 + Math.random() * 0.7;
693
+ drop.maxHeight = wh + 80 + Math.random() * 120;
694
+ drop.active = true;
695
+ drop.opacity = 0.5;
696
+ drop.trail.fill(wh);
697
+ };
698
+ const animate = () => {
699
+ time += 0.02;
700
+ const wavePos = particles.geometry.attributes.position.array;
701
+ for (let i = 0; i < particleCount; i++) {
702
+ const i3 = i * 3;
703
+ wavePos[i3 + 1] = getWaveHeight(wavePos[i3], wavePos[i3 + 2], time);
704
+ }
705
+ particles.geometry.attributes.position.needsUpdate = true;
706
+ const tPos = dropTrailParticles.geometry.attributes.position.array;
707
+ const tCol = dropTrailParticles.geometry.attributes.color.array;
708
+ const hPos = dropHeadParticles.geometry.attributes.position.array;
709
+ const hCol = dropHeadParticles.geometry.attributes.color.array;
710
+ let activeCount = 0;
711
+ const inactiveIndices = [];
712
+ for (let i = 0; i < dropCount; i++) {
713
+ const drop = rainDrops[i];
714
+ if (drop.active) {
715
+ activeCount++;
716
+ for (let j = trailLength - 1; j > 0; j--) drop.trail[j] = drop.trail[j - 1];
717
+ drop.trail[0] = drop.y;
718
+ drop.velocity = Math.max(drop.velocity * 0.995, 0.3);
719
+ drop.y += drop.velocity;
720
+ if (drop.opacity < 1) drop.opacity = Math.min(drop.opacity + 0.05, 1);
721
+ if (drop.y > drop.maxHeight) {
722
+ drop.opacity -= 0.03;
723
+ if (drop.opacity <= 0) {
724
+ drop.active = false;
725
+ drop.y = -200;
726
+ drop.trail.fill(-200);
727
+ inactiveIndices.push(i);
728
+ }
729
+ }
730
+ } else {
731
+ inactiveIndices.push(i);
732
+ }
733
+ }
734
+ if (time - lastSpawnTime >= 0.1 && inactiveIndices.length > 0) {
735
+ const spawnCount = Math.min(2 + Math.floor(Math.random() * 2), inactiveIndices.length);
736
+ const shuffled = [...inactiveIndices].sort(() => Math.random() - 0.5);
737
+ for (let n = 0; n < spawnCount; n++) activateDrop(rainDrops[shuffled[n]]);
738
+ lastSpawnTime = time;
739
+ }
740
+ const targetActive = 20;
741
+ const needActivate = Math.max(3, targetActive - activeCount);
742
+ const remaining = inactiveIndices.filter((i) => !rainDrops[i].active);
743
+ if (remaining.length > 0) {
744
+ const shuffled = [...remaining].sort(() => Math.random() - 0.5);
745
+ for (let n = 0; n < Math.min(needActivate, shuffled.length); n++) activateDrop(rainDrops[shuffled[n]]);
746
+ }
747
+ for (let i = 0; i < dropCount; i++) {
748
+ if (!rainDrops[i].active && Math.random() < 0.25) activateDrop(rainDrops[i]);
749
+ }
750
+ for (let i = 0; i < dropCount; i++) {
751
+ const drop = rainDrops[i];
752
+ const hi3 = i * 3;
753
+ hPos[hi3] = drop.x;
754
+ hPos[hi3 + 1] = drop.y;
755
+ hPos[hi3 + 2] = drop.z;
756
+ const brightness = drop.active ? 0.8 + Math.sin(time * 5 + i) * 0.2 : 0;
757
+ hCol[hi3] = 0.5 * brightness * drop.opacity;
758
+ hCol[hi3 + 1] = 1 * brightness * drop.opacity;
759
+ hCol[hi3 + 2] = 1 * brightness * drop.opacity;
760
+ for (let j = 0; j < trailLength; j++) {
761
+ const pi = (i * trailLength + j) * 3;
762
+ tPos[pi] = drop.x;
763
+ tPos[pi + 1] = drop.trail[j];
764
+ tPos[pi + 2] = drop.z;
765
+ const fade = 1 - j / trailLength;
766
+ tCol[pi] = 0.6 * fade * drop.opacity;
767
+ tCol[pi + 1] = 1 * fade * drop.opacity;
768
+ tCol[pi + 2] = 1 * fade * drop.opacity;
769
+ }
770
+ }
771
+ dropTrailParticles.geometry.attributes.position.needsUpdate = true;
772
+ dropTrailParticles.geometry.attributes.color.needsUpdate = true;
773
+ dropHeadParticles.geometry.attributes.position.needsUpdate = true;
774
+ dropHeadParticles.geometry.attributes.color.needsUpdate = true;
775
+ camera.position.x = Math.sin(time * 0.3) * 5;
776
+ camera.lookAt(0, 0, 0);
777
+ renderer.render(scene, camera);
778
+ animationRef.current = requestAnimationFrame(animate);
779
+ };
780
+ animate();
781
+ const handleResize = () => {
782
+ const w = container.clientWidth;
783
+ const h = container.clientHeight;
784
+ camera.aspect = w / h;
785
+ camera.updateProjectionMatrix();
786
+ renderer.setSize(w, h);
787
+ };
788
+ window.addEventListener("resize", handleResize);
789
+ return () => {
790
+ window.removeEventListener("resize", handleResize);
791
+ if (animationRef.current) cancelAnimationFrame(animationRef.current);
792
+ container.removeChild(renderer.domElement);
793
+ geometry.dispose();
794
+ material.dispose();
795
+ bokehGeometry.dispose();
796
+ bokehMaterial.dispose();
797
+ dropTrailGeometry.dispose();
798
+ dropTrailMaterial.dispose();
799
+ dropHeadGeometry.dispose();
800
+ dropHeadMaterial.dispose();
801
+ renderer.dispose();
802
+ };
803
+ }, [background]);
804
+ return /* @__PURE__ */ jsxRuntime.jsx(
805
+ "div",
806
+ {
807
+ ref: containerRef,
808
+ className,
809
+ style: {
810
+ position: "fixed",
811
+ top: 0,
812
+ left: 0,
813
+ width: "100%",
814
+ height: "100%",
815
+ zIndex: 1,
816
+ pointerEvents: "none",
817
+ background,
818
+ ...style
819
+ }
820
+ }
821
+ );
822
+ };
823
+ var ParticleWave_default = ParticleWave;
824
+ var ParticlesBackground = ({
825
+ theme: themeProp,
826
+ isDark: isDarkProp,
827
+ onLoaded,
828
+ className,
829
+ style
830
+ }) => {
831
+ var _a, _b;
832
+ const [init, setInit] = react.useState(false);
833
+ const ctx = useParticleThemeOptional();
834
+ const themeId = (_a = themeProp != null ? themeProp : ctx == null ? void 0 : ctx.themeId) != null ? _a : DEFAULT_THEME_ID;
835
+ const isDark = (_b = isDarkProp != null ? isDarkProp : ctx == null ? void 0 : ctx.isDark) != null ? _b : true;
836
+ const theme = getThemeById(themeId);
837
+ react.useEffect(() => {
838
+ Particles.initParticlesEngine(async (engine) => {
839
+ await slim.loadSlim(engine);
840
+ }).then(() => {
841
+ setInit(true);
842
+ }).catch(() => {
843
+ setInit(true);
844
+ });
845
+ }, []);
846
+ const options = react.useMemo(() => theme.options(isDark), [theme, isDark]);
847
+ if (themeId === "none") return null;
848
+ if (theme.isThreeJS) {
849
+ return /* @__PURE__ */ jsxRuntime.jsx(
850
+ ParticleWave_default,
851
+ {
852
+ background: theme.backgroundGradient || theme.backgroundColor,
853
+ className,
854
+ style
855
+ }
856
+ );
857
+ }
858
+ if (!init) return null;
859
+ return /* @__PURE__ */ jsxRuntime.jsx(
860
+ Particles__default.default,
861
+ {
862
+ id: "rpb-tsparticles",
863
+ className,
864
+ style,
865
+ particlesLoaded: async (container) => onLoaded == null ? void 0 : onLoaded(container),
866
+ options
867
+ },
868
+ themeId
869
+ );
870
+ };
871
+ var ParticlesBackground_default = ParticlesBackground;
872
+ var ThemeSelector = ({
873
+ position = "bottom-right",
874
+ accentColor = "#3b82f6"
875
+ }) => {
876
+ const [open, setOpen] = react.useState(false);
877
+ const { themeId, setTheme } = useParticleTheme();
878
+ const currentTheme = getThemeById(themeId);
879
+ const positionStyles = {
880
+ "bottom-right": { bottom: 80, right: 16 },
881
+ "bottom-left": { bottom: 80, left: 16 },
882
+ "top-right": { top: 80, right: 16 },
883
+ "top-left": { top: 80, left: 16 }
884
+ };
885
+ const drawerSide = position.includes("right") ? "right" : "left";
886
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
887
+ /* @__PURE__ */ jsxRuntime.jsx(
888
+ "button",
889
+ {
890
+ onClick: () => setOpen(true),
891
+ title: "Theme Settings",
892
+ style: {
893
+ position: "fixed",
894
+ ...positionStyles[position],
895
+ zIndex: 9999,
896
+ width: 44,
897
+ height: 44,
898
+ borderRadius: 12,
899
+ display: "flex",
900
+ alignItems: "center",
901
+ justifyContent: "center",
902
+ background: "rgba(255, 255, 255, 0.7)",
903
+ backdropFilter: "blur(12px)",
904
+ WebkitBackdropFilter: "blur(12px)",
905
+ border: "1px solid rgba(255, 255, 255, 0.5)",
906
+ boxShadow: "0 4px 20px rgba(0, 0, 0, 0.08)",
907
+ cursor: "pointer",
908
+ fontSize: 20,
909
+ transition: "transform 0.3s, box-shadow 0.3s"
910
+ },
911
+ onMouseEnter: (e) => {
912
+ e.currentTarget.style.transform = "scale(1.05) translateY(-2px)";
913
+ },
914
+ onMouseLeave: (e) => {
915
+ e.currentTarget.style.transform = "";
916
+ },
917
+ children: currentTheme.icon
918
+ }
919
+ ),
920
+ open && /* @__PURE__ */ jsxRuntime.jsx(
921
+ "div",
922
+ {
923
+ onClick: () => setOpen(false),
924
+ style: {
925
+ position: "fixed",
926
+ inset: 0,
927
+ zIndex: 1e4,
928
+ background: "rgba(0, 0, 0, 0.3)",
929
+ transition: "opacity 0.3s"
930
+ }
931
+ }
932
+ ),
933
+ /* @__PURE__ */ jsxRuntime.jsxs(
934
+ "div",
935
+ {
936
+ style: {
937
+ position: "fixed",
938
+ top: 0,
939
+ [drawerSide]: 0,
940
+ width: 320,
941
+ maxWidth: "85vw",
942
+ height: "100vh",
943
+ zIndex: 10001,
944
+ background: "#fff",
945
+ boxShadow: "-4px 0 24px rgba(0, 0, 0, 0.12)",
946
+ transform: open ? "translateX(0)" : `translateX(${drawerSide === "right" ? "100%" : "-100%"})`,
947
+ transition: "transform 0.3s ease",
948
+ overflowY: "auto",
949
+ display: "flex",
950
+ flexDirection: "column"
951
+ },
952
+ children: [
953
+ /* @__PURE__ */ jsxRuntime.jsxs(
954
+ "div",
955
+ {
956
+ style: {
957
+ display: "flex",
958
+ alignItems: "center",
959
+ justifyContent: "space-between",
960
+ padding: "16px 20px",
961
+ borderBottom: "1px solid #f0f0f0"
962
+ },
963
+ children: [
964
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, fontSize: 16 }, children: "Particle Theme" }),
965
+ /* @__PURE__ */ jsxRuntime.jsx(
966
+ "button",
967
+ {
968
+ onClick: () => setOpen(false),
969
+ style: {
970
+ background: "none",
971
+ border: "none",
972
+ fontSize: 20,
973
+ cursor: "pointer",
974
+ color: "#999",
975
+ padding: 4
976
+ },
977
+ children: "\xD7"
978
+ }
979
+ )
980
+ ]
981
+ }
982
+ ),
983
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: 16, display: "flex", flexDirection: "column", gap: 12 }, children: [...particleThemes, {
984
+ id: "none",
985
+ name: "None",
986
+ icon: "\u{1F6AB}",
987
+ description: "Disable particle effects"
988
+ }].map((theme) => {
989
+ const isActive = theme.id === themeId;
990
+ return /* @__PURE__ */ jsxRuntime.jsxs(
991
+ "div",
992
+ {
993
+ onClick: () => setTheme(theme.id),
994
+ style: {
995
+ padding: 16,
996
+ borderRadius: 12,
997
+ cursor: "pointer",
998
+ display: "flex",
999
+ alignItems: "center",
1000
+ gap: 12,
1001
+ transition: "all 0.2s",
1002
+ border: `2px solid ${isActive ? accentColor : "transparent"}`,
1003
+ background: isActive ? `${accentColor}10` : "#f5f5f5"
1004
+ },
1005
+ onMouseEnter: (e) => {
1006
+ if (!isActive) e.currentTarget.style.borderColor = "#e0e0e0";
1007
+ },
1008
+ onMouseLeave: (e) => {
1009
+ if (!isActive) e.currentTarget.style.borderColor = "transparent";
1010
+ },
1011
+ children: [
1012
+ /* @__PURE__ */ jsxRuntime.jsx(
1013
+ "div",
1014
+ {
1015
+ style: {
1016
+ width: 48,
1017
+ height: 48,
1018
+ borderRadius: 12,
1019
+ display: "flex",
1020
+ alignItems: "center",
1021
+ justifyContent: "center",
1022
+ fontSize: 24,
1023
+ background: isActive ? accentColor : "#e5e5e5",
1024
+ flexShrink: 0
1025
+ },
1026
+ children: theme.icon
1027
+ }
1028
+ ),
1029
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
1030
+ /* @__PURE__ */ jsxRuntime.jsx(
1031
+ "div",
1032
+ {
1033
+ style: {
1034
+ fontWeight: 600,
1035
+ fontSize: 14,
1036
+ color: isActive ? accentColor : "#1f2937"
1037
+ },
1038
+ children: theme.name
1039
+ }
1040
+ ),
1041
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "#9ca3af", marginTop: 2 }, children: theme.description })
1042
+ ] }),
1043
+ isActive && /* @__PURE__ */ jsxRuntime.jsx(
1044
+ "div",
1045
+ {
1046
+ style: {
1047
+ width: 24,
1048
+ height: 24,
1049
+ borderRadius: "50%",
1050
+ background: accentColor,
1051
+ display: "flex",
1052
+ alignItems: "center",
1053
+ justifyContent: "center",
1054
+ flexShrink: 0
1055
+ },
1056
+ children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "#fff", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 13l4 4L19 7" }) })
1057
+ }
1058
+ )
1059
+ ]
1060
+ },
1061
+ theme.id
1062
+ );
1063
+ }) }),
1064
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "0 16px 16px", marginTop: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsx(
1065
+ "div",
1066
+ {
1067
+ style: {
1068
+ padding: 16,
1069
+ background: "#f9fafb",
1070
+ borderRadius: 12,
1071
+ fontSize: 13,
1072
+ color: "#6b7280",
1073
+ lineHeight: 1.6
1074
+ },
1075
+ children: "Your theme selection is automatically saved and will be remembered on your next visit."
1076
+ }
1077
+ ) })
1078
+ ]
1079
+ }
1080
+ )
1081
+ ] });
1082
+ };
1083
+ var ThemeSelector_default = ThemeSelector;
1084
+
1085
+ exports.DEFAULT_COLORS = DEFAULT_COLORS;
1086
+ exports.DEFAULT_THEME_ID = DEFAULT_THEME_ID;
1087
+ exports.ParticleProvider = ParticleProvider;
1088
+ exports.ParticleWave = ParticleWave_default;
1089
+ exports.ParticlesBackground = ParticlesBackground_default;
1090
+ exports.ThemeSelector = ThemeSelector_default;
1091
+ exports.baseConfig = baseConfig;
1092
+ exports.bubbleTheme = bubbleTheme;
1093
+ exports.fireflyTheme = fireflyTheme;
1094
+ exports.geometryTheme = geometryTheme;
1095
+ exports.getThemeById = getThemeById;
1096
+ exports.particleThemes = particleThemes;
1097
+ exports.snowTheme = snowTheme;
1098
+ exports.starlineTheme = starlineTheme;
1099
+ exports.starsTheme = starsTheme;
1100
+ exports.useParticleTheme = useParticleTheme;
1101
+ exports.waveTheme = waveTheme;
1102
+ //# sourceMappingURL=index.js.map
1103
+ //# sourceMappingURL=index.js.map