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&¤tMinute===0&¤tSecond===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.