PageSourceSearch

https://5pfouk4zx997.trickle.host/

html 5pfouk4zx997.trickle.host collected 2026-10-03 05:07:57 UTC 137,098 bytes, 1,444 lines download raw bytes

1<html lang="en"><head>
2    <meta charset="UTF-8">
3    <meta name="viewport" content="width=device-width, initial-scale=1.0">
4    <title>Free Fireworks</title>
5    <meta property="og:title" content="Free Fireworks">
6    <meta property="og:description" content="Click, watch, relax. Let the fireworks do the rest.">
7    <meta property="og:type" content="website">
8    <meta property="og:image" content="https://app.trickle.so/storage/public/images/usr_09073b5640000001/2fa2439c-9c6a-43f6-b9a8-700ec784a7f9.png">
9    <meta property="og:image:width" content="1200">
10    <meta property="og:image:height" content="630">
11    <meta property="og:site_name" content="Free Fireworks">
12    <meta property="og:locale" content="en_US">
13    <meta name="twitter:card" content="summary_large_image">
14    <meta name="twitter:title" content="Free Fireworks">
15    <meta name="twitter:description" content="Click, watch, relax. Let the fireworks do the rest.">
16    <meta name="twitter:image" content="https://app.trickle.so/storage/public/images/usr_09073b5640000001/2fa2439c-9c6a-43f6-b9a8-700ec784a7f9.png">
17    <meta name="description" content="Click, watch, relax. Let the fireworks do the rest.">
18    <meta name="theme-color" content="#000000">
19    
20    <!-- React and Babel -->
21    
21<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
21
22    
22<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
22
23    
24    
24<script src="https://cdn.tailwindcss.com"></script>
24
25
26    <!-- Styles -->
27    <style>* {
28    margin: 0;
29    padding: 0;
30    box-sizing: border-box;
31}
32
33body {
34    overflow: hidden;
35    background: black;
36    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
37}
38
39/* 禁用画布和UI元素的文本选择 */
40.firework-canvas,
41.time-display,
42.settings-list,
43.guide-button,
44.sound-toggle,
45.auto-toggle,
46.cooldown-indicator,
47.firework-console {
48    -webkit-user-select: none;
49    -moz-user-select: none;
50    -ms-user-select: none;
51    user-select: none;
52    -webkit-touch-callout: none; /* 禁用iOS长按菜单 */
53}
54
55/* 保持链接和部分文本可选 */
56.trickle-link,
57.guide-content p,
58.guide-content li {
59    -webkit-user-select: text;
60    -moz-user-select: text;
61    -ms-user-select: text;
62    user-select: text;
63    -webkit-touch-callout: default; /* 恢复iOS长按菜单 */
64}
65
66/* 移动端特别处理 */
67@media (max-width: 768px) {
68    /* 增强移动端交互元素的触摸响应 */
69    .toggle-button,
70    .action-button,
71    .close-button,
72    .color-picker,
73    .settings-slider input[type="range"],
74    .date-format-select {
75        -webkit-tap-highlight-color: transparent; /* 移除点击高亮 */
76        touch-action: manipulation; /* 优化触摸操作 */
77    }
78}
79</style>
80    <style>.firework-canvas {
81    position: fixed;
82    top: 0;
83    left: 0;
84    width: 100%;
85    height: 100%;
86    background: black;
87    touch-action: none;
88}
89</style>
90    <style>.welcome-message {
91    position: fixed;
92    top: 50%;
93    left: 50%;
94    transform: translate(-50%, -50%);
95    text-align: center;
96    color: white;
97    animation: welcomeFadeInOut 5s forwards;
98    width: 100%;
99    max-width: 600px;
100    padding: 0 1rem;
101    user-select: none;
102    -webkit-touch-callout: none;
103}
104
105.welcome-title {
106    font-size: 3rem;
107    margin-bottom: 1rem;
108    text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
109}
110
111.welcome-text {
112    font-size: 1.5rem;
113    line-height: 2rem;
114    opacity: 0.8;
115    margin-bottom: 1rem;
116}
117
118.welcome-built {
119    font-size: 1rem;
120    color: #A855F7;
121}
122
123@keyframes welcomeFadeInOut {
124    0% { 
125        opacity: 0;
126        transform: translate(-50%, calc(-50% - 20px));
127    }
128    20% { 
129        opacity: 1;
130        transform: translate(-50%, -50%);
131    }
132    80% { 
133        opacity: 1;
134        transform: translate(-50%, -50%);
135    }
136    100% { 
137        opacity: 0;
138        transform: translate(-50%, calc(-50% + 20px));
139    }
140}
141
142@media (max-width: 768px) {
143    .welcome-title {
144        font-size: 2.5rem;
145    }
146
147    .welcome-text {
148        font-size: 1.25rem;
149        line-height: 1.75rem;
150    }
151
152    .welcome-built {
153        font-size: 0.875rem;
154    }
155}
156
157@media (max-width: 480px) {
158    .welcome-title {
159        font-size: 2rem;
160    }
161
162    .welcome-text {
163        font-size: 1rem;
164        line-height: 1.5rem;
165    }
166
167    .welcome-built {
168        font-size: 0.75rem;
169    }
170}
171</style>
172    <style>.time-display {
173    position: fixed;
174    transform: translate(-50%, -50%);
175    color: white;
176    font-family: monospace;
177    padding: 0.5rem 1rem;
178    border-radius: 0.5rem;
179    opacity: 0;
180    transition: opacity 0.3s ease;
181    text-align: center;
182    z-index: 5;
183    user-select: none;
184    -webkit-user-select: none;
185    -moz-user-select: none;
186    -ms-user-select: none;
187}
188
189.time-display.active {
190    opacity: 1;
191}
192
193.time-display.with-background {
194    backdrop-filter: blur(8px);
195    -webkit-backdrop-filter: blur(8px);
196}
197
198.time-display.dragging {
199    transition: none;
200}
201
202.date-display {
203    font-size: 0.3em;
204    opacity: 0.8;
205    margin-bottom: 0.25rem;
206}
207
208.select-container {
209    position: relative;
210    width: 100%;
211}
212
213.date-format-select {
214    width: 100%;
215    padding: 0.25rem 0.5rem;
216    font-size: 0.75rem;
217    color: white;
218    background: rgba(255, 255, 255, 0.1);
219    border: 1px solid rgba(255, 255, 255, 0.2);
220    border-radius: 0.375rem;
221    cursor: pointer;
222    appearance: none;
223    -webkit-appearance: none;
224}
225
226.date-format-select:focus {
227    outline: none;
228    border-color: rgba(255, 255, 255, 0.3);
229}
230
231.select-arrow {
232    position: absolute;
233    right: 0.5rem;
234    top: 50%;
235    transform: translateY(-50%);
236    pointer-events: none;
237    color: rgba(255, 255, 255, 0.7);
238    font-size: 0.75rem;
239}
240
241@media (max-width: 768px) {
242    .date-format-select {
243        padding: 0.375rem 0.75rem;
244        font-size: 0.875rem;
245    }
246}
247</style>
248    <style>@import 'settings/layout.css';
249@import 'settings/controls.css';
250@import 'settings/slider.css';
251@import 'settings/branding.css';
252</style>
253    <style>.settings-slider {
254    position: relative;
255    display: flex;
256    align-items: center;
257    gap: 0.75rem;
258    height: 24px;
259}
260
261.settings-slider input[type="range"] {
262    flex: 1;
263    height: 4px;
264    -webkit-appearance: none;
265    appearance: none;
266    background: transparent;
267    position: relative;
268}
269
270.settings-slider input[type="range"]::-webkit-slider-runnable-track {
271    width: 100%;
272    height: 4px;
273    background: rgba(255, 255, 255, 0.2);
274    border-radius: 2px;
275    border: none;
276}
277
278.settings-slider input[type="range"]::-webkit-slider-thumb {
279    -webkit-appearance: none;
280    width: 16px;
281    height: 16px;
282    background: white;
283    border-radius: 50%;
284    margin-top: -6px;
285    cursor: pointer;
286    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
287    transition: all 0.2s ease;
288}
289
290.settings-slider input[type="range"]::-webkit-slider-thumb:hover {
291    transform: scale(1.1);
292}
293
294.settings-slider input[type="range"]::-webkit-slider-thumb:active {
295    transform: scale(0.95);
296}
297
298.slider-value {
299    color: white;
300    font-size: 0.9rem;
301    min-width: 45px;
302    text-align: right;
303    opacity: 0.9;
304}
305
306@media (max-width: 768px) {
307    .settings-slider input[type="range"] {
308        width: 100px;
309    }
310}
311
312@media (max-width: 480px) {
313    .settings-slider input[type="range"] {
314        width: 80px;
315    }
316    
317    .slider-value {
318        min-width: 35px;
319        font-size: 0.85rem;
320    }
321}
322</style>
323    <style>.auto-toggle-container {
324    position: relative;
325    display: flex;
326    align-items: center;
327}
328
329.auto-toggle-tooltip {
330    position: absolute;
331    right: calc(100% + 0.5rem);
332    bottom: 50%;
333    transform: translateY(50%);
334    background: rgba(0, 0, 0, 0.8);
335    color: white;
336    padding: 0.5rem 1rem;
337    border-radius: 0.5rem;
338    font-size: 0.875rem;
339    white-space: nowrap;
340    animation: fadeInOut 5s forwards;
341    border: 1px solid rgba(255, 255, 255, 0.2);
342    backdrop-filter: blur(8px);
343    -webkit-backdrop-filter: blur(8px);
344}
345
346.auto-toggle-tooltip::after {
347    content: '';
348    position: absolute;
349    right: -6px;
350    bottom: 50%;
351    transform: translateY(50%);
352    border-width: 6px;
353    border-style: solid;
354    border-color: transparent transparent transparent rgba(0, 0, 0, 0.8);
355}
356
357.auto-toggle {
358    width: 3rem;
359    height: 3rem;
360    border-radius: 50%;
361    background: rgba(0, 0, 0, 0.8);
362    border: 1px solid rgba(255, 255, 255, 0.2);
363    color: white;
364    cursor: pointer;
365    display: flex;
366    align-items: center;
367    justify-content: center;
368    transition: all 0.3s ease;
369    opacity: 1;
370    padding: 0.5rem;
371}
372
373.auto-toggle:hover {
374    background: rgba(255, 255, 255, 0.1);
375    transform: scale(1.1);
376}
377
378.auto-toggle.active {
379    opacity: 0;
380    pointer-events: none;
381}
382
383.zen-icon {
384    width: 100%;
385    height: 100%;
386    object-fit: contain;
387    transition: all 0.3s ease;
388    opacity: 0.8;
389    filter: invert(1);
390}
391
392.auto-toggle:hover .zen-icon {
393    opacity: 1;
394    transform: scale(1.1);
395}
396
397@keyframes fadeInOut {
398    0% { opacity: 0; transform: translate(-10px, 50%); }
399    20% { opacity: 1; transform: translate(0, 50%); }
400    80% { opacity: 1; transform: translate(0, 50%); }
401    100% { opacity: 0; transform: translate(10px, 50%); }
402}
403
404@media (max-width: 768px) {
405    .auto-toggle {
406        width: 2.25rem;
407        height: 2.25rem;
408        min-width: 2.25rem;
409        min-height: 2.25rem;
410        padding: 0.5rem;
411    }
412    
413    .auto-toggle-tooltip {
414        font-size: 0.75rem;
415        padding: 0.35rem 0.75rem;
416    }
417}
418</style>
419    <style>.sound-toggle {
420    width: 3rem;
421    height: 3rem;
422    border-radius: 50%;
423    background: rgba(0, 0, 0, 0.8);
424    border: 1px solid rgba(255, 255, 255, 0.2);
425    color: white;
426    cursor: pointer;
427    display: flex;
428    align-items: center;
429    justify-content: center;
430    transition: all 0.3s ease;
431    opacity: 1;
432    padding: 0.75rem;
433    min-width: 3rem;
434    min-height: 3rem;
435}
436
437.sound-toggle:hover {
438    background: rgba(255, 255, 255, 0.1);
439    transform: scale(1.1);
440}
441
442.sound-toggle svg {
443    width: 100%;
444    height: 100%;
445    transition: all 0.3s ease;
446    opacity: 0.8;
447    min-width: 1.5rem;
448    min-height: 1.5rem;
449}
450
451.sound-toggle:hover svg {
452    opacity: 1;
453    transform: scale(1.1);
454}
455
456@media (max-width: 768px) {
457    .sound-toggle {
458        width: 2.25rem;
459        height: 2.25rem;
460        min-width: 2.25rem;
461        min-height: 2.25rem;
462        padding: 0.5rem;
463    }
464}
465</style>
466    <style>.guide-button {
467    width: 3rem;
468    height: 3rem;
469    border-radius: 50%;
470    background: rgba(0, 0, 0, 0.8);
471    border: 1px solid rgba(255, 255, 255, 0.2);
472    color: white;
473    cursor: pointer;
474    display: flex;
475    align-items: center;
476    justify-content: center;
477    transition: all 0.3s ease;
478    opacity: 1;
479    padding: 0.75rem;
480    min-width: 3rem;
481    min-height: 3rem;
482    touch-action: manipulation;
483}
484
485.guide-button:hover {
486    background: rgba(255, 255, 255, 0.1);
487    transform: scale(1.1);
488}
489
490.guide-button svg {
491    width: 100%;
492    height: 100%;
493    transition: all 0.3s ease;
494    opacity: 0.8;
495    min-width: 1.5rem;
496    min-height: 1.5rem;
497}
498
499.guide-button:hover svg {
500    opacity: 1;
501    transform: scale(1.1);
502}
503
504.guide-overlay {
505    position: fixed;
506    top: 0;
507    left: 0;
508    right: 0;
509    bottom: 0;
510    background: rgba(0, 0, 0, 0.85);
511    display: flex;
512    align-items: center;
513    justify-content: center;
514    z-index: 1000;
515    backdrop-filter: blur(8px);
516    -webkit-backdrop-filter: blur(8px);
517}
518
519.guide-content {
520    position: relative;
521    background: rgba(30, 30, 30, 0.95);
522    border-radius: 1rem;
523    padding: 2rem;
524    max-width: 90%;
525    width: 480px;
526    color: white;
527    border: 1px solid rgba(255, 255, 255, 0.1);
528    user-select: text;
529    -webkit-touch-callout: default;
530}
531
532.guide-close-button {
533    position: absolute;
534    top: 1rem;
535    right: 1rem;
536    background: transparent;
537    border: none;
538    color: rgba(255, 255, 255, 0.6);
539    cursor: pointer;
540    padding: 0.5rem;
541    display: flex;
542    align-items: center;
543    justify-content: center;
544    transition: all 0.3s ease;
545    touch-action: manipulation;
546    -webkit-tap-highlight-color: transparent;
547}
548
549.guide-close-button:hover {
550    color: white;
551    transform: scale(1.1);
552}
553
554.guide-close-button svg {
555    width: 1.25rem;
556    height: 1.25rem;
557}
558
559.guide-title {
560    font-size: 1.5rem;
561    font-weight: 600;
562    margin-bottom: 1.5rem;
563    padding-right: 2rem;
564}
565
566.guide-section {
567    margin-bottom: 1.5rem;
568}
569
570.guide-section h3 {
571    font-size: 1.125rem;
572    font-weight: 500;
573    margin-bottom: 0.75rem;
574    color: rgba(255, 255, 255, 0.9);
575}
576
577.guide-section ul {
578    list-style: none;
579    padding: 0;
580    margin: 0;
581}
582
583.guide-section li {
584    margin-bottom: 0.5rem;
585    color: rgba(255, 255, 255, 0.7);
586    font-size: 0.9375rem;
587    line-height: 1.5;
588}
589
590.guide-section li strong {
591    color: rgba(255, 255, 255, 0.9);
592    font-weight: 500;
593    margin-right: 0.25rem;
594}
595
596.guide-footer {
597    color: rgba(255, 255, 255, 0.5);
598    font-size: 0.875rem;
599    font-style: italic;
600    text-align: center;
601    margin-top: 2rem;
602}
603
604@media (max-width: 768px) {
605    .guide-button {
606        width: 2.25rem;
607        height: 2.25rem;
608        min-width: 2.25rem;
609        min-height: 2.25rem;
610        padding: 0.5rem;
611        -webkit-tap-highlight-color: transparent;
612    }
613
614    .guide-content {
615        padding: 1.5rem;
616        max-width: 95%;
617        margin: 1rem;
618    }
619
620    .guide-title {
621        font-size: 1.25rem;
622        margin-bottom: 1.25rem;
623    }
624
625    .guide-section h3 {
626        font-size: 1rem;
627    }
628
629    .guide-section li {
630        font-size: 0.875rem;
631    }
632
633    .guide-footer {
634        font-size: 0.8125rem;
635        margin-top: 1.5rem;
636    }
637
638    .guide-close-button {
639        top: 0.75rem;
640        right: 0.75rem;
641    }
642
643    .guide-close-button svg {
644        width: 1rem;
645        height: 1rem;
646    }
647}
648
649@media (max-width: 480px) {
650    .guide-content {
651        padding: 1.25rem;
652    }
653}
654</style>
655    <style>.firework-console {
656    position: fixed;
657    left: 1rem;
658    top: 1rem;
659    background: rgba(0, 0, 0, 0.85);
660    backdrop-filter: blur(8px);
661    -webkit-backdrop-filter: blur(8px);
662    border: 1px solid rgba(255, 255, 255, 0.1);
663    border-radius: 0.75rem;
664    padding: 1rem;
665    color: white;
666    width: 280px;
667    z-index: 100;
668}
669
670.console-toggle {
671    position: fixed;
672    left: 1rem;
673    top: 1rem;
674    width: 3rem;
675    height: 3rem;
676    border-radius: 50%;
677    background: rgba(0, 0, 0, 0.8);
678    border: 1px solid rgba(255, 255, 255, 0.2);
679    color: white;
680    cursor: pointer;
681    display: flex;
682    align-items: center;
683    justify-content: center;
684    transition: all 0.3s ease;
685    z-index: 100;
686}
687
688.console-toggle:hover {
689    background: rgba(255, 255, 255, 0.1);
690    transform: scale(1.1);
691}
692
693.console-toggle svg {
694    width: 1.5rem;
695    height: 1.5rem;
696    opacity: 0.8;
697    transition: opacity 0.3s ease;
698}
699
700.console-toggle:hover svg {
701    opacity: 1;
702}
703
704.console-header {
705    display: flex;
706    justify-content: space-between;
707    align-items: center;
708    margin-bottom: 1rem;
709}
710
711.console-header h3 {
712    font-size: 1rem;
713    font-weight: 500;
714    margin: 0;
715}
716
717.console-header .close-button {
718    background: transparent;
719    border: none;
720    color: rgba(255, 255, 255, 0.6);
721    cursor: pointer;
722    padding: 0.25rem;
723    display: flex;
724    align-items: center;
725    justify-content: center;
726    transition: all 0.3s ease;
727}
728
729.console-header .close-button:hover {
730    color: white;
731    transform: scale(1.1);
732}
733
734.console-header .close-button svg {
735    width: 1.25rem;
736    height: 1.25rem;
737}
738
739.controls-section h4 {
740    font-size: 0.875rem;
741    margin-bottom: 0.5rem;
742    color: rgba(255, 255, 255, 0.9);
743}
744
745.firework-buttons {
746    display: grid;
747    grid-template-columns: repeat(2, 1fr);
748    gap: 0.5rem;
749}
750
751.firework-button {
752    background: rgba(255, 255, 255, 0.1);
753    border: 1px solid rgba(255, 255, 255, 0.2);
754    border-radius: 0.375rem;
755    padding: 0.5rem;
756    color: white;
757    font-size: 0.875rem;
758    cursor: pointer;
759    transition: all 0.2s ease;
760}
761
762.firework-button:hover {
763    background: rgba(255, 255, 255, 0.2);
764    transform: translateY(-1px);
765}
766
767.firework-button:active {
768    transform: translateY(0);
769}
770
771@media (max-width: 768px) {
772    .firework-console {
773        left: 0.5rem;
774        top: 0.5rem;
775        width: calc(100% - 1rem);
776        max-width: 280px;
777    }
778
779    .console-toggle {
780        left: 0.5rem;
781        top: 0.5rem;
782        width: 2.5rem;
783        height: 2.5rem;
784    }
785
786    .console-toggle svg {
787        width: 1.25rem;
788        height: 1.25rem;
789    }
790}
791</style>
792    <style>.cooldown-indicator {
793    position: fixed;
794    bottom: 2rem;
795    left: 50%;
796    transform: translateX(-50%);
797    width: 200px;
798    height: 2rem;
799    background: rgba(0, 0, 0, 0.5);
800    border-radius: 1rem;
801    overflow: hidden;
802    backdrop-filter: blur(8px);
803    -webkit-backdrop-filter: blur(8px);
804    border: 1px solid rgba(255, 255, 255, 0.1);
805    z-index: 100;
806    display: flex;
807    align-items: center;
808    justify-content: center;
809}
810
811.cooldown-progress {
812    position: absolute;
813    top: 0;
814    left: 0;
815    height: 100%;
816    width: 0;
817    background: rgba(168, 85, 247, 0.4);
818    border-radius: 1rem;
819    transition: width 16ms linear;
820}
821
822.cooldown-text {
823    position: relative;
824    color: white;
825    font-size: 0.875rem;
826    font-weight: 500;
827    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
828    user-select: none;
829    -webkit-user-select: none;
830    z-index: 1;
831}
832
833@keyframes shake {
834    0%, 100% { transform: translateX(-50%) scale(1); }
835    25% { transform: translateX(-52%) scale(1.05); }
836    50% { transform: translateX(-48%) scale(0.95); }
837    75% { transform: translateX(-50%) scale(1.02); }
838}
839
840.cooldown-indicator.shaking {
841    animation: shake 0.5s ease-in-out;
842}
843
844@media (max-width: 768px) {
845    .cooldown-indicator {
846        bottom: 1.5rem;
847        width: 160px;
848        height: 1.75rem;
849    }
850
851    .cooldown-text {
852        font-size: 0.75rem;
853    }
854}
855</style>
856    <style>.trickle-badge {
857    position: fixed;
858    left: 1rem;
859    bottom: 1rem;
860    z-index: 10;
861    text-decoration: none;
862    transition: all 0.3s ease;
863}
864
865.trickle-badge-content {
866    display: flex;
867    align-items: center;
868    gap: 0.5rem;
869    background: rgba(0, 0, 0, 0.8);
870    padding: 0.5rem 0.75rem;
871    border-radius: 2rem;
872    border: 1px solid rgba(255, 255, 255, 0.2);
873    backdrop-filter: blur(8px);
874    -webkit-backdrop-filter: blur(8px);
875    transition: all 0.3s ease;
876}
877
878.trickle-badge:hover .trickle-badge-content {
879    background: rgba(255, 255, 255, 0.1);
880    transform: translateY(-2px);
881}
882
883.trickle-badge-text {
884    color: rgba(255, 255, 255, 0.6);
885    font-size: 0.875rem;
886}
887
888.trickle-badge-logo {
889    height: 1.125rem;
890    width: auto;
891}
892
893@media (max-width: 768px) {
894    .trickle-badge {
895        left: 0.75rem;
896        bottom: 0.75rem;
897    }
898
899    .trickle-badge-content {
900        padding: 0.4rem 0.6rem;
901    }
902
903    .trickle-badge-text {
904        font-size: 0.75rem;
905    }
906
907    .trickle-badge-logo {
908        height: 1rem;
909    }
910}
911</style>
912    <style>.settings-list {
913    display: flex;
914    flex-direction: column;
915    gap: 0.75rem;
916    user-select: none;
917    -webkit-touch-callout: none;
918}
919
920.settings-item {
921    display: flex;
922    justify-content: space-between;
923    align-items: center;
924    gap: 0.75rem;
925}
926
927.settings-label {
928    color: rgba(255, 255, 255, 0.9);
929    font-size: 0.8125rem;
930    flex-grow: 1;
931}
932
933.settings-control {
934    display: flex;
935    align-items: center;
936    gap: 0.5rem;
937}
938
939/* Desktop layout */
940@media (min-width: 769px) {
941    .settings-list {
942        display: grid;
943        grid-template-columns: repeat(2, 1fr);
944        gap: 0.75rem;
945        margin: 0 auto;
946    }
947
948    .settings-item:last-child {
949        grid-column: 1 / -1;
950        margin-top: 0.25rem;
951    }
952}
953
954/* Mobile layout */
955@media (max-width: 768px) {
956    .settings-list {
957        gap: 0.75rem;
958    }
959
960    .settings-label {
961        font-size: 0.875rem;
962    }
963
964    .settings-control {
965        gap: 0.5rem;
966    }
967
968    /* 优化移动端触摸操作 */
969    .settings-control > * {
970        touch-action: manipulation;
971        -webkit-tap-highlight-color: transparent;
972    }
973}
974</style>
975    <style>.toggle-button {
976    width: 2rem;
977    height: 1rem;
978    border-radius: 0.5rem;
979    background: rgba(255, 255, 255, 0.2);
980    border: none;
981    position: relative;
982    cursor: pointer;
983    transition: all 0.3s ease;
984}
985
986.toggle-button::after {
987    content: '';
988    position: absolute;
989    top: 0.125rem;
990    left: 0.125rem;
991    width: 0.75rem;
992    height: 0.75rem;
993    border-radius: 50%;
994    background: white;
995    transition: all 0.3s ease;
996}
997
998.toggle-button.active {
999    background: #A855F7;
1000}
1001
1002.toggle-button.active::after {
1003    left: calc(100% - 0.875rem);
1004}
1005
1006.action-button {
1007    background: rgba(255, 255, 255, 0.1);
1008    border: 1px solid rgba(255, 255, 255, 0.2);
1009    color: white;
1010    padding: 0.25rem 0.5rem;
1011    border-radius: 0.25rem;
1012    font-size: 0.8125rem;
1013    cursor: pointer;
1014    transition: all 0.3s ease;
1015}
1016
1017.action-button:hover {
1018    background: rgba(255, 255, 255, 0.2);
1019}
1020
1021.close-button {
1022    background: rgba(255, 255, 255, 0.1);
1023    border: 1px solid rgba(255, 255, 255, 0.2);
1024    color: white;
1025    padding: 0.25rem 0.5rem;
1026    border-radius: 0.25rem;
1027    font-size: 0.8125rem;
1028    cursor: pointer;
1029    transition: all 0.3s ease;
1030    min-width: 3.5rem;
1031    text-align: center;
1032}
1033
1034.close-button:hover {
1035    background: rgba(255, 255, 255, 0.2);
1036}
1037
1038.color-picker {
1039    width: 1.75rem;
1040    height: 1.75rem;
1041    padding: 0;
1042    border: none;
1043    border-radius: 0.25rem;
1044    cursor: pointer;
1045    background: none;
1046}
1047
1048.color-picker::-webkit-color-swatch-wrapper {
1049    padding: 0;
1050}
1051
1052.color-picker::-webkit-color-swatch {
1053    border: 1px solid rgba(255, 255, 255, 0.2);
1054    border-radius: 0.25rem;
1055}
1056
1057@media (max-width: 768px) {
1058    .toggle-button {
1059        width: 2.5rem;
1060        height: 1.25rem;
1061    }
1062
1063    .toggle-button::after {
1064        width: 1rem;
1065        height: 1rem;
1066    }
1067
1068    .toggle-button.active::after {
1069        left: calc(100% - 1.125rem);
1070    }
1071
1072    .action-button,
1073    .close-button {
1074        padding: 0.375rem 0.75rem;
1075        font-size: 0.875rem;
1076    }
1077
1078    .color-picker {
1079        width: 2rem;
1080        height: 2rem;
1081    }
1082}
1083</style>
1084    <style>.settings-slider {
1085    display: flex;
1086    ali
1086gn-items: center;
1087    gap: 0.5rem;
1088    min-width: 100px;
1089}
1090
1091.settings-slider input[type="range"] {
1092    flex: 1;
1093    height: 3px;
1094    -webkit-appearance: none;
1095    appearance: none;
1096    background: transparent;
1097    position: relative;
1098}
1099
1100.settings-slider input[type="range"]::-webkit-slider-runnable-track {
1101    width: 100%;
1102    height: 3px;
1103    background: rgba(255, 255, 255, 0.2);
1104    border-radius: 1.5px;
1105    border: none;
1106}
1107
1108.settings-slider input[type="range"]::-webkit-slider-thumb {
1109    -webkit-appearance: none;
1110    width: 12px;
1111    height: 12px;
1112    background: white;
1113    border-radius: 50%;
1114    margin-top: -4.5px;
1115    cursor: pointer;
1116    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
1117    transition: all 0.2s ease;
1118}
1119
1120.settings-slider input[type="range"]::-webkit-slider-thumb:hover {
1121    transform: scale(1.1);
1122}
1123
1124.settings-slider input[type="range"]::-webkit-slider-thumb:active {
1125    transform: scale(0.95);
1126}
1127
1128.size-value,
1129.speed-value {
1130    color: rgba(255, 255, 255, 0.9);
1131    font-size: 0.8125rem;
1132    min-width: 2.75rem;
1133    text-align: right;
1134}
1135
1136@media (max-width: 768px) {
1137    .settings-slider {
1138        min-width: 120px;
1139        gap: 0.75rem;
1140    }
1141
1142    .settings-slider input[type="range"] {
1143        height: 4px;
1144    }
1145
1146    .settings-slider input[type="range"]::-webkit-slider-runnable-track {
1147        height: 4px;
1148    }
1149
1150    .settings-slider input[type="range"]::-webkit-slider-thumb {
1151        width: 16px;
1152        height: 16px;
1153        margin-top: -6px;
1154    }
1155
1156    .size-value,
1157    .speed-value {
1158        font-size: 0.875rem;
1159        min-width: 3rem;
1160    }
1161}
1162</style>
1163    <style>.trickle-link {
1164    color: #A855F7;
1165    text-decoration: none;
1166    transition: all 0.3s ease;
1167    font-size: 0.8125rem;
1168}
1169
1170.trickle-link:hover {
1171    opacity: 0.8;
1172}
1173
1174@media (max-width: 768px) {
1175    .trickle-link {
1176        font-size: 0.875rem;
1177    }
1178}
1179</style>
1180    <style>.time-panel {
1181    position: fixed;
1182    bottom: 2rem;
1183    left: 50%;
1184    transform: translateX(-50%);
1185    background: rgba(0, 0, 0, 0.85);
1186    backdrop-filter: blur(8px);
1187    -webkit-backdrop-filter: blur(8px);
1188    padding: 1rem;
1189    border-radius: 0.75rem;
1190    border: 1px solid rgba(255, 255, 255, 0.1);
1191    opacity: 0;
1192    transition: opacity 0.3s ease;
1193    pointer-events: none;
1194    z-index: 10;
1195}
1196
1197.time-panel.active {
1198    opacity: 1;
1199    pointer-events: auto;
1200}
1201
1202.hover-trigger {
1203    position: fixed;
1204    bottom: 0;
1205    left: 0;
1206    right: 0;
1207    height: 200px;
1208    z-index: 9;
1209}
1210
1211.modal-mask {
1212    position: fixed;
1213    top: 0;
1214    left: 0;
1215    right: 0;
1216    bottom: 0;
1217    background: rgba(0, 0, 0, 0.5);
1218    backdrop-filter: blur(2px);
1219    -webkit-backdrop-filter: blur(2px);
1220    opacity: 0;
1221    pointer-events: none;
1222    transition: opacity 0.3s ease;
1223    z-index: 5;
1224}
1225
1226.modal-mask.active {
1227    opacity: 1;
1228    pointer-events: auto;
1229}
1230
1231/* Mobile styles */
1232@media (max-width: 768px) {
1233    .time-panel {
1234        bottom: 0;
1235        left: 0;
1236        right: 0;
1237        width: 100%;
1238        transform: translateY(100%);
1239        border-radius: 1rem 1rem 0 0;
1240        padding: 1.25rem;
1241        transition: transform 0.3s ease;
1242    }
1243
1244    .time-panel.active {
1245        transform: translateY(0);
1246    }
1247
1248    .hover-trigger {
1249        display: none;
1250    }
1251}
1252</style>
1253<script id="trickle-project">window.__PROTO_STATE__ = {"projectId":"proj_Tu4y05KHcD","userId":"usr_09073b5640000001","workspaceId":"workspace_id_usr_09073b5640000001","memberId":"member_id_usr_09073b5640000001","projectTitle":"Free Fireworks"};</script>
1253<script id="trickle-badge">!function () {
1254    fetch("https://app.trickle.so/proto/api/subs/features/5?userId=" + window.__PROTO_STATE__.userId).then((res) => res.json()).then((res) => {
1255        if (res.data && res.data.enable) {
1256            return;
1257        }
1258
1259        const el = document.createElement("div");
1260
1261        el.innerHTML = `<div id="proto-trickle-badge-element" class="fixed duration-300 bg-black bottom-6 hover:shadow-[0_0_15px_rgba(85,85,85,0.3)] right-6 py-2 pl-3 pr-5 rounded-lg border hover:border-gray-700 border-gray-800 hover:-translate-y-0.5 z-[199999]"><a href="https://app.trickle.so" target="_blank" rel="noopener noreferrer" class="flex items-center space-x-2"><span><svg width="20" height="20" viewBox="0 0 288 288" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M217.19 217.196V231.921C217.19 256.318 197.412 276.096 173.014 276.096H55.2133C30.816 276.096 11.038 256.318 11.038 231.921V114.12C11.038 89.7223 30.816 69.9443 55.2133 69.9443H69.9385" fill="white"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M69.9496 55.2193V173.02C69.9496 197.418 89.7275 217.196 114.125 217.196H231.926C256.323 217.196 276.101 197.418 276.101 173.02V55.2193C276.101 30.8219 256.323 11.0439 231.926 11.0439H114.125C89.7275 11.0439 69.9496 30.8219 69.9496 55.2193Z" fill="white"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M114.12 0C83.623 0 58.9005 24.7225 58.9005 55.2192V58.9005H55.2192C24.7225 58.9005 0 83.623 0 114.12V231.921C0 262.418 24.7225 287.14 55.2192 287.14H173.02C203.517 287.14 228.24 262.418 228.24 231.921V228.24H231.921C262.418 228.24 287.14 203.517 287.14 173.02V55.2192C287.14 24.7225 262.418 0 231.921 0H114.12ZM206.152 228.24V231.921C206.152 250.219 191.318 265.052 173.02 265.052H55.2192C36.9212 265.052 22.0877 250.219 22.0877 231.921V114.12C22.0877 95.8217 36.9212 80.9882 55.2192 80.9882H58.9005V173.02C58.9005 203.517 83.623 228.24 114.12 228.24H206.152ZM80.9882 173.02C80.9882 191.318 95.8217 206.152 114.12 206.152H231.921C250.219 206.152 265.052 191.318 265.052 173.02V55.2192C265.052 36.9212 250.219 22.0877 231.921 22.0877H114.12C95.8217 22.0877 80.9882 36.9212 80.9882 55.2192V173.02Z" fill="#1F2937"></path><path d="M125.165 84.6693C125.165 78.57 130.109 73.6255 136.209 73.6255H209.834C215.934 73.6255 220.878 78.57 220.878 84.6693C220.878 90.7687 215.934 95.7132 209.834 95.7132H184.065V158.295C184.065 164.394 179.121 169.339 173.022 169.339C166.922 169.339 161.978 164.394 161.978 158.295V95.7132H136.209C130.109 95.7132 125.165 90.7687 125.165 84.6693Z" fill="#1F2937"></path></svg></span><span class="text-sm text-gray-300 transition-colors">Built with Trickle</span></a><span id="badge-close-button" class="absolute p-1 top-0 right-0 z-10 cursor-pointer hover:bg-gray-800 rounded-full overflow-hidden" onclick="document.body.removeChild(document.querySelector('#proto-trickle-badge-element'))"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" stroke="grey" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg></span></div>`;
1262        document.body.appendChild(el.firstChild);
1263    });
1264}();
1265</script>
1265<script id="trickle-analytics">!function () {
1266    var analytics = window.analytics = window.analytics || [];
1267
1268    if (!analytics.initialize) if (analytics.invoked) window.console && console.error && console.error("Segment snippet included twice."); else {
1269        analytics.invoked = !0; analytics.methods = ["trackSubmit", "trackClick", "trackLink", "trackForm", "pageview", "identify", "reset", "group", "track", "ready", "alias", "debug", "page", "once", "off", "on", "addSourceMiddleware", "addIntegrationMiddleware", "setAnonymousId", "addDestinationMiddleware"]; analytics.factory = function (e) { return function () { var t = Array.prototype.slice.call(arguments); t.unshift(e); analytics.push(t); return analytics } }; for (var e = 0; e < analytics.methods.length; e++) { var key = analytics.methods[e]; analytics[key] = analytics.factory(key) } analytics.load = function (key, e) { var t = document.createElement("script"); t.type = "text/javascript"; t.async = !0; t.src = "https://cdn.segment.com/analytics.js/v1/" + key + "/analytics.min.js"; var n = document.getElementsByTagName("script")[0]; n.parentNode.insertBefore(t, n); analytics._loadOptions = e }; console.log(analytics); analytics._writeKey = "FMFy0tKyTGNTC64dzUT5nA3IrqfP6yhr"; analytics.SNIPPET_VERSION = "4.15.3";
1270        
1271        analytics.load("ryeevPxktAiS6AL1tjATM0s7iGf7piwM", {
1272            integrations: { "Segment.io": { apiHost: "app.trickle.so/sgmt/client", apiKey: "FMFy0tKyTGNTC64dzUT5nA3IrqfP6yhr" }}
1273        });
1274
1275        try {
1276            if (window.parent.location.href !== "https://app.trickle.so/project?id=" + window.__PROTO_STATE__.projectId) {
1277                analytics.page({
1278                    project_id: window.__PROTO_STATE__.projectId,
1279                    user_id: window.__PROTO_STATE__.userId,
1280                    indice: "nge-analytics-user"
1281                });
1282            }
1283        } catch (e) {
1284            analytics.page({
1285                project_id: window.__PROTO_STATE__.projectId,
1286                user_id: window.__PROTO_STATE__.userId,
1287                indice: "nge-analytics-user"
1288            });
1289        }
1290    }
1291}();</script>
1291<script id="trickle-report-error">function reportError (error) {
1292	if (window.parent) {
1293		window.parent.postMessage({
1294            type: "error",
1295            data: error.stack || error.message || ''
1296        }, '*');
1297	}
1298}</script>
1298</head>
1299<body>
1299<script src="https://app.trickle.so/storage/trickle.js"></script>
1299
1300    <div id="root"></div>
1301
1302    <!-- Utils -->
1303    
1303<script>function isDebugMode(){try{const currentUrl=window.location.href;console.log("Current URL:",currentUrl);const isPreviewUrl=/https:\/\/preview__[^.]+\.trickle\.host/.test(currentUrl);console.log("Is preview URL:",isPreviewUrl);return isPreviewUrl}catch(error){reportError(error);console.error("Error checking debug mode:",error);return false}}</script>
1303
1304    
1304<script>function getRandomColor(){try{const colors=["#ff0000","#00ff00","#0000ff","#ffff00","#ff00ff","#00ffff","#ff8800","#ff0088","#88ff00","#0088ff","#ff7777","#77ff77","#ffffff","#ff3333","#33ff33","#3333ff","#ffaa00","#00ffaa","#aa00ff","#ffff33","#33ffff","#ff33ff","#ffccaa","#aaffcc"];return colors[Math.floor(Math.random()*colors.length)]}catch(error){reportError(error);return"#ffffff"}}function createGradientColors(baseColor){try{const rgb=parseInt(baseColor.slice(1),16);const r=rgb>>16&255;const g=rgb>>8&255;const b=rgb&255;return{start:`rgba(${r}, ${g}, ${b}, 1)`,mid:`rgba(${r}, ${g}, ${b}, 0.5)`,end:`rgba(${r}, ${g}, ${b}, 0}`}}catch(error){reportError(error);return{start:baseColor,mid:baseColor,end:"transparent"}}}function createColorVariation(baseColor,variance=40){try{const rgb=parseInt(baseColor.slice(1),16);const r=rgb>>16&255;const g=rgb>>8&255;const b=rgb&255;const varianceFactor=0.7;const dr=Math.random()*variance-variance/2;const dg=Math.random()*variance-variance/2;const db=Math.random()*variance-variance/2;const newR=Math.min(255,Math.max(0,r+dr*varianceFactor));const newG=Math.min(255,Math.max(0,g+dg*varianceFactor));const newB=Math.min(255,Math.max(0,b+db*varianceFactor));return`#${Math.round(newR).toString(16).padStart(2,"0")}${Math.round(newG).toString(16).padStart(2,"0")}${Math.round(newB).toString(16).padStart(2,"0")}`}catch(error){reportError(error);return baseColor}}</script>
1304
1305    
1305<script>function _extends(){_extends=Object.assign?Object.assign.bind():function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key]}}}return target};return _extends.apply(this,arguments)}</script>
1305
1306    
1307    <!-- Config -->
1308    
1308<script>const particleConfig={base:{decay:0.015,gravity:0.2,size:2,trailLength:120,trailDecay:0.98,speedDecay:0.985,angleChange:0.002},super:{decay:0.002,gravity:0.3,size:0.5,trailLength:200,trailDecay:0.996,speedDecay:0.98,angleChange:0.002,glowSize:1.2,speedMultiplier:2},flicker:{decay:0.02,gravity:0.2,size:2,trailLength:120,trailDecay:0.98,speedDecay:0.985,angleChange:0.002,flickerChance:0.5,flickerRange:0.05},comet:{decay:0.02,gravity:0.15,size:2,trailLength:30,trailDecay:0.85,speedDecay:0.99,angleChange:0.001,maxHistoryPoints:5},glitter:{decay:0.015,gravity:0.36,size:2,trailLength:120,trailDecay:0.98,speedDecay:0.985,angleChange:0.002,fadeStartTime:30},sparkle:{decay:0,gravity:0,size:1,speedDecay:1,angleChange:0,flashDuration:3,flashInterval:2,maxFlashes:5}};</script>
1308
1309    
1309<script>const rocketConfig={base:{speed:15,size:2,trailLength:10,lineWidth:2,glowSize:0},large:{speed:8,size:8,trailLength:20,lineWidth:4,glowSize:6},super:{speed:5,size:4,trailLength:15,lineWidth:3,glowSize:6,sparkle:{count:5,maxPoints:100,speed:{min:0.5,max:1.5},size:{min:0.3,max:0.6},alpha:{min:0.6,max:1},gravity:{min:0.01,max:0.02},color:{base:[255,200,0],variance:55},lifeDecay:0.95,angleSpread:Math.PI}},secondary:{speed:12,size:2,trailLength:8,lineWidth:2,glowSize:4}};</script>
1309
1310    
1310<script>const sparkleConfig={particleCount:{base:250,screenMultiplier:1},radius:{min:160,max:320,screenMultiplier:1,distribution:{type:"exponential",power:2},spread:{speed:0.15,maxDistance:40}},centerFlash:{size:20,duration:5,passes:3,fadeOut:0.8,gradient:{inner:0.2,outer:1}},delay:{min:0,max:24},flash:{count:{min:4,max:6},duration:{min:8,max:12},interval:{min:6,max:10},startTime:{min:0,max:45}},glow:{core:[{size:0.25,passes:6},{size:0.5,passes:4},{size:1,passes:3}],outer:[{size:2,passes:2},{size:4,passes:1}],boost:[{size:0.125,passes:8}],blendModes:["lighter","lighter","lighter","lighter","lighter"]}};</script>
1310
1311
1312    <!-- Sound System -->
1313    
1313<script>function createWebAudioManager(){try{let audioContext=null;let soundBuffer=null;let largeBuffer=null;let isMuted=false;let lastPlayTime=0;let initializedCount=0;const MAX_SOURCES=20;const sourcePool=[];const gainPool=[];let currentSourceIndex=0;let silentSource=null;let silentGain=null;const keepContextActive=()=>{try{if(!audioContext||!silentSource)return;const silentBuffer=audioContext.createBuffer(1,2,44100);const channelData=silentBuffer.getChannelData(0);channelData[0]=1e-7;channelData[1]=1e-7;silentSource=audioContext.createBufferSource();silentSource.buffer=silentBuffer;silentSource.loop=true;if(!silentGain){silentGain=audioContext.createGain();silentGain.gain.value=1e-7;silentGain.connect(audioContext.destination)}silentSource.connect(silentGain);silentSource.start();setTimeout(()=>{if(silentSource){silentSource.stop();silentSource.disconnect()}keepContextActive()},10000)}catch(error){reportError(error)}};const initialize=async()=>{try{if(audioContext)return;audioContext=new(window.AudioContext||window.webkitAudioContext)({latencyHint:"interactive",sampleRate:44100});const[normalResponse,largeResponse]=await Promise.all([fetch("https://raw.githubusercontent.com/chocoford/chocoford/main/public/firework-snare.wav"),fetch("https://raw.githubusercontent.com/chocoford/chocoford/main/public/firework-explosion.wav")]);const[normalArrayBuffer,largeArrayBuffer]=await Promise.all([normalResponse.arrayBuffer(),largeResponse.arrayBuffer()]);soundBuffer=await audioContext.decodeAudioData(normalArrayBuffer);largeBuffer=await audioContext.decodeAudioData(largeArrayBuffer);for(let i=0;i<MAX_SOURCES;i++){const source=audioContext.createBufferSource();const gain=audioContext.createGain();gain.gain.value=0.7;gain.connect(audioContext.destination);sourcePool.push(source);gainPool.push(gain)}keepContextActive();initializedCount=2}catch(error){reportError(error)}};const getNextSource=()=>{try{currentSourceIndex=(currentSourceIndex+1)%MAX_SOURCES;const source=audioContext.createBufferSource();const gain=gainPool[currentSourceIndex];sourcePool[currentSourceIndex]=source;return{source,gain}}catch(error){reportError(error);return null}};const playSound=(isLarge=false)=>{try{const buffer=isLarge?largeBuffer:soundBuffer;if(!audioContext||!buffer||isMuted){return}const now=Date.now();if(now-lastPlayTime<16){return}lastPlayTime=now;if(audioContext.state==="suspended"){audioContext.resume()}const nodes=getNextSource();if(!nodes){return}const{source,gain}=nodes;source.buffer=buffer;source.connect(gain);const startDelay=0.001;const exactStartTime=audioContext.currentTime+startDelay;source.start(exactStartTime)}catch(error){reportError(error)}};const setMuted=muted=>{try{isMuted=muted;gainPool.forEach(gain=>{gain.gain.value=muted?0:0.7});if(silentGain){silentGain.gain.value=muted?0:1e-7}}catch(error){reportError(error)}};const getMuted=()=>isMuted;const getInitializedCount=()=>initializedCount;const initOnInteraction=()=>{try{initialize();document.removeEventListener("click",initOnInteraction);document.removeEventListener("touchstart",initOnInteraction);document.removeEventListener("keydown",initOnInteraction)}catch(error){reportError(error)}};document.addEventListener("click",initOnInteraction);document.addEventListener("touchstart",initOnInteraction);document.addEventListener("keydown",initOnInteraction);return{initialize,playSound,setMuted,getMuted,getInitializedCount}}catch(error){reportError(error);return{initialize:()=>{},playSound:()=>{},setMuted:()=>{},getMuted:()=>false,getInitializedCount:()=>0}}}</script>
1313
1314    
1314<script>function createAudioPoolManager(){try{let isMuted=false;let lastPlayTime=0;const POOL_SIZE=20;const audioPool=Array.from({length:POOL_SIZE},()=>{const audio=new Audio("https://raw.githubusercontent.com/chocoford/chocoford/main/public/firework-snare.wav");audio.preload="auto";audio.volume=0.7;return audio});let currentAudioIndex=0;let initializedCount=0;const silentAudio=new Audio("data:audio/mp3;base64,//MkxAAHiAICWABElBeKPL/RANb2w+yiT1g/gTok//lP/W/l3h8QO/OCdCqCW2Cw//MkxAQHkAIWUAhEmAQXWUOFW2dxPu//9mr60ElY5sseQ+xxesmHKtZr7bsqqX2L//MkxAgFwAYiQAhEAC2hq22d3///9FTV6tA36JdgBJoOGgc+7qvqej5Zu7/7uI9l//MkxBQHAAYi8AhEAO193vt9KGOq+6qcT7hhfN5FTInmwk8RkqKImTM55pRQHQSq//MkxBsGkgoIAABHhTACIJLf99nVI///yuW1uBqWfEu7CgNPWGpUadBmZ////4sL//MkxCMHMAH9iABEmAsKioqKigsLCwtVTEFNRTMuOTkuNVVVVVVVVVVVVVVVVVVV//MkxCkECAUYCAAAAFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV");silentAudio.loop=true;silentAudio.volume=0.001;let isSilentPlaying=false;const initialize=async()=>{try{const batchSize=5;for(let i=0;i<POOL_SIZE;i+=batchSize){const batch=audioPool.slice(i,Math.min(i+batchSize,POOL_SIZE));await Promise.all(batch.map(audio=>{return new Promise(resolve=>{audio.addEventListener("canplaythrough",()=>{initializedCount++;resolve()},{once:true})})}))}}catch(error){reportError(error)}};const ensureSilentAudio=()=>{try{if(!isSilentPlaying&&!isMuted){silentAudio.play().catch(error=>{reportError(error)});isSilentPlaying=true}}catch(error){reportError(error)}};const playSound=()=>{try{if(isMuted)return;const now=Date.now();if(now-lastPlayTime<16)return;lastPlayTime=now;ensureSilentAudio();let audio=audioPool[currentAudioIndex];let attempts=0;while(attempts<POOL_SIZE&&audio.currentTime>0&&!audio.paused){currentAudioIndex=(currentAudioIndex+1)%POOL_SIZE;audio=audioPool[currentAudioIndex];attempts++}if(audio.currentTime>0&&!audio.paused){const newAudio=new Audio(audio.src);newAudio.volume=0.7;audioPool[currentAudioIndex]=newAudio;newAudio.play().catch(error=>{reportError(error)})}else{audio.currentTime=0;audio.play().catch(error=>{reportError(error)})}currentAudioIndex=(currentAudioIndex+1)%POOL_SIZE}catch(error){reportError(error)}};const setMuted=muted=>{try{isMuted=muted;audioPool.forEach(audio=>{audio.volume=muted?0:0.7});silentAudio.volume=muted?0:0.001;if(muted&&isSilentPlaying){silentAudio.pause();isSilentPlaying=false}else if(!muted&&!isSilentPlaying){silentAudio.play().catch(error=>{reportError(error)});isSilentPlaying=true}}catch(error){reportError(error)}};const getMuted=()=>isMuted;const getInitializedCount=()=>initializedCount;const initOnInteraction=()=>{try{initialize();document.removeEventListener("click",initOnInteraction);document.removeEventListener("touchstart",initOnInteraction);document.removeEventListener("keydown",initOnInteraction)}catch(error){reportError(error)}};document.addEventListener("click",initOnInteraction);document.addEventListener("touchstart",initOnInteraction);document.addEventListener("keydown",initOnInteraction);return{initialize,playSound,setMuted,getMuted,getInitializedCount}}catch(error){reportError(error);return{initialize:()=>{},playSound:()=>{},setMuted:()=>{},getMuted:()=>false,getInitializedCount:()=>0}}}</script>
1314
1315    
1315<script>const soundManager=createWebAudioManager();</script>
1315
1316
1317    <!-- Firework System -->
1318    
1318<script>function createFirework(x,y,color,screenMultiplier,specialType=null){try{let particles=[];if(specialType==="super"){return createSuperFirework(x,y,color,screenMultiplier)}else if(specialType==="large"||specialType==="extra-large"){return createLargeFirework(x,y,color,screenMultiplier,specialType==="extra-large")}else if(specialType==="comet"){return createCometFirework(x,y,color,screenMultiplier)}else if(specialType==="glitter"){return createGlitterFirework(x,y,color,screenMultiplier)}else if(specialType==="sparkle"){return createSparkleFirework(x,y,color,screenMultiplier)}const particleCount=Math.floor(40*screenMultiplier);for(let i=0;i<particleCount;i++){const angle=Math.PI*2*i/particleCount;const speed=(2+Math.random()*2)*Math.sqrt(screenMultiplier);const particle=createParticle(x,y,angle,speed,color);if(particle)particles.push(particle)}if(Math.random()<0.4){const secondaryColor=createColorVariation(color);const secondaryCount=Math.floor(30*screenMultiplier);for(let i=0;i<secondaryCount;i++){const angle=Math.PI*2*i/secondaryCount+(Math.random()-0.5)*1;const speed=(2+Math.random()*3)*Math.sqrt(screenMultiplier);const particle=createParticle(x,y,angle,speed,secondaryColor,"flicker");if(particle)particles.push(particle)}}return particles}catch(error){reportError(error);return[]}}</script>
1318
1319    
1319<script>function createCelebrationManager(){let isCelebrating=false;let celebrationStartTime=0;const CELEBRATION_DURATION=15000;const MIN_INTERVAL=50;const MAX_INTERVAL=150;const SPECIAL_FIREWORK_CHANCE=0.01;const startCelebration=()=>{try{isCelebrating=true;celebrationStartTime=Date.now()}catch(error){reportError(error)}};const shouldLaunchFirework=()=>{try{if(!isCelebrating)return false;const currentTime=Date.now();const elapsedTime=currentTime-celebrationStartTime;if(elapsedTime>CELEBRATION_DURATION){isCelebrating=false;return false}const progress=elapsedTime/CELEBRATION_DURATION;const intensity=Math.sin(progress*Math.PI);const interval=MIN_INTERVAL+(1-intensity)*(MAX_INTERVAL-MIN_INTERVAL);return Math.random()*interval<MIN_INTERVAL}catch(error){reportError(error);return false}};const getRandomFireworkType=()=>{try{if(Math.random()<SPECIAL_FIREWORK_CHANCE){const specialTypes=["extra-large","super","glitter","sparkle"];return specialTypes[Math.floor(Math.random()*specialTypes.length)]}return null}catch(error){reportError(error);return null}};const getRandomPosition=(width,height)=>{try{return{x:Math.random()*width,y:Math.random()*(height*0.6)}}catch(error){reportError(error);return{x:width/2,y:height/3}}};const isCelebrationActive=()=>isCelebrating;return{startCelebration,shouldLaunchFirework,getRandomFireworkType,getRandomPosition,isCelebrationActive}}const celebrationManager=createCelebrationManager();</script>
1319
1320    
1320<script>function createRocket(startX,startY,endX,endY,fromExplosion=false){try{const angle=Math.atan2(endY-startY,endX-startX);return{x:startX,y:startY,startX,startY,targetX:endX,targetY:endY,distanceTraveled:0,totalDistance:Math.hypot(endX-startX,endY-startY),angle,speed:fromExplosion?rocketConfig.secondary.speed:rocketConfig.base.speed,size:rocketConfig.base.size,color:getRandomColor(),specialType:null,sparklePoints:[],lastSparkleTime:0,cleanup(){this.sparklePoints=[]},update(){try{const config=this.specialType?rocketConfig[this.specialType]||rocketConfig.base:rocketConfig.base;this.speed=config.speed;this.size=config.size;const wiggle=Math.sin(this.distanceTraveled*0.1)*0.5;this.x+=Math.cos(this.angle)*this.speed+wiggle;this.y+=Math.sin(this.angle)*this.speed;this.distanceTraveled=Math.hypot(this.x-this.startX,this.y-this.startY);if(this.specialType==="super"){const sparkConfig=config.sparkle;while(this.sparklePoints.length>sparkConfig.maxPoints){this.sparklePoints.shift()}for(let i=0;i<sparkConfig.count;i++){const sparkAngle=this.angle+Math.PI+(Math.random()-0.5)*sparkConfig.angleSpread;const sparkSpeed=sparkConfig.speed.min+Math.random()*(sparkConfig.speed.max-sparkConfig.speed.min);const r=sparkConfig.color.base[0];const g=sparkConfig.color.base[1]+Math.random()*sparkConfig.color.variance;const b=sparkConfig.color.base[2];this.sparklePoints.push({x:this.x,y:this.y,vx:Math.cos(sparkAngle)*sparkSpeed,vy:Math.sin(sparkAngle)*sparkSpeed,alpha:sparkConfig.alpha.min+Math.random()*(sparkConfig.alpha.max-sparkConfig.alpha.min),size:sparkConfig.size.min+Math.random()*(sparkConfig.size.max-sparkConfig.size.min),color:`rgba(${r}, ${g}, ${b}, 1)`,life:1,gravity:sparkConfig.gravity.min+Math.random()*(sparkConfig.gravity.max-sparkConfig.gravity.min)})}this.sparklePoints=this.sparklePoints.filter(point=>{point.x+=point.vx;point.y+=point.vy;point.vy+=point.gravity;point.life*=sparkConfig.lifeDecay;point.alpha=point.life;return point.life>0.1})}return this.distanceTraveled<this.totalDistance}catch(error){reportError(error);throw error}}}}catch(error){reportError(error);throw error}}</script>
1320
1321    
1321<script>function createBurstFireworks(baseX,baseY,activeTouches,disabledUntilRef){try{const currentTime=Date.now();if(currentTime<disabledUntilRef.current){console.log("Burst fireworks cooling down...",{currentTime,disabledUntil:disabledUntilRef.current,remaining:disabledUntilRef.current-currentTime});return false}const BURST_RADIUS=100;const BURST_COUNT=6;const COOLDOWN_TIME=5000;const endTime=currentTime+COOLDOWN_TIME;disabledUntilRef.current=endTime;window.dispatchEvent(new CustomEvent("burstFireworkStart",{detail:{endTime,startTime:currentTime,duration:COOLDOWN_TIME}}));console.log("Starting burst fireworks with cooldown:",{startTime:currentTime,endTime:endTime,duration:COOLDOWN_TIME});for(let i=0;i<BURST_COUNT;i++){const angle=Math.random()*Math.PI*2;const distance=Math.random()*BURST_RADIUS;const offsetX=Math.cos(angle)*distance;const offsetY=Math.sin(angle)*distance;const randomType=getRandomSpecialType();const fireworkId=`burst_${currentTime}_${i}`;activeTouches.current.set(fireworkId,{x:baseX+offsetX,y:baseY+offsetY,identifier:fireworkId,specialType:randomType});requestAnimationFrame(()=>{activeTouches.current.delete(fireworkId)})}return true}catch(error){reportError(error);throw error}}</script>
1321
1322    
1322<script>function createClassicLargeFirework(x,y,baseColor,screenMultiplier,isExtraLarge=false){try{const particles=[];const isMobileScreen=window.innerWidth<=768;if(isMobileScreen){if(isExtraLarge){screenMultiplier*=0.6}else{screenMultiplier*=0.7}}const layers=isExtraLarge?6:4;const sizeMultiplier=isExtraLarge?1.5:1;const baseParticleCount=Math.floor(60*screenMultiplier*sizeMultiplier);const colors=[baseColor,createColorVariation(baseColor,15),createColorVariation(baseColor,15),getRandomColor(),getRandomColor(),createColorVariation(getRandomColor(),15)];for(let layer=0;layer<layers;layer++){const layerParticleCount=Math.floor(baseParticleCount*(1-layer*0.15));const baseSpeed=(4+Math.random()*4)*Math.sqrt(screenMultiplier)*(isExtraLarge?1.3:1);const layerSpeed=baseSpeed*(1+layer*0.4);for(let i=0;i<layerParticleCount;i++){const angle=Math.PI*2*i/layerParticleCount+(Math.random()-0.5)*0.4+layer*Math.PI/6;const color=colors[Math.floor(Math.random()*colors.length)];const particle=createParticle(x,y,angle,layerSpeed*(0.85+Math.random()*0.5),color,"classic");if(particle){particle.size=(2.25+Math.random()*1.5)*(isExtraLarge?1.5:1)*(isMobileScreen?0.8:1);particle.decay=isExtraLarge?0.004+Math.random()*0.002:0.008+Math.random()*0.004;particle.gravity=0.05;particles.push(particle)}}}const sparkCount=Math.floor(75*screenMultiplier*(isExtraLarge?1.5:1));for(let i=0;i<sparkCount;i++){const angle=Math.random()*Math.PI*2;const speed=(1.5+Math.random()*2.25)*Math.sqrt(screenMultiplier)*(isExtraLarge?1.3:1);const sparkColor=colors[Math.floor(Math.random()*colors.length)];const particle=createParticle(x,y,angle,speed,sparkColor,"flicker");if(particle){particle.size=(1.125+Math.random())*(isExtraLarge?1.5:1)*(isMobileScreen?0.8:1);particle.decay=isExtraLarge?0.008+Math.random()*0.004:0.015+Math.random()*0.008;particles.push(particle)}}if(isExtraLarge){for(let i=0;i<3;i++){const angle=Math.random()*Math.PI*2;const distance=100+Math.random()*150;const subX=x+Math.cos(angle)*distance;const subY=y+Math.sin(angle)*distance;const delay=15+Math.floor(Math.random()*15);const rocket=createRocket(x,y,subX,subY,true);if(rocket){rocket.delay=delay;rocket.color=createColorVariation(baseColor,15);rocket.isSecondary=true;particles.push(rocket)}}}return particles}catch(error){reportError(error);return[]}}</script>
1322
1323    
1323<script>function createCometFirework(x,y,baseColor,screenMultiplier){try{const particles=[];const particlesToCleanup=new Set;const screenBounds={left:-50,right:window.innerWidth+50,top:-50,bottom:window.innerHeight+50};const colors=[baseColor,createColorVariation(baseColor)];const cometCount=Math.floor(24*screenMultiplier);const trailsPerComet=2;for(let i=0;i<cometCount;i++){const angle=Math.PI*2*i/cometCount+(Math.random()-0.5)*0.2;const speed=(3+Math.random()*2)*Math.sqrt(screenMultiplier);const color=colors[Math.floor(Math.random()*colors.length)];const comet=createParticle(x,y,angle,speed,color,"comet");if(comet){comet.size=2+Math.random();comet.decay=0.02+Math.random()*0.01;comet.gravity=0.15;comet.trailLength=0;const originalUpdate=comet.update;comet.update=function(){const result=originalUpdate.call(this);if(!result)return false;if(this.x<screenBounds.left||this.x>screenBounds.right||this.y<screenBounds.top||this.y>screenBounds.bottom){particlesToCleanup.add(this);return false}return true};particles.push(comet);if(Math.random()<0.7){for(let j=0;j<trailsPerComet;j++){const trailAngle=angle+(Math.random()-0.5)*0.3;const trailSpeed=speed*(0.4+Math.random()*0.3);const trail=createParticle(x,y,trailAngle,trailSpeed,color,"trail");if(trail){trail.size=0.5+Math.random()*0.5;trail.decay=0.03+Math.random()*0.02;trail.gravity=0.05;const originalTrailUpdate=trail.update;trail.update=function(){const result=originalTrailUpdate.call(this);if(!result)return false;if(this.x<screenBounds.left||this.x>screenBounds.right||this.y<screenBounds.top||this.y>screenBounds.bottom){particlesToCleanup.add(this);return false}return true};particles.push(trail)}}}}}particles.cleanup=()=>{particlesToCleanup.forEach(particle=>{if(particle&&typeof particle.cleanup==="function"){particle.cleanup()}});particlesToCleanup.clear()};return particles}catch(error){reportError(error);throw error}}</script>
1323
1324    
1324<script>function createGlitterFirework(x,y,baseColor,screenMultiplier){try{const particles=[];const particlesToCleanup=new Set;const screenBounds={left:-50,right:window.innerWidth+50,top:-50,bottom:window.innerHeight+50};const colors=[baseColor,createColorVariation(baseColor),"#FFFFFF","#FFFFAA"];const particleCount=Math.floor(400*screenMultiplier);for(let i=0;i<particleCount;i++){const angle=Math.PI*2*i/particleCount;const baseSpeed=(2.5+Math.random()*1.5)*Math.sqrt(screenMultiplier);for(let j=0;j<8;j++){const sparkleAngle=angle+(Math.random()-0.5)*0.8;const sparkleSpeed=baseSpeed*(0.8+Math.random()*0.4);const sparkle=createParticle(x,y,sparkleAngle,sparkleSpeed,"#FFFFFF","sparkle");if(sparkle){sparkle.size=1+Math.random();sparkle.decay=0.01+Math.random()*0.01;sparkle.gravity=0.15;sparkle.alpha=0.3+Math.random()*0.3;sparkle.delay=Math.floor(Math.random()*10);const originalUpdate=sparkle.update;sparkle.update=function(){const result=originalUpdate.call(this);if(!result)return false;if(this.x<screenBounds.left||this.x>screenBounds.right||this.y<screenBounds.top||this.y>screenBounds.bottom){particlesToCleanup.add(this);return false}return true};particles.push(sparkle)}}}const centerParticleCount=Math.floor(75*screenMultiplier);for(let i=0;i<centerParticleCount;i++){const angle=Math.PI*2*i/centerParticleCount;const speed=(2+Math.random()*1.5)*Math.sqrt(screenMultiplier);const color=colors[Math.floor(Math.random()*colors.length)];const mainParticle=createParticle(x,y,angle,speed,color,"glitter");if(mainParticle){mainParticle.size=2+Math.random();mainParticle.decay=0.012;mainParticle.gravity=0.36;mainParticle.glitterDelay=5+Math.floor(Math.random()*10);mainParticle.fadeStartTime=45+Math.floor(Math.random()*20);mainParticle.minFallDistance=50+Math.random()*30;const originalUpdate=mainParticle.update;mainParticle.update=function(){const result=originalUpdate.call(this);if(!result)return false;if(this.x<screenBounds.left||this.x>screenBounds.right||this.y<screenBounds.top||this.y>screenBounds.bottom){particlesToCleanup.add(this);return false}return true};particles.push(mainParticle);if(Math.random()<0.5){for(let j=0;j<2;j++){const glitterSpeed=speed*(0.95+Math.random()*0.1);const glitterAngle=angle+(Math.random()-0.5)*0.2;const glitter=createParticle(x,y,glitterAngle,glitterSpeed,"#FFFFFF","glitter");if(glitter){glitter.size=1.5+Math.random();glitter.decay=0.015+Math.random()*0.008;glitter.gravity=0.36;glitter.alpha=0.7+Math.random()*0.3;glitter.glitterDelay=Math.floor(Math.random()*15);glitter.fadeStartTime=35+Math.floor(Math.random()*15);glitter.minFallDistance=40+Math.random()*20;const originalGlitterUpdate=glitter.update;glitter.update=function(){const result=originalGlitterUpdate.call(this);if(!result)return false;if(this.x<screenBounds.left||this.x>screenBounds.right||this.y<screenBounds.top||this.y>screenBounds.bottom){particlesToCleanup.add(this);return false}return true};particles.push(glitter)}}}}}particles.cleanup=()=>{particlesToCleanup.forEach(particle=>{if(particle&&typeof particle.cleanup==="function"){particle.cleanup()}});particlesToCleanup.clear()};return particles}catch(error){reportError(error);throw error}}</script>
1324
1325    
1325<script>function createLargeFirework(x,y,baseColor,screenMultiplier,isExtraLarge=false){try{const particles=[];const isMobileScreen=window.innerWidth<=768;if(isMobileScreen){if(isExtraLarge){screenMultiplier*=0.6}else{screenMultiplier*=0.7}}const layers=isExtraLarge?6:4;const sizeMultiplier=isExtraLarge?1.5:1;const baseParticleCount=Math.floor(60*screenMultiplier*sizeMultiplier);const colors=[baseColor,createColorVariation(baseColor),createColorVariation(baseColor),getRandomColor(),getRandomColor(),createColorVariation(getRandomColor())];for(let layer=0;layer<layers;layer++){const layerParticleCount=Math.floor(baseParticleCount*(1-layer*0.15));const baseSpeed=(4+Math.random()*4)*Math.sqrt(screenMultiplier)*(isExtraLarge?1.3:1);const layerSpeed=baseSpeed*(1+layer*0.4);for(let i=0;i<layerParticleCount;i++){const angle=Math.PI*2*i/layerParticleCount+(Math.random()-0.5)*0.4+layer*Math.PI/6;const color=colors[Math.floor(Math.random()*colors.length)];const particle=createParticle(x,y,angle,layerSpeed*(0.85+Math.random()*0.5),color,"classic");if(particle){particle.size=(2.25+Math.random()*1.5)*(isExtraLarge?1.5:1)*(isMobileScreen?0.8:1);particle.decay=isExtraLarge?0.004+Math.random()*0.002:0.008+Math.random()*0.004;particle.gravity=0.05;particles.push(particle)}}}const sparkCount=Math.floor(75*screenMultiplier*(isExtraLarge?1.5:1));for(let i=0;i<sparkCount;i++){const angle=Math.random()*Math.PI*2;const speed=(1.5+Math.random()*2.25)*Math.sqrt(screenMultiplier)*(isExtraLarge?1.3:1);const sparkColor=colors[Math.floor(Math.random()*colors.length)];const particle=createParticle(x,y,angle,speed,sparkColor,"flicker");if(particle){particle.size=(1.125+Math.random())*(isExtraLarge?1.5:1)*(isMobileScreen?0.8:1);particle.decay=isExtraLarge?0.01+Math.random()*0.005:0.02+Math.random()*0.01;particles.push(particle)}}if(isExtraLarge){for(let i=0;i<3;i++){const angle=Math.random()*Math.PI*2;const distance=50+Math.random()*100;const subX=x+Math.cos(angle)*distance;const subY=y+Math.sin(angle)*distance;const delay=10+Math.floor(Math.random()*20);const rocket=createRocket(x,y,subX,subY,true);if(rocket){rocket.delay=delay;rocket.color=createColorVariation(baseColor);rocket.isSecondary=true;particles.push(rocket)}}}return particles}catch(error){reportError(error);return[]}}</script>
1325
1326    
1326<script>function createSparkleFirework(x,y,baseColor,screenMultiplier){try{const particles=[];const particlesToCleanup=new Set;const randomFactor=Math.pow(Math.random(),sparkleConfig.radius.distribution.power);const radiusRange=sparkleConfig.radius.max-sparkleConfig.radius.min;const radius=(sparkleConfig.radius.min+radiusRange*randomFactor)*screenMultiplier;const centerFlash=createParticle(x,y,0,0,baseColor,"sparkle");if(centerFlash){centerFlash.isCenter=true;centerFlash.size=3+Math.random()*3;centerFlash.flashLife=5;centerFlash.currentLife=0;centerFlash.alpha=1;centerFlash.update=function(){if(this.currentLife>=this.flashLife){particlesToCleanup.add(this);return false}this.currentLife++;this.alpha=Math.pow(0.8,this.currentLife);return true};centerFlash.cleanup=function(){delete this.trailHistory;delete this.sparklePoints};particles.push(centerFlash)}const radiusRatio=radius/(sparkleConfig.radius.min*screenMultiplier);const sparkleCount=Math.floor(sparkleConfig.particleCount.base*radiusRatio*screenMultiplier);const baseDelay=2;for(let i=0;i<sparkleCount;i++){const angle=Math.random()*Math.PI*2;const distance=Math.pow(Math.random(),0.3)*radius;const sparkleX=x+Math.cos(angle)*distance;const sparkleY=y+Math.sin(angle)*distance;const sparkle=createParticle(sparkleX,sparkleY,0,0,baseColor,"sparkle");if(sparkle){sparkle.isCenter=false;sparkle.startDelay=baseDelay+Math.floor(Math.random()*sparkleConfig.flash.startTime.max);sparkle.delay=Math.floor(sparkleConfig.delay.min+Math.random()*(sparkleConfig.delay.max-sparkleConfig.delay.min));sparkle.flashCount=0;sparkle.maxFlashes=Math.floor(sparkleConfig.flash.count.min+Math.random()*(sparkleConfig.flash.count.max-sparkleConfig.flash.count.min));sparkle.flashDuration=sparkleConfig.flash.duration.min;sparkle.flashInterval=sparkleConfig.flash.interval.min;sparkle.currentFlashTime=0;sparkle.initialX=sparkleX;sparkle.initialY=sparkleY;sparkle.spreadAngle=angle;sparkle.spreadDistance=0;sparkle.maxSpreadDistance=sparkleConfig.radius.spread.maxDistance*(0.3+Math.random()*0.7);sparkle.update=function(){if(this.startDelay>0){this.startDelay--;this.alpha=0;return true}if(this.delay>0){this.delay--;this.alpha=0;return true}if(this.flashCount>=this.maxFlashes){particlesToCleanup.add(this);return false}this.spreadDistance=Math.min(this.maxSpreadDistance,this.spreadDistance+sparkleConfig.radius.spread.speed);this.x=this.initialX+Math.cos(this.spreadAngle)*this.spreadDistance;this.y=this.initialY+Math.sin(this.spreadAngle)*this.spreadDistance;this.currentFlashTime++;if(this.currentFlashTime<=this.flashDuration){const flashProgress=this.currentFlashTime/this.flashDuration;const brightness=Math.sin(flashProgress*Math.PI);this.alpha=brightness>=0.5?1:0}else if(this.currentFlashTime>=this.flashDuration+this.flashInterval){this.currentFlashTime=0;this.flashCount++;this.flashDuration=sparkleConfig.flash.duration.min+Math.floor(Math.random()*(sparkleConfig.flash.duration.max-sparkleConfig.flash.duration.min));this.flashInterval=sparkleConfig.flash.interval.min+Math.floor(Math.random()*(sparkleConfig.flash.interval.max-sparkleConfig.flash.interval.min))}else{this.alpha=0}return true};sparkle.cleanup=function(){delete this.previousPositions;delete this.sparklePoints};particles.push(sparkle)}}particles.cleanup=()=>{particlesToCleanup.forEach(particle=>{if(particle&&typeof particle.cleanup==="function"){particle.cleanup()}});particlesToCleanup.clear()};return particles}catch(error){reportError(error);throw error}}</script>
1326
1327    
1327<script>function createSuperFirework(x,y,baseColor,screenMultiplier,explosionCount=1){try{const particles=[];const isMobileScreen=window.innerWidth<=768;if(isMobileScreen){screenMultiplier*=1.5}const sizeMultiplier=1-(explosionCount-1)*0.3;const baseCount=Math.floor(48*screenMultiplier*sizeMultiplier);const angleStep=Math.PI*2/baseCount;for(let i=0;i<baseCount;i++){const angle=angleStep*i+(Math.random()-0.5)*0.1;const speed=(2+Math.random())*Math.sqrt(screenMultiplier)*particleConfig.super.speedMultiplier*sizeMultiplier*(isMobileScreen?0.7:1);const mainParticle=createParticle(x,y,angle,speed,baseColor,"super");if(mainParticle){mainParticle.size=(isMobileScreen?0.6:1)*sizeMultiplier;mainParticle.delay=(explosionCount-1)*200;mainParticle.lifeSpanMultiplier=1/explosionCount;particles.push(mainParticle)}if(Math.random()<(isMobileScreen?0.4:0.3)){const subAngle=angle+(Math.random()-0.5)*0.1;const subSpeed=speed*0.8;const subParticle=createParticle(x,y,subAngle,subSpeed,baseColor,"super");if(subParticle){subParticle.size=(isMobileScreen?0.45:0.7)*sizeMultiplier;subParticle.delay=(explosionCount-1)*200;subParticle.lifeSpanMultiplier=1/explosionCount;particles.push(subParticle)}}}const glitterCount=Math.floor(baseCount/(isMobileScreen?2.5:3));for(let i=0;i<glitterCount;i++){const angle=Math.random()*Math.PI*2;const speed=(1.5+Math.random())*Math.sqrt(screenMultiplier)*particleConfig.super.speedMultiplier*sizeMultiplier*(isMobileScreen?0.7:1);const glitterParticle=createParticle(x,y,angle,speed,"#FFFFFF","super");if(glitterParticle){glitterParticle.size=(isMobileScreen?0.3:0.5)*sizeMultiplier;glitterParticle.delay=(explosionCount-1)*200;glitterParticle.lifeSpanMultiplier=1/explosionCount;particles.push(glitterParticle)}}return particles}catch(error){reportError(error);return[]}}</script>
1327
1328
1329    <!-- Renderers -->
1330    
1330<script>function createCanvasRenderer(ctx){try{const renderRocket=firework=>{try{const config=firework.specialType?rocketConfig[firework.specialType]||rocketConfig.base:rocketConfig.base;if(firework.specialType==="super"&&firework.sparklePoints){ctx.save();ctx.globalCompositeOperation="lighter";firework.sparklePoints.forEach(point=>{ctx.beginPath();ctx.fillStyle=point.color.replace("1)",`${point.alpha})`);ctx.arc(point.x,point.y,point.size,0,Math.PI*2);ctx.fill()});ctx.restore()}const gradient=ctx.createLinearGradient(firework.x,firework.y,firework.x-Math.cos(firework.angle)*config.trailLength,firework.y-Math.sin(firework.angle)*config.trailLength);gradient.addColorStop(0,firework.color);gradient.addColorStop(1,"transparent");ctx.beginPath();ctx.strokeStyle=gradient;ctx.lineWidth=config.lineWidth;ctx.moveTo(firework.x,firework.y);ctx.lineTo(firework.x-Math.cos(firework.angle)*config.trailLength,firework.y-Math.sin(firework.angle)*config.trailLength);ctx.stroke();ctx.beginPath();ctx.arc(firework.x,firework.y,config.size*0.5,0,Math.PI*2);ctx.fillStyle=firework.color;ctx.fill();if(config.glowSize>0){ctx.beginPath();ctx.arc(firework.x,firework.y,config.glowSize,0,Math.PI*2);ctx.fillStyle=`${firework.color}33`;ctx.fill()}}catch(error){reportError(error)}};const renderParticle=particle=>{try{ctx.beginPath();
1330switch(particle.type){case"ray":renderRayParticle(ctx,particle);break;case"glow":renderGlowParticle(ctx,particle);break;case"spark":renderSparkParticle(ctx,particle);break;case"comet":renderCometParticle(ctx,particle);break;case"trail":renderTrailParticle(ctx,particle);break;case"glitter":case"sparkle":renderGlitterParticle(ctx,particle);break;default:renderDefaultParticle(ctx,particle)}ctx.shadowBlur=6;ctx.shadowColor=particle.color;ctx.fill();ctx.shadowBlur=0}catch(error){reportError(error)}};const clear=(width,height)=>{try{ctx.fillStyle="rgba(0, 0, 0, 0.2)";ctx.fillRect(0,0,width,height)}catch(error){reportError(error)}};return{renderRocket,renderParticle,clear}}catch(error){reportError(error);return{renderRocket:()=>{},renderParticle:()=>{},clear:()=>{}}}}</script>
1330
1331    
1331<script>function renderRayParticle(ctx,particle){try{if(particle.delay>0){particle.delay--;return}const waveAmplitude=particle.size*0.8;const waveFrequency=0.15;const points=[];const segments=12;for(let i=0;i<=segments;i++){const t=i/segments;const dist=particle.trailLength*t;const wave=Math.sin(t*Math.PI*2*2)*waveAmplitude*(1-t);const perpX=Math.sin(particle.angle)*wave;const perpY=-Math.cos(particle.angle)*wave;points.push({x:particle.x-Math.cos(particle.angle)*dist+perpX,y:particle.y-Math.sin(particle.angle)*dist+perpY})}const glowGradient=ctx.createRadialGradient(particle.x,particle.y,0,particle.x,particle.y,particle.size*3);glowGradient.addColorStop(0,`${particle.color}${Math.floor(particle.alpha*255).toString(16).padStart(2,"0")}`);glowGradient.addColorStop(1,"transparent");ctx.fillStyle=glowGradient;ctx.beginPath();ctx.arc(particle.x,particle.y,particle.size*3,0,Math.PI*2);ctx.fill();const pathGradient=ctx.createLinearGradient(particle.x,particle.y,points[points.length-1].x,points[points.length-1].y);pathGradient.addColorStop(0,`${particle.color}${Math.floor(particle.alpha*255).toString(16).padStart(2,"0")}`);pathGradient.addColorStop(0.4,`${particle.color}${Math.floor(particle.alpha*180).toString(16).padStart(2,"0")}`);pathGradient.addColorStop(1,"transparent");ctx.strokeStyle=pathGradient;ctx.lineWidth=particle.size;ctx.lineCap="round";ctx.lineJoin="round";ctx.beginPath();ctx.moveTo(points[0].x,points[0].y);for(let i=1;i<points.length-1;i++){const xc=(points[i].x+points[i+1].x)/2;const yc=(points[i].y+points[i+1].y)/2;ctx.quadraticCurveTo(points[i].x,points[i].y,xc,yc)}ctx.quadraticCurveTo(points[points.length-2].x,points[points.length-2].y,points[points.length-1].x,points[points.length-1].y);ctx.stroke();const sparkCount=Math.floor(3+Math.random()*2);for(let i=0;i<sparkCount;i++){const t=Math.random();const idx=Math.floor(t*(points.length-1));const p=points[idx];const sparkGradient=ctx.createRadialGradient(p.x,p.y,0,p.x,p.y,particle.size);sparkGradient.addColorStop(0,`${particle.color}${Math.floor(particle.alpha*200).toString(16).padStart(2,"0")}`);sparkGradient.addColorStop(1,"transparent");ctx.fillStyle=sparkGradient;ctx.beginPath();ctx.arc(p.x,p.y,particle.size*(0.3+Math.random()*0.3),0,Math.PI*2);ctx.fill()}}catch(error){reportError(error)}}</script>
1331
1332    
1332<script>function renderGlowParticle(ctx,particle){try{const glowGradient=ctx.createRadialGradient(particle.x,particle.y,0,particle.x,particle.y,particle.size*2);glowGradient.addColorStop(0,`${particle.color}${Math.floor(particle.alpha*255).toString(16).padStart(2,"0")}`);glowGradient.addColorStop(1,"transparent");ctx.fillStyle=glowGradient;ctx.arc(particle.x,particle.y,particle.size*2,0,Math.PI*2);ctx.fill()}catch(error){reportError(error)}}</script>
1332
1333    
1333<script>function renderSparkParticle(ctx,particle){try{const sparkGradient=ctx.createRadialGradient(particle.x,particle.y,0,particle.x,particle.y,particle.size);sparkGradient.addColorStop(0,`${particle.color}${Math.floor(particle.alpha*255).toString(16).padStart(2,"0")}`);sparkGradient.addColorStop(1,"transparent");ctx.fillStyle=sparkGradient;ctx.arc(particle.x,particle.y,particle.size,0,Math.PI*2);ctx.fill()}catch(error){reportError(error)}}</script>
1333
1334    
1334<script>function renderCometParticle(ctx,particle){try{const trailGradient=ctx.createLinearGradient(particle.x,particle.y,particle.x-Math.cos(particle.angle)*(10+particle.trailLength),particle.y-Math.sin(particle.angle)*(10+particle.trailLength));trailGradient.addColorStop(0,`${particle.color}${Math.floor(particle.alpha*255).toString(16).padStart(2,"0")}`);trailGradient.addColorStop(1,"transparent");ctx.strokeStyle=trailGradient;ctx.lineWidth=particle.size;ctx.moveTo(particle.x,particle.y);ctx.lineTo(particle.x-Math.cos(particle.angle)*(10+particle.trailLength),particle.y-Math.sin(particle.angle)*(10+particle.trailLength));ctx.stroke()}catch(error){reportError(error)}}</script>
1334
1335    
1335<script>function renderTrailParticle(ctx,particle){try{const maxHistoryPoints=5;if(!particle.trailHistory){particle.trailHistory=[]}if(particle.lifeTime%2===0){particle.trailHistory.unshift({x:particle.x,y:particle.y,alpha:particle.alpha,size:particle.size});if(particle.trailHistory.length>maxHistoryPoints){particle.trailHistory.pop()}}if(particle.trailHistory.length<2)return;const gradient=ctx.createLinearGradient(particle.trailHistory[0].x,particle.trailHistory[0].y,particle.trailHistory[particle.trailHistory.length-1].x,particle.trailHistory[particle.trailHistory.length-1].y);gradient.addColorStop(0,`${particle.color}${Math.floor(particle.alpha*255).toString(16).padStart(2,"0")}`);gradient.addColorStop(1,"transparent");ctx.beginPath();ctx.strokeStyle=gradient;ctx.lineWidth=particle.size;ctx.lineCap="round";ctx.lineJoin="round";ctx.moveTo(particle.trailHistory[0].x,particle.trailHistory[0].y);for(let i=1;i<particle.trailHistory.length;i++){ctx.lineTo(particle.trailHistory[i].x,particle.trailHistory[i].y)}ctx.stroke();const glowSize=particle.size*1.2;const currentGlow=ctx.createRadialGradient(particle.x,particle.y,0,particle.x,particle.y,glowSize);const currentAlpha=Math.floor(particle.alpha*255).toString(16).padStart(2,"0");currentGlow.addColorStop(0,`${particle.color}${currentAlpha}`);currentGlow.addColorStop(1,"transparent");ctx.fillStyle=currentGlow;ctx.beginPath();ctx.arc(particle.x,particle.y,glowSize,0,Math.PI*2);ctx.fill()}catch(error){reportError(error)}}</script>
1335
1336    
1336<script>function renderGlitterParticle(ctx,particle){try{const glowSize=particle.size*(1+Math.random()*0.5);ctx.arc(particle.x,particle.y,glowSize,0,Math.PI*2);ctx.fillStyle=`${particle.color}${Math.floor(particle.alpha*255).toString(16).padStart(2,"0")}`;ctx.fill();ctx.beginPath();ctx.arc(particle.x,particle.y,glowSize*2,0,Math.PI*2);ctx.fillStyle=`${particle.color}${Math.floor(particle.alpha*127).toString(16).padStart(2,"0")}`;ctx.fill()}catch(error){reportError(error)}}</script>
1336
1337    
1337<script>function renderSparkleParticle(ctx,particle){try{if(particle.alpha===0)return;ctx.save();if(particle.isCenter){ctx.globalCompositeOperation="lighter";const gradient=ctx.createRadialGradient(particle.x,particle.y,0,particle.x,particle.y,particle.size);const alpha=particle.alpha;gradient.addColorStop(0,`rgba(255, 255, 255, ${alpha})`);gradient.addColorStop(0.2,particle.color.replace("rgb","rgba").replace(")",`,${alpha})`));gradient.addColorStop(1,"transparent");for(let i=0;i<6;i++){ctx.fillStyle=gradient;ctx.beginPath();ctx.arc(particle.x,particle.y,particle.size,0,Math.PI*2);ctx.fill()}}else{sparkleConfig.glow.blendModes.forEach(()=>{ctx.globalCompositeOperation="lighter";if(particle.alpha>=0.5){sparkleConfig.glow.boost.forEach(layer=>{ctx.fillStyle=particle.color;const x=Math.floor(particle.x);const y=Math.floor(particle.y);for(let i=0;i<layer.passes;i++){ctx.fillRect(x,y,layer.size,layer.size)}});sparkleConfig.glow.core.forEach(layer=>{ctx.fillStyle=particle.color;const x=Math.floor(particle.x)+0.25;const y=Math.floor(particle.y)+0.25;const halfSize=layer.size/2;for(let i=0;i<layer.passes;i++){if(layer.size<=0.5){ctx.fillRect(x-halfSize,y-halfSize,layer.size,layer.size)}else{ctx.beginPath();ctx.arc(x,y,halfSize,0,Math.PI*2);ctx.fill()}}});sparkleConfig.glow.outer.forEach(layer=>{ctx.fillStyle=particle.color;const x=Math.floor(particle.x)+0.25;const y=Math.floor(particle.y)+0.25;for(let i=0;i<layer.passes;i++){ctx.beginPath();ctx.arc(x,y,layer.size/2,0,Math.PI*2);ctx.fill()}})}})}ctx.restore()}catch(error){reportError(error)}}</script>
1337
1338    
1338<script>function renderDefaultParticle(ctx,particle){try{if(particle.type==="super"&&particle.previousPositions.length>=2){const config=particleConfig[particle.type];const gradient=ctx.createLinearGradient(particle.x,particle.y,particle.previousPositions[particle.previousPositions.length-1].x,particle.previousPositions[particle.previousPositions.length-1].y);for(let i=0;i<=20;i++){const stop=i/20;const alpha=Math.floor(particle.alpha*(255*Math.pow(1-stop,1.5))).toString(16).padStart(2,"0");gradient.addColorStop(stop,`${particle.color}${alpha}`)}ctx.save();ctx.globalCompositeOperation="lighter";ctx.beginPath();ctx.strokeStyle=gradient;ctx.lineWidth=particle.size*config.glowSize;ctx.lineCap="round";ctx.lineJoin="round";ctx.filter="blur(2px)";ctx.moveTo(particle.x,particle.y);let i=1;const step=Math.max(1,Math.floor(particle.previousPositions.length/50));while(i<particle.previousPositions.length-2){const xc=(particle.previousPositions[i].x+particle.previousPositions[i+1].x)/2;const yc=(particle.previousPositions[i].y+particle.previousPositions[i+1].y)/2;ctx.quadraticCurveTo(particle.previousPositions[i].x,particle.previousPositions[i].y,xc,yc);i+=step}ctx.stroke();ctx.restore();ctx.beginPath();ctx.strokeStyle=gradient;ctx.lineWidth=particle.size*0.6;ctx.lineCap="round";ctx.lineJoin="round";
1338ctx.moveTo(particle.x,particle.y);i=1;while(i<particle.previousPositions.length-2){const xc=(particle.previousPositions[i].x+particle.previousPositions[i+1].x)/2;const yc=(particle.previousPositions[i].y+particle.previousPositions[i+1].y)/2;ctx.quadraticCurveTo(particle.previousPositions[i].x,particle.previousPositions[i].y,xc,yc);i+=step}ctx.stroke()}ctx.beginPath();ctx.arc(particle.x,particle.y,particle.size,0,Math.PI*2);ctx.fillStyle=`${particle.color}${Math.floor(particle.alpha*255).toString(16).padStart(2,"0")}`;ctx.fill()}catch(error){reportError(error)}}</script>
1338
1339    
1339<script>function createWebGLRenderer(canvas){try{const gl=canvas.getContext("webgl")||canvas.getContext("experimental-webgl");if(!gl){console.warn("WebGL not supported, falling back to Canvas 2D");return null}console.log("Successfully created WebGL context");const vsSource=`
1340            attribute vec2 a_position;
1341            attribute float a_size;
1342            attribute vec4 a_color;
1343
1344            uniform vec2 u_resolution;
1345
1346            varying vec4 v_color;
1347            varying float v_size;
1348
1349            void main() {
1350                // Convert position from pixels to 0.0 to 1.0
1351                vec2 zeroToOne = a_position / u_resolution;
1352
1353                // Convert from 0->1 to 0->2
1354                vec2 zeroToTwo = zeroToOne * 2.0;
1355
1356                // Convert from 0->2 to -1->+1 (clipspace)
1357                vec2 clipSpace = zeroToTwo - 1.0;
1358
1359                // Flip Y coordinate
1360                gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
1361                gl_PointSize = a_size;
1362                v_color = a_color;
1363                v_size = a_size;
1364            }
1365        `;const fsSource=`
1366            precision mediump float;
1367            
1368            varying vec4 v_color;
1369            varying float v_size;
1370
1371            void main() {
1372                vec2 center = gl_PointCoord - vec2(0.5, 0.5);
1373                float dist = length(center);
1374                float alpha = 1.0 - smoothstep(0.45, 0.5, dist);
1375                gl_FragColor = v_color * alpha;
1376            }
1377        `;const vertexShader=createShader(gl,gl.VERTEX_SHADER,vsSource);const fragmentShader=createShader(gl,gl.FRAGMENT_SHADER,fsSource);const program=createProgram(gl,vertexShader,fragmentShader);const positionLoc=gl.getAttribLocation(program,"a_position");const sizeLoc=gl.getAttribLocation(program,"a_size");const colorLoc=gl.getAttribLocation(program,"a_color");const resolutionLoc=gl.getUniformLocation(program,"u_resolution");const positionBuffer=gl.createBuffer();const sizeBuffer=gl.createBuffer();const colorBuffer=gl.createBuffer();gl.enable(gl.BLEND);gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);function createShader(gl,type,source){const shader=gl.createShader(type);gl.shaderSource(shader,source);gl.compileShader(shader);if(!gl.getShaderParameter(shader,gl.COMPILE_STATUS)){console.error("Shader compile error:",gl.getShaderInfoLog(shader));gl.deleteShader(shader);return null}return shader}function createProgram(gl,vertexShader,fragmentShader){const program=gl.createProgram();gl.attachShader(program,vertexShader);gl.attachShader(program,fragmentShader);gl.linkProgram(program);if(!gl.getProgramParameter(program,gl.LINK_STATUS)){console.error("Program link error:",gl.getProgramInfoLog(program));return null}return program}function renderParticles(particles){try{console.log(`Rendering ${particles.length} particles with WebGL`);const positions=new Float32Array(particles.length*2);const sizes=new Float32Array(particles.length);const colors=new Float32Array(particles.length*4);particles.forEach((particle,i)=>{positions[i*2]=particle.x;positions[i*2+1]=particle.y;sizes[i]=particle.size*2;const color=parseColor(particle.color);colors[i*4]=color[0];colors[i*4+1]=color[1];colors[i*4+2]=color[2];colors[i*4+3]=particle.alpha});gl.viewport(0,0,gl.canvas.width,gl.canvas.height);gl.clear(gl.COLOR_BUFFER_BIT);gl.useProgram(program);gl.uniform2f(resolutionLoc,gl.canvas.width,gl.canvas.height);gl.bindBuffer(gl.ARRAY_BUFFER,positionBuffer);gl.bufferData(gl.ARRAY_BUFFER,positions,gl.STATIC_DRAW);gl.enableVertexAttribArray(positionLoc);gl.vertexAttribPointer(positionLoc,2,gl.FLOAT,false,0,0);gl.bindBuffer(gl.ARRAY_BUFFER,sizeBuffer);gl.bufferData(gl.ARRAY_BUFFER,sizes,gl.STATIC_DRAW);gl.enableVertexAttribArray(sizeLoc);gl.vertexAttribPointer(sizeLoc,1,gl.FLOAT,false,0,0);gl.bindBuffer(gl.ARRAY_BUFFER,colorBuffer);gl.bufferData(gl.ARRAY_BUFFER,colors,gl.STATIC_DRAW);gl.enableVertexAttribArray(colorLoc);gl.vertexAttribPointer(colorLoc,4,gl.FLOAT,false,0,0);gl.drawArrays(gl.POINTS,0,particles.length)}catch(error){reportError(error);throw error}}function parseColor(color){try{if(color.startsWith("rgba")){const matches=color.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/);if(matches){return[parseInt(matches[1])/255,parseInt(matches[2])/255,parseInt(matches[3])/255,parseFloat(matches[4])]}}if(color.startsWith("#")){const r=parseInt(color.substr(1,2),16)/255;const g=parseInt(color.substr(3,2),16)/255;const b=parseInt(color.substr(5,2),16)/255;return[r,g,b,1]}return[1,1,1,1]}catch(error){reportError(error);return[1,1,1,1]}}function clear(){try{gl.clearColor(0,0,0,0.2);gl.clear(gl.COLOR_BUFFER_BIT)}catch(error){reportError(error);throw error}}function resize(width,height){try{gl.canvas.width=width;gl.canvas.height=height;gl.viewport(0,0,width,height)}catch(error){reportError(error);throw error}}return{gl,renderParticles,clear,resize}}catch(error){reportError(error);return null}}</script>
1377
1378
1379    <!-- Hooks -->
1380    
1380<script>function useScreenSize(){const getScreenSizeMultiplier=React.useCallback(()=>{try{const width=window.innerWidth;const height=window.innerHeight;const area=width*height;const baseArea=1920*1080;const multiplier=Math.sqrt(area/baseArea);if(area>baseArea){return Math.max(1.5,multiplier*1.2)}return Math.max(1,multiplier)}catch(error){reportError(error);return 1}},[]);const[dimensions,setDimensions]=React.useState({width:window.innerWidth,height:window.innerHeight});React.useEffect(()=>{try{const handleResize=()=>{setDimensions({width:window.innerWidth,height:window.innerHeight})};window.addEventListener("resize",handleResize);return()=>window.removeEventListener("resize",handleResize)}catch(error){reportError(error)}},[]);return{...dimensions,getScreenSizeMultiplier}}</script>
1380
1381    
1381<script>const INTERACTION_CONSTANTS={DOUBLE_CLICK_DELAY:500,DOUBLE_CLICK_DISTANCE:20,DRAG_THRESHOLD:15,DRAG_TIME_THRESHOLD:100,SPECIAL_FIREWORK_CHANCE:0.01,SPECIAL_FIREWORK_MIN_INTERVAL:800,SPECIAL_FIREWORK_DISTANCE:{min:100,max:300},DRAG_OFFSET:90,FRAME_INTERVAL:16};const SPECIAL_FIREWORK_TYPES=["large","extra-large","super","comet","glitter","sparkle"];</script>
1381
1382    
1382<script>function isClicksClose(x1,y1,x2,y2,threshold){try{const distance=Math.hypot(x2-x1,y2-y1);return distance<=threshold}catch(error){reportError(error);throw error}}function getRandomSpecialType(){try{return SPECIAL_FIREWORK_TYPES[Math.floor(Math.random()*SPECIAL_FIREWORK_TYPES.length)]}catch(error){reportError(error);throw error}}function createSpecialFirework(x,y,type){try{return{x,y,identifier:`special_${Date.now()}`,specialType:type,startX:x,startY:y}}catch(error){reportError(error);throw error}}</script>
1382
1383    
1383<script>function useKeyboardInteractions(activeTouches){const isSpacePressed=React.useRef(false);const spaceDisabledUntil=React.useRef(0);const spaceKeyDownTime=React.useRef(0);const mousePosition=React.useRef({x:0,y:0});const HOLD_THRESHOLD=300;React.useEffect(()=>{try{const handleMouseMove=e=>{mousePosition.current={x:e.clientX,y:e.clientY}};window.addEventListener("mousemove",handleMouseMove);return()=>window.removeEventListener("mousemove",handleMouseMove)}catch(error){reportError(error);throw error}},[]);React.useEffect(()=>{try{const handleKeyDown=e=>{if((e.code==="Space"||e.key===" ")&&!isSpacePressed.current){e.preventDefault();const currentTime=Date.now();if(currentTime<spaceDisabledUntil.current){console.log("Space key cooling down...");return}isSpacePressed.current=true;spaceKeyDownTime.current=currentTime}};const handleKeyUp=e=>{if(e.code==="Space"||e.key===" "){e.preventDefault();if(!isSpacePressed.current)return;const keyUpTime=Date.now();const pressDuration=keyUpTime-spaceKeyDownTime.current;if(pressDuration<HOLD_THRESHOLD){const randomType=getRandomSpecialType();const fireworkId=`keyboard_${Date.now()}`;activeTouches.current.set(fireworkId,{x:mousePosition.current.x,y:mousePosition.current.y,identifier:fireworkId,specialType:randomType});requestAnimationFrame(()=>{activeTouches.current.delete(fireworkId)});console.log("Single special firework launched:",randomType)}else{createBurstFireworks(mousePosition.current.x,mousePosition.current.y,activeTouches,spaceDisabledUntil)}isSpacePressed.current=false;spaceKeyDownTime.current=0}};window.addEventListener("keydown",handleKeyDown);window.addEventListener("keyup",handleKeyUp);return()=>{window.removeEventListener("keydown",handleKeyDown);window.removeEventListener("keyup",handleKeyUp)}}catch(error){reportError(error);throw error}},[activeTouches])}</script>
1383
1384    
1384<script>function createPressedPositionHandler({isMousePressed,lastFrameTime,currentMousePosition,activeTouches,handleSpecialFirework,animationFrameRef}){const updatePosition=React.useCallback(()=>{try{if(!isMousePressed.current)return;const currentTime=Date.now();if(currentTime-lastFrameTime.current<INTERACTION_CONSTANTS.FRAME_INTERVAL){animationFrameRef.current=requestAnimationFrame(updatePosition);return}lastFrameTime.current=currentTime;const touchInfo=activeTouches.current.get("mouse");if(!touchInfo)return;const offsetX=(Math.random()-0.5)*INTERACTION_CONSTANTS.DRAG_OFFSET*2;const offsetY=(Math.random()-0.5)*INTERACTION_CONSTANTS.DRAG_OFFSET*2;activeTouches.current.set("mouse",{...touchInfo,x:currentMousePosition.current.x+offsetX,y:currentMousePosition.current.y+offsetY});handleSpecialFirework(currentMousePosition.current.x,currentMousePosition.current.y);animationFrameRef.current=requestAnimationFrame(updatePosition)}catch(error){reportError(error);throw error}},[handleSpecialFirework]);return updatePosition}</script>
1384
1385    
1385<script>function createMouseHandlers({activeTouches,isMousePressed,mouseDownTimeRef,currentMousePosition,lastFrameTime,animationFrameRef,handleClickEvent,updatePressedPosition}){const handleMouseDown=React.useCallback(e=>{try{const rect=e.currentTarget.getBoundingClientRect();const x=e.clientX-rect.left;const y=e.clientY-rect.top;mouseDownTimeRef.current=Date.now();isMousePressed.current=true;currentMousePosition.current={x,y};lastFrameTime.current=Date.now();handleClickEvent(x,y,"mouse");if(animationFrameRef.current){cancelAnimationFrame(animationFrameRef.current)}animationFrameRef.current=requestAnimationFrame(updatePressedPosition)}catch(error){reportError(error);throw error}},[handleClickEvent,updatePressedPosition]);const handleMouseMove=React.useCallback(e=>{try{if(!isMousePressed.current)return;const rect=e.currentTarget.getBoundingClientRect();const x=e.clientX-rect.left;const y=e.clientY-rect.top;currentMousePosition.current={x,y}}catch(error){reportError(error);throw error}},[]);const handleMouseUp=React.useCallback(()=>{try{if(animationFrameRef.current){cancelAnimationFrame(animationFrameRef.current);animationFrameRef.current=null}activeTouches.current.delete("mouse");isMousePressed.current=false;mouseDownTimeRef.current=0}catch(error){reportError(error);throw error}},[]);return{handleMouseDown,handleMouseMove,handleMouseUp}}</script>
1385
1386    
1386<script>function createTouchHandlers({activeTouches,handleClickEvent,handleSpecialFirework}){const handleTouchStart=React.useCallback(e=>{try{e.preventDefault();const rect=e.currentTarget.getBoundingClientRect();Array.from(e.touches).forEach(touch=>{const x=touch.clientX-rect.left;const y=touch.clientY-rect.top;handleClickEvent(x,y,touch.identifier)})}catch(error){reportError(error);throw error}},[handleClickEvent]);const handleTouchMove=React.useCallback(e=>{try{e.preventDefault();const rect=e.currentTarget.getBoundingClientRect();Array.from(e.touches).forEach(touch=>{const touchInfo=activeTouches.current.get(touch.identifier);if(!touchInfo)return;const currentX=touch.clientX-rect.left;const currentY=touch.clientY-rect.top;const offsetX=(Math.random()-0.5)*INTERACTION_CONSTANTS.DRAG_OFFSET*2;const offsetY=(Math.random()-0.5)*INTERACTION_CONSTANTS.DRAG_OFFSET*2;activeTouches.current.set(touch.identifier,{...touchInfo,x:currentX+offsetX,y:currentY+offsetY});handleSpecialFirework(currentX,currentY)})}catch(error){reportError(error);throw error}},[handleSpecialFirework]);const handleTouchEnd=React.useCallback(e=>{try{e.preventDefault();Array.from(e.changedTouches).forEach(touch=>{activeTouches.current.delete(touch.identifier)})}catch(error){reportError(error);throw error}},[]);return{handleTouchStart,handleTouchMove,handleTouchEnd}}</script>
1386
1387    
1387<script>function useFireworkInteractions(launchFirework){const activeTouches=React.useRef(new Map);const isMousePressed=React.useRef(false);const lastClickTime=React.useRef(0);const lastClickPosition=React.useRef({x:0,y:0});const clickTimeoutRef=React.useRef(null);const mouseDownTimeRef=React.useRef(0);const lastSpecialFireworkTime=React.useRef(0);const animationFrameRef=React.useRef(null);const currentMousePosition=React.useRef({x:0,y:0});const lastFrameTime=React.useRef(0);const doubleClickDisabledUntil=React.useRef(0);useKeyboardInteractions(activeTouches);const handleBurstFireworks=React.useCallback((baseX,baseY)=>{try{return createBurstFireworks(baseX,baseY,activeTouches,doubleClickDisabledUntil)}catch(error){reportError(error);throw error}},[]);const handleSpecialFirework=React.useCallback((baseX,baseY)=>{try{const currentTime=Date.now();if(currentTime-lastSpecialFireworkTime.current<INTERACTION_CONSTANTS.SPECIAL_FIREWORK_MIN_INTERVAL){return}const random=Math.random();const willTrigger=random<INTERACTION_CONSTANTS.SPECIAL_FIREWORK_CHANCE;if(willTrigger){const angle=Math.random()*Math.PI*2;const distance=INTERACTION_CONSTANTS.SPECIAL_FIREWORK_DISTANCE.min+Math.random()*(INTERACTION_CONSTANTS.SPECIAL_FIREWORK_DISTANCE.max-INTERACTION_CONSTANTS.SPECIAL_FIREWORK_DISTANCE.min);const targetX=baseX+Math.cos(angle)*distance;const targetY=baseY+Math.sin(angle)*distance;const specialType=getRandomSpecialType();const specialFirework=createSpecialFirework(targetX,targetY,specialType);activeTouches.current.set(specialFirework.identifier,specialFirework);lastSpecialFireworkTime.current=currentTime;requestAnimationFrame(()=>{activeTouches.current.delete(specialFirework.identifier)})}}catch(error){reportError(error);throw error}},[]);const handleClickEvent=React.useCallback((x,y,identifier)=>{try{const currentTime=Date.now();if(currentTime-lastClickTime.current<=INTERACTION_CONSTANTS.DOUBLE_CLICK_DELAY&&isClicksClose(x,y,lastClickPosition.current.x,lastClickPosition.current.y,INTERACTION_CONSTANTS.DOUBLE_CLICK_DISTANCE)){if(clickTimeoutRef.current){clearTimeout(clickTimeoutRef.current);clickTimeoutRef.current=null}handleBurstFireworks(x,y);lastClickTime.current=0;lastClickPosition.current={x:0,y:0};return}lastClickTime.current=currentTime;lastClickPosition.current={x,y};activeTouches.current.set(identifier,{x,y,identifier,startX:x,startY:y})}catch(error){reportError(error);throw error}},[handleBurstFireworks]);const updatePressedPosition=createPressedPositionHandler({isMousePressed,lastFrameTime,currentMousePosition,activeTouches,handleSpecialFirework,animationFrameRef});const mouseHandlers=createMouseHandlers({activeTouches,isMousePressed,mouseDownTimeRef,currentMousePosition,lastFrameTime,animationFrameRef,handleClickEvent,updatePressedPosition});const touchHandlers=createTouchHandlers({activeTouches,handleClickEvent,handleSpecialFirework});React.useEffect(()=>{return()=>{if(animationFrameRef.current){cancelAnimationFrame(animationFrameRef.current)}if(clickTimeoutRef.current){clearTimeout(clickTimeoutRef.current)}}},[]);return{activeTouches,handlers:{onMouseDown:mouseHandlers.handleMouseDown,onMouseMove:mouseHandlers.handleMouseMove,onMouseUp:mouseHandlers.handleMouseUp,onMouseLeave:mouseHandlers.handleMouseUp,onTouchStart:touchHandlers.handleTouchStart,onTouchMove:touchHandlers.handleTouchMove,onTouchEnd:touchHandlers.handleTouchEnd,onTouchCancel:touchHandlers.handleTouchEnd}}}</script>
1387
1388
1389    <!-- Components -->
1390    
1390<script>function WelcomeMessage(){return React.createElement("div",{"data-name":"welcome-message",className:"welcome-message"},React.createElement("h1",{"data-name":"welcome-title",className:"welcome-title"},"\uD83C\uDF86 Free Fireworks"),React.createElement("p",{"data-name":"welcome-text",className:"welcome-text"},"Click, watch, relax.",React.createElement("br",null),"Let the fireworks do the rest."),React.createElement("p",{"data-name":"welcome-built",className:"welcome-built"},"Built with Trickle"))}</script>
1390
1391    
1391<script>function AutoToggle({isActive,onToggle}){const[showTooltip,setShowTooltip]=React.useState(true);React.useEffect(()=>{try{const timer=setTimeout(()=>{setShowTooltip(false)},5000);return()=>clearTimeout(timer)}catch(error){reportError(error)}},[]);return React.createElement("div",{"data-name":"auto-toggle-container",className:"auto-toggle-container"},showTooltip&&React.createElement("div",{"data-name":"auto-toggle-tooltip",className:"auto-toggle-tooltip"},"Click to show time"),React.createElement("button",{"data-name":"auto-toggle",className:`auto-toggle ${isActive?"active":""}`,onClick:onToggle,"aria-pressed":isActive,title:isActive?"Zen Mode On":"Zen Mode Off"},React.createElement("img",{"data-name":"zen-icon",src:"https://app.trickle.so/storage/public/images/usr_09073b5640000001/0db27d7c-74fd-40ab-ba97-85286ddad28a.png",alt:"Zen Mode",className:"zen-icon"})))}</script>
1391
1392    
1392<script>function SoundToggle({className=""}){const[isMuted,setIsMuted]=React.useState(false);React.useEffect(()=>{try{soundManager.initialize()}catch(error){reportError(error)}},[]);const handleToggle=React.useCallback(()=>{try{const newMuted=!isMuted;setIsMuted(newMuted);soundManager.setMuted(newMuted)}catch(error){reportError(error)}},[isMuted]);return React.createElement("button",{"data-name":"sound-toggle",className:`sound-toggle ${isMuted?"muted":""} ${className}`,onClick:handleToggle,"aria-label":isMuted?"Unmute":"Mute",title:isMuted?"Unmute":"Mute"},isMuted?React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},React.createElement("path",{d:"M11 5L6 9H2v6h4l5 4V5z"}),React.createElement("line",{x1:"23",y1:"9",x2:"17",y2:"15"}),React.createElement("line",{x1:"17",y1:"9",x2:"23",y2:"15"})):React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},React.createElement("path",{d:"M11 5L6 9H2v6h4l5 4V5z"}),React.createElement("path",{d:"M15.54 8.46a5 5 0 0 1 0 7.07"}),React.createElement("path",{d:"M19.07 4.93a10 10 0 0 1 0 14.14"})))}</script>
1392
1393    
1393<script>function GuideButton({className=""}){const[isOpen,setIsOpen]=React.useState(false);const handleToggle=React.useCallback(()=>{try{setIsOpen(prev=>!prev)}catch(error){reportError(error)}},[]);const handleClose=React.useCallback(()=>{try{setIsOpen(false)}catch(error){reportError(error)}},[]);return React.createElement(React.Fragment,null,React.createElement("button",{"data-name":"guide-button",className:`guide-button ${className}`,onClick:handleToggle,"aria-label":"Show Guide",title:"Show Guide"},React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},React.createElement("path",{d:"M4 19.5A2.5 2.5 0 0 1 6.5 17H20"}),React.createElement("path",{d:"M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"}))),isOpen&&React.createElement("div",{className:"guide-overlay",onClick:handleClose,style:{pointerEvents:isOpen?"auto":"none"}},React.createElement("div",{className:"guide-content",onClick:e=>e.stopPropagation()},React.createElement("button",{className:"guide-close-button",onClick:handleClose,"aria-label":"Close Guide"},React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},React.createElement("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),React.createElement("line",{x1:"6",y1:"6",x2:"18",y2:"18"}))),React.createElement("h2",{className:"guide-title"},"Interactive Guide"),React.createElement("div",{className:"guide-section"},React.createElement("h3",null,"Basic Controls"),React.createElement("ul",null,React.createElement("li",null,React.createElement("strong",null,"Click/Tap:")," Launch a single firework"),React.createElement("li",null,React.createElement("strong",null,"Click & Hold/Touch & Hold:")," Create continuous fireworks"),React.createElement("li",null,React.createElement("strong",null,"Double Click/Double Tap:")," Launch a burst of special fireworks (with cooldown)"))),React.createElement("div",{className:"guide-section"},React.createElement("h3",null,"Zen Mode"),React.createElement("ul",null,React.createElement("li",null,React.createElement("strong",null,"Zen Button:")," Toggle auto-play mode with customizable settings"),React.createElement("li",null,React.createElement("strong",null,"Time Display:")," Drag to reposition, click/tap to access settings"),React.createElement("li",null,React.createElement("strong",null,"Hourly Celebration:")," Special firework shows at the start of each hour"))),React.createElement("div",{className:"guide-section"},React.createElement("h3",null,"Keyboard Controls"),React.createElement("ul",null,React.createElement("li",null,React.createElement("strong",null,"Space Bar (Tap):")," Launch a special firework"),React.createElement("li",null,React.createElement("strong",null,"Space Bar (Hold):")," Create a burst of fireworks"))),React.createElement("div",{className:"guide-footer"},React.createElement("a",{href:"https://www.trickle.so",target:"_blank",className:"trickle-link"},"Built with Trickle")))))}</script>
1393
1394    
1394<script>function TimePanel({isVisible,onSpeedChange,onClose}){if(!isVisible){return null}const isMobile=window.innerWidth<=768;const Container=isMobile?MobileContainer:DesktopContainer;return React.createElement(Container,null,React.createElement(SettingsPanelContent,{onSpeedChange:onSpeedChange,onClose:onClose}))}</script>
1394
1395    
1395<script>function FireworkConsole({onLaunch}){const[isOpen,setIsOpen]=React.useState(false);const[shouldShow,setShouldShow]=React.useState(false);React.useEffect(()=>{try{const shouldShowConsole=isDebugMode();console.log("Should show console:",shouldShowConsole);setShouldShow(shouldShowConsole)}catch(error){reportError(error);console.error("Error in FireworkConsole:",error)}},[]);const fireworkTypes=[{id:"normal",name:"\u666E\u901A\u70DF\u82B1",type:null},{id:"large",name:"\u5927\u578B\u70DF\u82B1",type:"large"},{id:"extra-large",name:"\u7279\u5927\u70DF\u82B1",type:"extra-large"},{id:"super",name:"\u8D85\u7EA7\u70DF\u82B1",type:"super"},{id:"comet",name:"\u5F57\u661F\u70DF\u82B1",type:"comet"},{id:"glitter",name:"\u95EA\u5149\u70DF\u82B1",type:"glitter"},{id:"sparkle",name:"\u95EA\u8000\u70DF\u82B1",type:"sparkle"}];const handleToggle=React.useCallback(()=>{try{setIsOpen(prev=>!prev)}catch(error){reportError(error)}},[]);const handleLaunch=React.useCallback(type=>{try{const screenWidth=window.innerWidth;const screenHeight=window.innerHeight;const x=screenWidth/2;const y=screenHeight/2;console.log("Launching firework:",{type,x,y});onLaunch(x,y,type)}catch(error){reportError(error)}},[onLaunch]);if(!shouldShow){return null}if(!isOpen){return React.createElement("button",{"data-name":"console-toggle",className:"console-toggle",onClick:handleToggle,title:"\u6253\u5F00\u63A7\u5236\u53F0"},React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},React.createElement("polyline",{points:"16 18 22 12 16 6"}),React.createElement("polyline",{points:"8 6 2 12 8 18"})))}return React.createElement("div",{"data-name":"firework-console",className:"firework-console"},React.createElement("div",{className:"console-header"},React.createElement("h3",null,"\u70DF\u82B1\u63A7\u5236\u53F0"),React.createElement("button",{className:"close-button",onClick:handleToggle,title:"\u5173\u95ED\u63A7\u5236\u53F0"},React.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"},React.createElement("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),React.createElement("line",{x1:"6",y1:"6",x2:"18",y2:"18"})))),React.createElement("div",{className:"console-content"},React.createElement("div",{className:"controls-section"},React.createElement("h4",null,"\u70DF\u82B1\u7C7B\u578B"),React.createElement("div",{className:"firework-buttons"},fireworkTypes.map(firework=>React.createElement("button",{key:firework.id,className:"firework-button",onClick:()=>handleLaunch(firework.type)},firework.name))))))}</script>
1395
1396    
1396<script>function CooldownIndicator(){const[isShaking,setIsShaking]=React.useState(false);const[remainingTime,setRemainingTime]=React.useState(0);const[progress,setProgress]=React.useState(0);const animationFrameRef=React.useRef(null);const endTimeRef=React.useRef(0);const updateCooldown=React.useCallback(()=>{try{const now=Date.now();const timeLeft=Math.max(0,endTimeRef.current-now);if(timeLeft<=0){setRemainingTime(0);setProgress(100);endTimeRef.current=0;if(animationFrameRef.current){cancelAnimationFrame(animationFrameRef.current);animationFrameRef.current=null}return}const newProgress=(5000-timeLeft)/5000*100;setRemainingTime(Math.ceil(timeLeft/1000));setProgress(newProgress);animationFrameRef.current=requestAnimationFrame(updateCooldown)}catch(error){reportError(error);throw error}},[]);React.useEffect(()=>{try{const handleBurstStart=event=>{try{const{endTime,startTime}=event.detail;console.log("Received burst start event:",{endTime,startTime});if(endTimeRef.current>0){setIsShaking(true);setTimeout(()=>setIsShaking(false),500);return}endTimeRef.current=endTime;setRemainingTime(5);setProgress(0);if(animationFrameRef.current){cancelAnimationFrame(animationFrameRef.current)}animationFrameRef.current=requestAnimationFrame(updateCooldown)}catch(error){reportError(error);throw error}};window.addEventListener("burstFireworkStart",handleBurstStart);return()=>{window.removeEventListener("burstFireworkStart",handleBurstStart);if(animationFrameRef.current){cancelAnimationFrame(animationFrameRef.current);animationFrameRef.current=null}}}catch(error){reportError(error);throw error}},[updateCooldown]);if(remainingTime<=0){return null}return React.createElement("div",{"data-name":"cooldown-indicator",className:`cooldown-indicator ${isShaking?"shaking":""}`},React.createElement("div",{"data-name":"cooldown-progress",className:"cooldown-progress",style:{width:`${progress}%`}}),React.createElement("div",{className:"cooldown-text"},"Cooldown ",remainingTime,"s"))}</script>
1396
1397    
1397<script>function TrickleBadge(){return React.createElement("a",{"data-name":"trickle-badge",href:"https://www.trickle.so",className:"trickle-badge",target:"_blank"},React.createElement("div",{className:"trickle-badge-content"},React.createElement("span",{className:"trickle-badge-text"},"Built with"),React.createElement("img",{src:"https://app.trickle.so/storage/public/images/usr_09073b5640000001/64b555cd-e9b1-4a09-8048-27de64928470.svg",alt:"Trickle",className:"trickle-badge-logo"})))}</script>
1397
1398    
1398<script>function DraggableTime({time,settings,formatTime,formatDate,isVisible,onPositionChange,onClick}){const[isDragging,setIsDragging]=React.useState(false);const elementRef=React.useRef(null);const dragStartRef=React.useRef({mouseX:0,mouseY:0,elementX:0,elementY:0,startTime:0});const handleDragStart=React.useCallback(e=>{try{const touch=e.touches?e.touches[0]:e;if(!touch)return;dragStartRef.current={mouseX:touch.clientX,mouseY:touch.clientY,elementX:settings.position.x,elementY:settings.position.y,startTime:Date.now()};setIsDragging(true)}catch(error){reportError(error)}},[settings.position.x,settings.position.y]);const handleDrag=React.useCallback(e=>{try{if(!isDragging)return;const touch=e.touches?e.touches[0]:e;if(!touch)return;e.preventDefault();e.stopPropagation();const deltaX=touch.clientX-dragStartRef.current.mouseX;const deltaY=touch.clientY-dragStartRef.current.mouseY;const percentX=deltaX/window.innerWidth*100;const percentY=deltaY/window.innerHeight*100;const newX=dragStartRef.current.elementX+percentX;const newY=dragStartRef.current.elementY+percentY;onPositionChange({x:Math.max(0,Math.min(100,newX)),y:Math.max(0,Math.min(100,newY))})}catch(error){reportError(error)}},[isDragging,onPositionChange]);const handleDragEnd=React.useCallback(e=>{try{const endTime=Date.now();const dragDuration=endTime-dragStartRef.current.startTime;setIsDragging(false);if(dragDuration<200&&onClick){const touch=e.changedTouches?e.changedTouches[0]:e;const deltaX=touch.clientX-dragStartRef.current.mouseX;const deltaY=touch.clientY-dragStartRef.current.mouseY;const distance=Math.sqrt(deltaX*deltaX+deltaY*deltaY);if(distance<10){onClick(e)}}}catch(error){reportError(error)}},[onClick]);React.useEffect(()=>{if(isDragging){window.addEventListener("mousemove",handleDrag);window.addEventListener("mouseup",handleDragEnd);window.addEventListener("touchmove",handleDrag,{passive:false});window.addEventListener("touchend",handleDragEnd);return()=>{window.removeEventListener("mousemove",handleDrag);window.removeEventListener("mouseup",handleDragEnd);window.removeEventListener("touchmove",handleDrag);window.removeEventListener("touchend",handleDragEnd)}}},[isDragging,handleDrag,handleDragEnd]);return React.createElement("div",{ref:elementRef,"data-name":"draggable-time",className:`time-display ${isVisible?"active":""} ${settings.showTimeBackground?"with-background":""} ${isDragging?"dragging":""}`,style:{backgroundColor:settings.showTimeBackground?`${settings.timeBackgroundColor}cc`:undefined,fontSize:`${settings.fontSize}rem`,left:`${settings.position.x}%`,top:`${settings.position.y}%`,cursor:isDragging?"grabbing":"grab"},onMouseDown:handleDragStart,onTouchStart:handleDragStart},settings.showDate&&React.createElement("div",{"data-name":"date-display",className:"date-display"},formatDate(time)),React.createElement("div",{"data-name":"time-text"},formatTime(time)))}</script>
1398
1399    
1399<script>function Slider({value,onChange,min,max,step,suffix="",width=120}){return React.createElement("div",{"data-name":"settings-slider",className:"settings-slider",style:{minWidth:width}},React.createElement("input",{type:"range",min:min,max:max,step:step,value:value,onChange:onChange}),React.createElement("span",{className:"slider-value"},value,suffix))}</script>
1399
1400    
1400<script>function SettingsPanelContent({onSpeedChange,onClose,settings,onSettingsChange}){const[showTestButton,setShowTestButton]=React.useState(false);React.useEffect(()=>{try{setShowTestButton(isDebugMode())}catch(error){reportError(error)}},[]);const toggleSetting=React.useCallback(key=>{try{onSettingsChange({[key]:!settings[key]})}catch(error){reportError(error)}},[settings,onSettingsChange]);const handleSpeedChange=React.useCallback(e=>{try{const newSpeed=parseInt(e.target.value);onSpeedChange(newSpeed)}catch(error){reportError(error)}},[onSpeedChange]);const handleFontSizeChange=React.useCallback(e=>{try{const newSize=parseFloat(e.target.value);onSettingsChange({fontSize:newSize})}catch(error){reportError(error)}},[onSettingsChange]);const handleColorChange=React.useCallback(e=>{try{onSettingsChange({timeBackgroundColor:e.target.value})}catch(error){reportError(error)}},[onSettingsChange]);const handleResetPosition=React.useCallback(e=>{try{e.stopPropagation();onSettingsChange({position:{x:50,y:50}})}catch(error){reportError(error)}},[onSettingsChange]);const handleDateFormatChange=React.useCallback(e=>{try{onSettingsChange({dateFormat:e.target.value})}catch(error){reportError(error)}},[onSettingsChange]);const handleTestCelebration=React.useCallback(()=>{try{celebrationManager.startCelebration()}catch(error){reportError(error)}},[]);return React.createElement("div",{"data-name":"settings-list",className:"settings-list"},React.createElement("div",{className:"settings-item"},React.createElement("label",{className:"settings-label"},"Show Time"),React.createElement("div",{className:"settings-control"},React.createElement("button",{"data-name":"show-time-toggle",className:`toggle-button ${settings.showTime?"active":""}`,onClick:()=>toggleSetting("showTime")}))),settings.showTime&&React.createElement(React.Fragment,null,React.createElement("div",{className:"settings-item"},React.createElement("label",{className:"settings-label"},"Show Date"),React.createElement("div",{className:"settings-control"},React.createElement("button",{"data-name":"show-date-toggle",className:`toggle-button ${settings.showDate?"active":""}`,onClick:()=>toggleSetting("showDate")}))),settings.showDate&&React.createElement("div",{className:"settings-item"},React.createElement("label",{className:"settings-label"},"Date Format"),React.createElement("div",{className:"settings-control select-container"},React.createElement("select",{value:settings.dateFormat,onChange:handleDateFormatChange,className:"date-format-select"},React.createElement("option",{value:"yyyy-MM-dd"},"YYYY-MM-DD"),React.createElement("option",{value:"MM/dd/yyyy"},"MM/DD/YYYY"),React.createElement("option",{value:"dd/MM/yyyy"},"DD/MM/YYYY"),React.createElement("option",{value:"MMM dd"},"MMM DD"),React.createElement("option",{value:"MMMM dd"},"MMMM DD")),React.createElement("span",{className:"select-arrow"},"\u25BC"))),React.createElement("div",{className:"settings-item"},React.createElement("label",{className:"settings-label"},"Time Format"),React.createElement("div",{className:"settings-control"},React.createElement("button",{"data-name":"time-format-toggle",className:`toggle-button ${settings.is24Hour?"active":""}`,onClick:()=>toggleSetting("is24Hour")}))),React.createElement("div",{className:"settings-item"},React.createElement("label",{className:"settings-label"}
1400,"Time Background"),React.createElement("div",{className:"settings-control"},React.createElement("input",{type:"color",value:settings.timeBackgroundColor,onChange:handleColorChange,className:"color-picker"}),React.createElement("button",{"data-name":"time-background-toggle",className:`toggle-button ${settings.showTimeBackground?"active":""}`,onClick:()=>toggleSetting("showTimeBackground")}))),React.createElement("div",{className:"settings-item"},React.createElement("label",{className:"settings-label"},"Font Size"),React.createElement("div",{className:"settings-control settings-slider"},React.createElement("input",{type:"range",min:"2",max:"8",step:"0.5",value:settings.fontSize,onChange:handleFontSizeChange}),React.createElement("span",{className:"size-value"},settings.fontSize,"rem"))),React.createElement("div",{className:"settings-item"},React.createElement("label",{className:"settings-label"},"Position"),React.createElement("div",{className:"settings-control"},React.createElement("button",{"data-name":"reset-position",className:"action-button",onClick:handleResetPosition},"Reset")))),React.createElement("div",{className:"settings-item"},React.createElement("label",{className:"settings-label"},"Hourly Fireworks"),React.createElement("div",{className:"settings-control"},React.createElement("button",{"data-name":"hourly-fireworks-toggle",className:`toggle-button ${settings.hourlyFireworks?"active":""}`,onClick:()=>toggleSetting("hourlyFireworks")}),showTestButton&&React.createElement("button",{"data-name":"test-celebration",className:"action-button",onClick:handleTestCelebration},"Test"))),React.createElement("div",{className:"settings-item"},React.createElement("label",{className:"settings-label"},"Firework Speed"),React.createElement("div",{className:"settings-control settings-slider"},React.createElement("input",{type:"range",min:"1",max:"100",value:settings.fireworkSpeed,onChange:handleSpeedChange}),React.createElement("span",{className:"speed-value"},settings.fireworkSpeed,"%"))),React.createElement("div",{className:"settings-item"},React.createElement("div",{className:"settings-label"},React.createElement("a",{href:"https://www.trickle.so",target:"_blank",className:"trickle-link"},"Built with Trickle")),React.createElement("div",{className:"settings-control"},React.createElement("button",{"data-name":"close-auto-mode",className:"close-button",onClick:onClose},"Exit"))))}</script>
1400
1401    
1401<script>function MobileContainer({children}){const[isPanelVisible,setIsPanelVisible]=React.useState(false);const[time,setTime]=React.useState(new Date);const[settings,setSettings]=React.useState({is24Hour:true,showTime:true,showDate:true,dateFormat:"yyyy-MM-dd",showTimeBackground:true,timeBackgroundColor:"#333333",fontSize:4,position:{x:50,y:50},hourlyFireworks:true,fireworkSpeed:50});React.useEffect(()=>{try{const timer=setInterval(()=>{setTime(new Date)},1000);return()=>clearInterval(timer)}catch(error){reportError(error)}},[]);const formatTime=React.useCallback(date=>{try{if(settings.is24Hour){return date.toLocaleTimeString("en-US",{hour12:false,hour:"2-digit",minute:"2-digit",second:"2-digit"})}else{return date.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",second:"2-digit"})}}catch(error){reportError(error);return""}},[settings.is24Hour]);const formatDate=React.useCallback(date=>{try{const format=settings.dateFormat;const year=date.getFullYear();const month=String(date.getMonth()+1).padStart(2,"0");const day=String(date.getDate()).padStart(2,"0");const monthNames=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];const fullMonthNames=["January","February","March","April","May","June","July","August","September","October","November","December"];switch(format){case"yyyy-MM-dd":return`${year}-${month}-${day}`;case"MM/dd/yyyy":return`${month}/${day}/${year}`;case"dd/MM/yyyy":return`${day}/${month}/${year}`;case"MMM dd":return`${monthNames[date.getMonth()]} ${day}`;case"MMMM dd":return`${fullMonthNames[date.getMonth()]} ${day}`;default:return`${year}-${month}-${day}`}}catch(error){reportError(error);return""}},[settings.dateFormat]);const handleModalClick=React.useCallback(e=>{try{e.stopPropagation();setIsPanelVisible(false)}catch(error){reportError(error)}},[]);const handleTimeClick=React.useCallback(e=>{try{e.stopPropagation();if(window.innerWidth<=768){setIsPanelVisible(true)}}catch(error){reportError(error)}},[]);const handlePositionChange=React.useCallback(newPosition=>{try{setSettings(prev=>({...prev,position:newPosition}))}catch(error){reportError(error)}},[]);const handleSettingsChange=React.useCallback(newSettings=>{try{setSettings(prev=>({...prev,...newSettings}))}catch(error){reportError(error)}},[]);return React.createElement("div",{"data-name":"time-container"},React.createElement("div",{"data-name":"modal-mask",className:`modal-mask ${isPanelVisible?"active":""}`,onClick:handleModalClick}),settings.showTime&&React.createElement(DraggableTime,{time:time,settings:settings,formatTime:formatTime,formatDate:formatDate,isVisible:true,onPositionChange:handlePositionChange,onClick:handleTimeClick}),React.createElement("div",{"data-name":"time-panel",className:`time-panel ${isPanelVisible?"active":""}`,onClick:e=>e.stopPropagation()},React.cloneElement(children,{settings:settings,onSettingsChange:handleSettingsChange})))}</script>
1401
1402    
1402<script>function DesktopContainer({children}){const[isPanelVisible,setIsPanelVisible]=React.useState(false);const[time,setTime]=React.useState(new Date);const[settings,setSettings]=React.useState({is24Hour:true,showTime:true,showDate:true,dateFormat:"yyyy-MM-dd",showTimeBackground:true,timeBackgroundColor:"#333333",fontSize:4,position:{x:50,y:50},hourlyFireworks:true,fireworkSpeed:50});const hideTimeoutRef=React.useRef(null);React.useEffect(()=>{try{const timer=setInterval(()=>{setTime(new Date)},1000);return()=>{clearInterval(timer);if(hideTimeoutRef.current){clearTimeout(hideTimeoutRef.current)}}}catch(error){reportError(error)}},[]);const formatTime=React.useCallback(date=>{try{if(settings.is24Hour){return date.toLocaleTimeString("en-US",{hour12:false,hour:"2-digit",minute:"2-digit",second:"2-digit"})}else{return date.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",second:"2-digit"})}}catch(error){reportError(error);return""}},[settings.is24Hour]);const formatDate=React.useCallback(date=>{try{const format=settings.dateFormat;const year=date.getFullYear();const month=String(date.getMonth()+1).padStart(2,"0");const day=String(date.getDate()).padStart(2,"0");const monthNames=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];const fullMonthNames=["January","February","March","April","May","June","July","August","September","October","November","December"];switch(format){case"yyyy-MM-dd":return`${year}-${month}-${day}`;case"MM/dd/yyyy":return`${month}/${day}/${year}`;case"dd/MM/yyyy":return`${day}/${month}/${year}`;case"MMM dd":return`${monthNames[date.getMonth()]} ${day}`;case"MMMM dd":return`${fullMonthNames[date.getMonth()]} ${day}`;default:return`${year}-${month}-${day}`}}catch(error){reportError(error);return""}},[settings.dateFormat]);const handlePanelMouseEnter=React.useCallback(()=>{try{if(hideTimeoutRef.current){clearTimeout(hideTimeoutRef.current)}setIsPanelVisible(true)}catch(error){reportError(error)}},[]);const handlePanelMouseLeave=React.useCallback(()=>{try{if(hideTimeoutRef.current){clearTimeout(hideTimeoutRef.current)}hideTimeoutRef.current=setTimeout(()=>{setIsPanelVisible(false)},500)}catch(error){reportError(error)}},[]);const handlePositionChange=React.useCallback(newPosition=>{try{setSettings(prev=>({...prev,position:newPosition}))}catch(error){reportError(error)}},[]);const handleSettingsChange=React.useCallback(newSettings=>{try{setSettings(prev=>({...prev,...newSettings}))}catch(error){reportError(error)}},[]);return React.createElement("div",{"data-name":"time-container"},settings.showTime&&React.createElement(DraggableTime,{time:time,settings:settings,formatTime:formatTime,formatDate:formatDate,isVisible:true,onPositionChange:handlePositionChange}),React.createElement("div",{"data-name":"hover-trigger",className:"hover-trigger",onMouseEnter:handlePanelMouseEnter}),React.createElement("div",{"data-name":"time-panel",className:`time-panel ${isPanelVisible?"active":""}`,onMouseEnter:handlePanelMouseEnter,onMouseLeave:handlePanelMouseLeave,onClick:e=>e.stopPropagation()},React.cloneElement(children,{settings:settings,onSettingsChange:handleSettingsChange})))}</script>
1402
1403    
1403<script>function useExplosion(getScreenSizeMultiplier){const secondaryExplosionsRef=React.useRef(new Set);return React.useCallback((x,y,color,specialType=null)=>{try{const screenMultiplier=getScreenSizeMultiplier();if(specialType==="super"&&!secondaryExplosionsRef.current.has(`${x},${y}`)){secondaryExplosionsRef.current.add(`${x},${y}`);const allParticles=[];const firstExplosion=createSuperFirework(x,y,color,screenMultiplier,1);allParticles.push(...firstExplosion);const secondColor=createColorVariation(color,15);const secondExplosion=createSuperFirework(x,y,secondColor,screenMultiplier,2);secondExplosion.forEach(p=>{p.delay=18});allParticles.push(...secondExplosion);const thirdColor=createColorVariation(color,25);const thirdExplosion=createSuperFirework(x,y,thirdColor,screenMultiplier,3);thirdExplosion.forEach(p=>{p.delay=36});allParticles.push(...thirdExplosion);setTimeout(()=>{secondaryExplosionsRef.current.delete(`${x},${y}`)},1000);return{immediate:allParticles,secondary:[]}}else{const newParticles=createFirework(x,y,color,screenMultiplier,specialType);return{immediate:newParticles,secondary:[]}}}catch(error){reportError(error);return{immediate:[],secondary:[]}}},[getScreenSizeMultiplier])}</script>
1403
1404    
1404<script>function useLaunchFirework(width,getScreenSizeMultiplier,lastFireTime){return React.useCallback((targetX,targetY,touchId,specialType=null)=>{try{const currentTime=Date.now();const lastTime=lastFireTime.current.get(touchId)||0;const screenMultiplier=getScreenSizeMultiplier();if(touchId==="custom"||currentTime-lastTime>(specialType==="large"||specialType==="extra-large"?100:40/screenMultiplier)){const startX=Math.random()*width;const startY=window.innerHeight;const rocket=createRocket(startX,startY,targetX,targetY);if(rocket){rocket.specialType=specialType;lastFireTime.current.set(touchId,currentTime);console.log("Launching rocket:",{startX,startY,targetX,targetY,specialType,touchId});return rocket}}else{console.log("Launch blocked by interval:",{currentTime,lastTime,diff:currentTime-lastTime,touchId,specialType})}return null}catch(error){reportError(error);throw error}},[width,getScreenSizeMultiplier])}</script>
1404
1405    
1405<script>function useAutoFirework(width,height,fireworkSpeed,launchFirework,autoModeManager){const lastAutoFireTime=React.useRef(0);const lastHourRef=React.useRef(-1);return React.useCallback(()=>{try{const currentTime=Date.now();const currentHour=new Date().getHours();const currentMinute=new Date().getMinutes();const currentSecond=new Date().getSeconds();if(lastHourRef.current!==currentHour&&currentMinute===0&&currentSecond===0){lastHourRef.current=currentHour;celebrationManager.startCelebration()}if(celebrationManager.shouldLaunchFirework()){const position=celebrationManager.getRandomPosition(width,height);const specialType=celebrationManager.getRandomFireworkType();const rocket=launchFirework(position.x,position.y,"celebration",specialType);if(rocket){return rocket}}if(autoModeManager.burstModeRef.active){if(currentTime-lastAutoFireTime.current>=autoModeManager.burstModeRef.nextDelay){const targetX=Math.random()*width;const targetY=Math.random()*(height*0.7);const rocket=launchFirework(targetX,targetY,"auto",autoModeManager.burstModeRef.isExtraLarge?"extra-large":autoModeManager.burstModeRef.isLargeFirework?"large":null);if(rocket){lastAutoFireTime.current=currentTime;autoModeManager.burstModeRef.remainingCount--;if(autoModeManager.burstModeRef.remainingCount<=0){autoModeManager.burstModeRef.active=false}else{autoModeManager.burstModeRef.nextDelay=autoModeManager.burstModeRef.isLargeFirework?200+Math.random()*300:30+Math.random()*100}return rocket}}}else{const baseInterval=3000*Math.pow(1-fireworkSpeed/100,2)+250;const interval=autoModeManager.getRandomInterval(baseInterval,fireworkSpeed);if(currentTime-lastAutoFireTime.current>interval){const targetX=Math.random()*width;const targetY=Math.random()*(height*0.7);let specialType=null;if(Math.random()<0.03){const specialTypes=["large","comet","glitter","sparkle","super"];specialType=specialTypes[Math.floor(Math.random()*specialTypes.length)]}const rocket=launchFirework(targetX,targetY,"auto",specialType);if(rocket){lastAutoFireTime.current=currentTime;autoModeManager.updateBurstMode();return rocket}}}return null}catch(error){reportError(error);return null}},[width,height,fireworkSpeed,launchFirework,autoModeManager])}</script>
1405
1406    
1406<script>function useAnimation(canvasRef,autoMode,activeTouches,launchFirework,launchAutoFirework,createExplosion){const[fireworks,setFireworks]=React.useState([]);const[particles,setParticles]=React.useState([]);const requestRef=React.useRef();const webglRendererRef=React.useRef(null);const cleanupRef=React.useRef({pendingExplosions:new Set,timeouts:new Set,particleCount:0,maxParticles:3000});React.useEffect(()=>{try{const canvas=canvasRef.current;if(canvas){webglRendererRef.current=createWebGLRenderer(canvas);if(webglRendererRef.current){webglRendererRef.current.resize(canvas.width,canvas.height);console.log("WebGL renderer initialized")}else{console.log("Using Canvas 2D fallback")}}return()=>{webglRendererRef.current=null}}catch(error){reportError(error)}},[]);const cleanup=React.useCallback(()=>{try{cleanupRef.current.pendingExplosions.forEach(promise=>{if(promise&&typeof promise.cancel==="function"){promise.cancel()}});cleanupRef.current.pendingExplosions.clear();cleanupRef.current.timeouts.forEach(timeout=>{clearTimeout(timeout)});cleanupRef.current.timeouts.clear();setParticles(prev=>{prev.forEach(particle=>{if(particle&&typeof particle.cleanup==="function"){particle.cleanup()}});return[]});setFireworks(prev=>{prev.forEach(firework=>{if(firework&&typeof firework.cleanup==="function"){firework.cleanup()}});return[]});cleanupRef.current.particleCount=0}catch(error){reportError(error)}},[]);const limitParticles=React.useCallback(newParticles=>{try{if(!newParticles)return[];const currentCount=cleanupRef.current.particleCount;const remainingSpace=cleanupRef.current.maxParticles-currentCount;if(remainingSpace<=0){console.warn("Maximum particle limit reached, skipping new particles");return[]}if(newParticles.length>remainingSpace){console.warn(`Limiting particles from ${newParticles.length} to ${remainingSpace}`);return newParticles.slice(0,remainingSpace)}return newParticles}catch(error){reportError(error);return[]}},[]);const animate=React.useCallback(()=>{try{const canvas=canvasRef.current;if(!canvas)return;const renderer=webglRendererRef.current||createCanvasRenderer(canvas.getContext("2d"));renderer.clear(canvas.width,canvas.height);activeTouches.current.forEach(pos=>{const rocket=launchFirework(pos.x,pos.y,pos.identifier,pos.specialType);if(rocket){setFireworks(prev=>[...prev,rocket])}});if(autoMode){const autoRocket=launchAutoFirework();if(autoRocket){setFireworks(prev=>[...prev,autoRocket])}}setFireworks(prev=>prev.filter(firework=>{if(!firework.update()){const explosionResult=createExplosion(firework.x,firework.y,firework.color,firework.specialType);setParticles(prev=>[...prev,...explosionResult.immediate]);if(explosionResult.secondary&&explosionResult.secondary.length>0){explosionResult.secondary.forEach(promise=>{promise.then(result=>{if(result){const{rocket,targetX,targetY,flightTime}=result;setFireworks(prev=>[...prev,rocket]);setTimeout(()=>{const secondaryParticles=createExplosion(targetX,targetY,getRandomColor()).immediate;setParticles(prev=>[...prev,...secondaryParticles]);soundManager.playSound(true)},flightTime)}})})}soundManager.playSound(firework.specialType==="large"||firework.specialType==="extra-large");return false}if(!webglRendererRef.current){renderer.renderRocket(firework)}else{const ctx=canvas.getContext("2d");const canvasRenderer=createCanvasRenderer(ctx);canvasRenderer.renderRocket(firework)}return true}));setParticles(prev=>{const remainingParticles=prev.filter(particle=>{if(!particle||typeof particle.update!=="function"){cleanupRef.current.particleCount--;return false}if(!particle.update()){if(particle.cleanup){particle.cleanup()}cleanupRef.current.particleCount--;return false}return true});if(webglRendererRef.current&&remainingParticles.length>0){console.log("Using WebGL renderer for particles");webglRendererRef.current.renderParticles(remainingParticles)}else{console.log("Using Canvas 2D renderer for particles");remainingParticles.forEach(particle=>{if(typeof particle.draw==="function"){particle.draw(canvas.getContext("2d"))}else{renderer.renderParticle(particle)}})}return remainingParticles});requestRef.current=requestAnimationFrame(animate)}catch(error){reportError(error);throw error}},[autoMode,launchFirework,launchAutoFirework,createExplosion,limitParticles]);React.useEffect(()=>{requestRef.current=requestAnimationFrame(animate);return()=>{if(requestRef.current){cancelAnimationFrame(requestRef.current)}cleanup()}},[animate,cleanup]);return[fireworks,particles]}</script>
1406
1407    
1407<script>function _extends(){return _extends=Object.assign?Object.assign.bind():function(n){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var r in t)({}).hasOwnProperty.call(t,r)&&(n[r]=t[r])}return n},_extends.apply(null,arguments)}function FireworkCanvas({autoMode,fireworkSpeed=50}){const canvasRef=React.useRef(null);const{width,height,getScreenSizeMultiplier}=useScreenSize();const lastFireTime=React.useRef(new Map);const autoModeManager=React.useMemo(()=>createAutoModeManager(getScreenSizeMultiplier),[getScreenSizeMultiplier]);React.useEffect(()=>{try{const canvas=canvasRef.current;if(canvas){canvas.width=width;canvas.height=height}}catch(error){reportError(error);throw error}},[width,height]);const createExplosion=useExplosion(getScreenSizeMultiplier);const launchFirework=useLaunchFirework(width,getScreenSizeMultiplier,lastFireTime);const{activeTouches,handlers}=useFireworkInteractions((x,y,identifier,specialType)=>{return launchFirework(x,y,identifier,specialType)});const launchAutoFirework=useAutoFirework(width,height,fireworkSpeed,launchFirework,autoModeManager);React.useEffect(()=>{try{const handleCustomLaunch=e=>{try{const{x,y,type}=e.detail;const rocket=launchFirework(x,y,"custom",type);if(rocket){setFireworks(prev=>[...prev,rocket])}}catch(error){reportError(error);throw error}};window.addEventListener("launchFirework",handleCustomLaunch);return()=>{window.removeEventListener("launchFirework",handleCustomLaunch)}}catch(error){reportError(error);throw error}},[launchFirework]);const[fireworks,setFireworks]=React.useState([]);const[particles,setParticles]=React.useState([]);const requestRef=React.useRef();const animate=React.useCallback(()=>{try{const canvas=canvasRef.current;if(!canvas)return;const ctx=canvas.getContext("2d");const renderer=createCanvasRenderer(ctx);renderer.clear(canvas.width,canvas.height);activeTouches.current.forEach(pos=>{const rocket=launchFirework(pos.x,pos.y,pos.identifier,pos.specialType);if(rocket){setFireworks(prev=>[...prev,rocket])}});if(autoMode){const autoRocket=launchAutoFirework();if(autoRocket){setFireworks(prev=>[...prev,autoRocket])}}setFireworks(prev=>prev.filter(firework=>{if(!firework.update()){const explosionResult=createExplosion(firework.x,firework.y,firework.color,firework.specialType);setParticles(prev=>[...prev,...explosionResult.immediate]);if(explosionResult.secondary&&explosionResult.secondary.length>0){explosionResult.secondary.forEach(promise=>{promise.then(result=>{if(result){const{rocket,targetX,targetY,flightTime}=result;setFireworks(prev=>[...prev,rocket]);setTimeout(()=>{const secondaryParticles=createExplosion(targetX,targetY,getRandomColor()).immediate;setParticles(prev=>[...prev,...secondaryParticles]);soundManager.playSound(true)},flightTime)}})})}soundManager.playSound(firework.specialType==="large"||firework.specialType==="extra-large");return false}renderer.renderRocket(firework);return true}));setParticles(prev=>prev.filter(particle=>{if(!particle.update())return false;renderer.renderParticle(particle);return true}));requestRef.current=requestAnimationFrame(animate)}catch(error){reportError(error);throw error}},[autoMode,launchFirework,launchAutoFirework,createExplosion]);React.useEffect(()=>{requestRef.current=requestAnimationFrame(animate);return()=>{if(requestRef.current){cancelAnimationFrame(requestRef.current)}}},[animate]);return React.createElement("canvas",_extends({"data-name":"firework-canvas",ref:canvasRef,className:"firework-canvas"},handlers))}</script>
1407
1408
1409    <!-- Main App -->
1410    
1410<script>function App(){const[showWelcome,setShowWelcome]=React.useState(true);const[autoMode,setAutoMode]=React.useState(false);const[fireworkSpeed,setFireworkSpeed]=React.useState(50);React.useEffect(()=>{try{const timer=setTimeout(()=>{setShowWelcome(false)},5000);return()=>clearTimeout(timer)}catch(error){reportError(error)}},[]);const handleAutoToggle=React.useCallback(()=>{try{setAutoMode(true)}catch(error){reportError(error)}},[]);const handleAutoClose=React.useCallback(()=>{try{setAutoMode(false)}catch(error){reportError(error)}},[]);const handleSpeedChange=React.useCallback(speed=>{try{setFireworkSpeed(speed)}catch(error){reportError(error)}},[]);const handleLaunch=React.useCallback((x,y,type)=>{try{const event=new CustomEvent("launchFirework",{detail:{x,y,type}});window.dispatchEvent(event)}catch(error){reportError(error)}},[]);return React.createElement("div",{"data-name":"app",className:"w-full h-screen"},React.createElement(FireworkCanvas,{autoMode:autoMode,fireworkSpeed:fireworkSpeed}),showWelcome&&React.createElement(WelcomeMessage,null),React.createElement("div",{"data-name":"controls-container",className:"fixed bottom-4 right-4 flex gap-4 items-center z-10"},React.createElement(GuideButton,null),React.createElement(SoundToggle,null),!autoMode&&React.createElement(AutoToggle,{isActive:autoMode,onToggle:handleAutoToggle})),React.createElement(TimePanel,{isVisible:autoMode,onSpeedChange:handleSpeedChange,onClose:handleAutoClose}),React.createElement(FireworkConsole,{onLaunch:handleLaunch}),React.createElement(CooldownIndicator,null),React.createElement(TrickleBadge,null))}const root=ReactDOM.createRoot(document.getElementById("root"));root.render(React.createElement(App,null));</script>
1410
1411    
1411<script>function createParticle(x,y,angle,speed,color,type="normal"){try{const config=particleConfig[type]||particleConfig.base;return{x,y,initialX:x,initialY:y,angle,speed,color,type,alpha:1,size:config.size,decay:config.decay,gravity:config.gravity,trailLength:0,glitterDelay:0,glitterCount:0,delay:0,initialSpeed:speed,fadeStartTime:config.fadeStartTime||30,minFallDistance:0,lifeTime:0,fallDistance:0,previousPositions:[],maxTrailLength:config.trailLength,trailDecay:config.trailDecay,lifeSpanMultiplier:1,update(){if(this.delay>0){this.delay--;this.x=-1000;this.y=-1000;return true}if(this.delay===0&&this.x===-1000){this.x=this.initialX;this.y=this.initialY}const prevY=this.y;this.speed*=config.speedDecay;this.lifeTime++;if(this.previousPositions.length>=this.maxTrailLength){this.previousPositions.pop()}this.previousPositions.unshift({x:this.x,y:this.y,alpha:this.alpha});this.x+=Math.cos(this.angle)*this.speed;this.y+=Math.sin(this.angle)*this.speed+this.gravity;this.angle+=config.angleChange*this.gravity;if(this.y>prevY){this.fallDistance+=this.y-prevY}this.previousPositions.forEach((pos,index)=>{pos.alpha*=this.trailDecay});const effectiveDecay=config.decay*(1/this.lifeSpanMultiplier);switch(this.type){case"super":this.alpha-=effectiveDecay;break;case"sparkle":if(this.delay===0){this.alpha-=effectiveDecay}break;case"glitter":if(this.glitterDelay>0){this.glitterDelay--;this.alpha=Math.min(1,this.alpha+0.1)}else if(this.lifeTime>=this.fadeStartTime&&this.fallDistance>=this.minFallDistance){this.alpha=Math.max(0,this.alpha-effectiveDecay)}break;case"flicker":if(Math.random()<config.flickerChance){this.alpha=Math.max(0,this.alpha+(Math.random()-0.5)*config.flickerRange)}this.alpha-=effectiveDecay;break;default:this.alpha-=effectiveDecay}return this.alpha>0}}}catch(error){reportError(error);throw error}}</script>
1411
1412    
1412<script type="text/babel">
1413</script>
1413
1414    
1414<script type="text/babel">// This file has been refactored into smaller modules under components/FireworkCanvas/
1415// Please see:
1416// - components/FireworkCanvas/useExplosion.js
1417// - components/FireworkCanvas/useLaunchFirework.js
1418// - components/FireworkCanvas/useAutoFirework.js
1419// - components/FireworkCanvas/useAnimation.js
1420// - components/FireworkCanvas/index.js
1421</script>
1421
1422    
1422<script type="text/babel">// This file has been refactored. Contents moved to:
1423// - components/SettingsPanelContent.js
1424// - components/containers/MobileContainer.js
1425</script>
1425
1426    
1426<script type="text/babel">// This file has been refactored. Contents moved to:
1427// - components/SettingsPanelContent.js
1428// - components/containers/DesktopContainer.js
1429</script>
1429
1430    
1430<script type="text/babel">// This file has been refactored. Please see:
1431// - utils/sound/audioPool.js for the audio pool implementation
1432// - utils/sound/index.js for the sound manager instance
1433</script>
1433
1434    
1434<script>function createAutoModeManager(getScreenSizeMultiplier){try{const burstModeRef={active:false,remainingCount:0,nextDelay:0,isLargeFirework:false};const updateBurstMode=()=>{try{const screenMultiplier=getScreenSizeMultiplier();if(Math.random()<0.01){burstModeRef.active=true;burstModeRef.remainingCount=1;burstModeRef.nextDelay=0;burstModeRef.isLargeFirework=true;burstModeRef.isExtraLarge=true;return}if(Math.random()<0.2*screenMultiplier&&!burstModeRef.active){const isLargeFireworkBurst=Math.random()<0.3;let baseBurstCount;if(isLargeFireworkBurst){baseBurstCount=Math.floor(Math.random()*2)+2}else{baseBurstCount=Math.random()<0.4?Math.floor(Math.random()*4)+6:Math.floor(Math.random()*3)+3}const burstCount=Math.floor(baseBurstCount*screenMultiplier);burstModeRef.active=true;burstModeRef.remainingCount=burstCount;burstModeRef.nextDelay=isLargeFireworkBurst?200+Math.random()*300:30+Math.random()*100;burstModeRef.isLargeFirework=isLargeFireworkBurst;burstModeRef.isExtraLarge=false}}catch(error){reportError(error)}};const getRandomInterval=(baseInterval,fireworkSpeed)=>{try{const screenMultiplier=getScreenSizeMultiplier();const distribution=Math.random();let multiplier;if(distribution<0.15){multiplier=0.1+Math.random()*0.2}else if(distribution<0.4){multiplier=0.3+Math.random()*0.3}else if(distribution<0.8){multiplier=0.6+Math.random()*0.4}else if(distribution<0.95){multiplier=1+Math.random()*0.5}else{multiplier=1.5+Math.random()*0.5}const speedMultiplier=Math.pow(fireworkSpeed/100,2);return baseInterval*multiplier/(screenMultiplier*speedMultiplier)}catch(error){reportError(error);return baseInterval}};return{burstModeRef,updateBurstMode,getRandomInterval}}catch(error){reportError(error);return{burstModeRef:{active:false,remainingCount:0,nextDelay:0,isLargeFirework:false},updateBurstMode:()=>{},getRandomInterval:()=>3000}}}</script>
1434
1435    
1435<script type="text/babel">// This file has been refactored. Please see:
1436// - hooks/useFireworkInteractions/constants.js for constants
1437// - hooks/useFireworkInteractions/utils.js for utility functions
1438// - hooks/useFireworkInteractions/keyboard.js for keyboard interactions
1439// - hooks/useFireworkInteractions/index.js for the main hook
1440</script>
1440
1441    
1441<script>function createSparkParticle(x,y,angle,speed,color){try{return{x,y,angle,speed,color,alpha:1,size:1,decay:0.02,gravity:0.1,update(){this.speed*=0.95;this.x+=Math.cos(this.angle)*this.speed;this.y+=Math.sin(this.angle)*this.speed+this.gravity;this.alpha-=this.decay;return this.alpha>0}}}catch(error){reportError(error);return null}}</script>
1441
1442
1443
1444</body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.