Repository: DavidHDev/react-bits Branch: main Commit: 273184207197 Files: 1808 Total size: 25.8 MB Directory structure: gitextract_zr0nlso3/ ├── .assetsignore ├── .context/ │ └── new-component.md ├── .eslintrc.cjs ├── .github/ │ ├── FUNDING.yml │ └── ISSUE_TEMPLATE/ │ ├── 1-bug-report.yml │ └── 2-feature-request.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── index.html ├── jsrepo.config.ts ├── package.json ├── public/ │ ├── README.md │ ├── assets/ │ │ ├── 3d/ │ │ │ ├── bar.glb │ │ │ ├── cube.glb │ │ │ └── lens.glb │ │ └── video/ │ │ ├── animatedcontent.webm │ │ ├── animatedlist.webm │ │ ├── antigravity.webm │ │ ├── asciitext.webm │ │ ├── aurora.webm │ │ ├── balatro.webm │ │ ├── ballpit.webm │ │ ├── beams.webm │ │ ├── blobcursor.webm │ │ ├── blurtext.webm │ │ ├── borderglow.webm │ │ ├── bouncecards.webm │ │ ├── bubblemenu.webm │ │ ├── cardnav.webm │ │ ├── cardswap.webm │ │ ├── carousel.webm │ │ ├── chromagrid.webm │ │ ├── circulargallery.webm │ │ ├── circulartext.webm │ │ ├── clickspark.webm │ │ ├── colorbends.webm │ │ ├── counter.webm │ │ ├── countup.webm │ │ ├── crosshair.webm │ │ ├── cubes.webm │ │ ├── curvedloop.webm │ │ ├── darkveil.webm │ │ ├── decaycard.webm │ │ ├── decryptedtext.webm │ │ ├── dither.webm │ │ ├── dock.webm │ │ ├── domegallery.webm │ │ ├── dotgrid.webm │ │ ├── elasticslider.webm │ │ ├── electricborder.webm │ │ ├── evileye.webm │ │ ├── fadecontent.webm │ │ ├── fallingtext.webm │ │ ├── faultyterminal.webm │ │ ├── floatinglines.webm │ │ ├── flowingmenu.webm │ │ ├── fluidglass.webm │ │ ├── flyingposters.webm │ │ ├── folder.webm │ │ ├── fuzzytext.webm │ │ ├── galaxy.webm │ │ ├── ghostcursor.webm │ │ ├── glarehover.webm │ │ ├── glassicons.webm │ │ ├── glasssurface.webm │ │ ├── glitchtext.webm │ │ ├── gooeynav.webm │ │ ├── gradientblinds.webm │ │ ├── gradienttext.webm │ │ ├── gradualblur.webm │ │ ├── grainient.webm │ │ ├── griddistortion.webm │ │ ├── gridmotion.webm │ │ ├── gridscan.webm │ │ ├── hyperspeed.webm │ │ ├── imagetrail.webm │ │ ├── infinitemenu.webm │ │ ├── infinitescroll.webm │ │ ├── iridescence.webm │ │ ├── lanyard.webm │ │ ├── laserflow.webm │ │ ├── letterglitch.webm │ │ ├── lightning.webm │ │ ├── lightpillar.webm │ │ ├── lightrays.webm │ │ ├── linewaves.webm │ │ ├── liquidchrome.webm │ │ ├── liquidether.webm │ │ ├── logoloop.webm │ │ ├── magicbento.webm │ │ ├── magicrings.webm │ │ ├── magnet.webm │ │ ├── magnetlines.webm │ │ ├── masonry.webm │ │ ├── metaballs.webm │ │ ├── metallicpaint.webm │ │ ├── modelviewer.webm │ │ ├── noise.webm │ │ ├── orb.webm │ │ ├── orbitimages.webm │ │ ├── particles.webm │ │ ├── pillnav.webm │ │ ├── pixelblast.webm │ │ ├── pixelcard.webm │ │ ├── pixelsnow.webm │ │ ├── pixeltrail.webm │ │ ├── pixeltransition.webm │ │ ├── plasma.webm │ │ ├── prism.webm │ │ ├── prismaticburst.webm │ │ ├── profilecard.webm │ │ ├── radar.webm │ │ ├── reflectivecard.webm │ │ ├── ribbons.webm │ │ ├── ripplegrid.webm │ │ ├── rotatingtext.webm │ │ ├── scrambledtext.webm │ │ ├── scrollfloat.webm │ │ ├── scrollreveal.webm │ │ ├── scrollstack.webm │ │ ├── scrollvelocity.webm │ │ ├── shapeblur.webm │ │ ├── shinytext.webm │ │ ├── shuffle.webm │ │ ├── silk.webm │ │ ├── softaurora.webm │ │ ├── splashcursor.webm │ │ ├── splittext.webm │ │ ├── spotlightcard.webm │ │ ├── squares.webm │ │ ├── stack.webm │ │ ├── staggeredmenu.webm │ │ ├── starborder.webm │ │ ├── stepper.webm │ │ ├── stickerpeel.webm │ │ ├── targetcursor.webm │ │ ├── textcursor.webm │ │ ├── textpressure.webm │ │ ├── textrotate.webm │ │ ├── texttype.webm │ │ ├── threads.webm │ │ ├── tiltedcard.webm │ │ ├── truefocus.webm │ │ ├── variableproximity.webm │ │ └── waves.webm │ ├── llms.txt │ ├── r/ │ │ ├── ASCIIText-JS-CSS.json │ │ ├── ASCIIText-JS-TW.json │ │ ├── ASCIIText-TS-CSS.json │ │ ├── ASCIIText-TS-TW.json │ │ ├── AnimatedContent-JS-CSS.json │ │ ├── AnimatedContent-JS-TW.json │ │ ├── AnimatedContent-TS-CSS.json │ │ ├── AnimatedContent-TS-TW.json │ │ ├── AnimatedList-JS-CSS.json │ │ ├── AnimatedList-JS-TW.json │ │ ├── AnimatedList-TS-CSS.json │ │ ├── AnimatedList-TS-TW.json │ │ ├── Antigravity-JS-CSS.json │ │ ├── Antigravity-JS-TW.json │ │ ├── Antigravity-TS-CSS.json │ │ ├── Antigravity-TS-TW.json │ │ ├── Aurora-JS-CSS.json │ │ ├── Aurora-JS-TW.json │ │ ├── Aurora-TS-CSS.json │ │ ├── Aurora-TS-TW.json │ │ ├── Balatro-JS-CSS.json │ │ ├── Balatro-JS-TW.json │ │ ├── Balatro-TS-CSS.json │ │ ├── Balatro-TS-TW.json │ │ ├── Ballpit-JS-CSS.json │ │ ├── Ballpit-JS-TW.json │ │ ├── Ballpit-TS-CSS.json │ │ ├── Ballpit-TS-TW.json │ │ ├── Beams-JS-CSS.json │ │ ├── Beams-JS-TW.json │ │ ├── Beams-TS-CSS.json │ │ ├── Beams-TS-TW.json │ │ ├── BlobCursor-JS-CSS.json │ │ ├── BlobCursor-JS-TW.json │ │ ├── BlobCursor-TS-CSS.json │ │ ├── BlobCursor-TS-TW.json │ │ ├── BlurText-JS-CSS.json │ │ ├── BlurText-JS-TW.json │ │ ├── BlurText-TS-CSS.json │ │ ├── BlurText-TS-TW.json │ │ ├── BorderGlow-JS-CSS.json │ │ ├── BorderGlow-JS-TW.json │ │ ├── BorderGlow-TS-CSS.json │ │ ├── BorderGlow-TS-TW.json │ │ ├── BounceCards-JS-CSS.json │ │ ├── BounceCards-JS-TW.json │ │ ├── BounceCards-TS-CSS.json │ │ ├── BounceCards-TS-TW.json │ │ ├── BubbleMenu-JS-CSS.json │ │ ├── BubbleMenu-JS-TW.json │ │ ├── BubbleMenu-TS-CSS.json │ │ ├── BubbleMenu-TS-TW.json │ │ ├── CardNav-JS-CSS.json │ │ ├── CardNav-JS-TW.json │ │ ├── CardNav-TS-CSS.json │ │ ├── CardNav-TS-TW.json │ │ ├── CardSwap-JS-CSS.json │ │ ├── CardSwap-JS-TW.json │ │ ├── CardSwap-TS-CSS.json │ │ ├── CardSwap-TS-TW.json │ │ ├── Carousel-JS-CSS.json │ │ ├── Carousel-JS-TW.json │ │ ├── Carousel-TS-CSS.json │ │ ├── Carousel-TS-TW.json │ │ ├── ChromaGrid-JS-CSS.json │ │ ├── ChromaGrid-JS-TW.json │ │ ├── ChromaGrid-TS-CSS.json │ │ ├── ChromaGrid-TS-TW.json │ │ ├── CircularGallery-JS-CSS.json │ │ ├── CircularGallery-JS-TW.json │ │ ├── CircularGallery-TS-CSS.json │ │ ├── CircularGallery-TS-TW.json │ │ ├── CircularText-JS-CSS.json │ │ ├── CircularText-JS-TW.json │ │ ├── CircularText-TS-CSS.json │ │ ├── CircularText-TS-TW.json │ │ ├── ClickSpark-JS-CSS.json │ │ ├── ClickSpark-JS-TW.json │ │ ├── ClickSpark-TS-CSS.json │ │ ├── ClickSpark-TS-TW.json │ │ ├── ColorBends-JS-CSS.json │ │ ├── ColorBends-JS-TW.json │ │ ├── ColorBends-TS-CSS.json │ │ ├── ColorBends-TS-TW.json │ │ ├── CountUp-JS-CSS.json │ │ ├── CountUp-JS-TW.json │ │ ├── CountUp-TS-CSS.json │ │ ├── CountUp-TS-TW.json │ │ ├── Counter-JS-CSS.json │ │ ├── Counter-JS-TW.json │ │ ├── Counter-TS-CSS.json │ │ ├── Counter-TS-TW.json │ │ ├── Crosshair-JS-CSS.json │ │ ├── Crosshair-JS-TW.json │ │ ├── Crosshair-TS-CSS.json │ │ ├── Crosshair-TS-TW.json │ │ ├── Cubes-JS-CSS.json │ │ ├── Cubes-JS-TW.json │ │ ├── Cubes-TS-CSS.json │ │ ├── Cubes-TS-TW.json │ │ ├── CurvedLoop-JS-CSS.json │ │ ├── CurvedLoop-JS-TW.json │ │ ├── CurvedLoop-TS-CSS.json │ │ ├── CurvedLoop-TS-TW.json │ │ ├── DarkVeil-JS-CSS.json │ │ ├── DarkVeil-JS-TW.json │ │ ├── DarkVeil-TS-CSS.json │ │ ├── DarkVeil-TS-TW.json │ │ ├── DecayCard-JS-CSS.json │ │ ├── DecayCard-JS-TW.json │ │ ├── DecayCard-TS-CSS.json │ │ ├── DecayCard-TS-TW.json │ │ ├── DecryptedText-JS-CSS.json │ │ ├── DecryptedText-JS-TW.json │ │ ├── DecryptedText-TS-CSS.json │ │ ├── DecryptedText-TS-TW.json │ │ ├── Dither-JS-CSS.json │ │ ├── Dither-JS-TW.json │ │ ├── Dither-TS-CSS.json │ │ ├── Dither-TS-TW.json │ │ ├── Dock-JS-CSS.json │ │ ├── Dock-JS-TW.json │ │ ├── Dock-TS-CSS.json │ │ ├── Dock-TS-TW.json │ │ ├── DomeGallery-JS-CSS.json │ │ ├── DomeGallery-JS-TW.json │ │ ├── DomeGallery-TS-CSS.json │ │ ├── DomeGallery-TS-TW.json │ │ ├── DotGrid-JS-CSS.json │ │ ├── DotGrid-JS-TW.json │ │ ├── DotGrid-TS-CSS.json │ │ ├── DotGrid-TS-TW.json │ │ ├── ElasticSlider-JS-CSS.json │ │ ├── ElasticSlider-JS-TW.json │ │ ├── ElasticSlider-TS-CSS.json │ │ ├── ElasticSlider-TS-TW.json │ │ ├── ElectricBorder-JS-CSS.json │ │ ├── ElectricBorder-JS-TW.json │ │ ├── ElectricBorder-TS-CSS.json │ │ ├── ElectricBorder-TS-TW.json │ │ ├── EvilEye-JS-CSS.json │ │ ├── EvilEye-JS-TW.json │ │ ├── EvilEye-TS-CSS.json │ │ ├── EvilEye-TS-TW.json │ │ ├── FadeContent-JS-CSS.json │ │ ├── FadeContent-JS-TW.json │ │ ├── FadeContent-TS-CSS.json │ │ ├── FadeContent-TS-TW.json │ │ ├── FallingText-JS-CSS.json │ │ ├── FallingText-JS-TW.json │ │ ├── FallingText-TS-CSS.json │ │ ├── FallingText-TS-TW.json │ │ ├── FaultyTerminal-JS-CSS.json │ │ ├── FaultyTerminal-JS-TW.json │ │ ├── FaultyTerminal-TS-CSS.json │ │ ├── FaultyTerminal-TS-TW.json │ │ ├── FloatingLines-JS-CSS.json │ │ ├── FloatingLines-JS-TW.json │ │ ├── FloatingLines-TS-CSS.json │ │ ├── FloatingLines-TS-TW.json │ │ ├── FlowingMenu-JS-CSS.json │ │ ├── FlowingMenu-JS-TW.json │ │ ├── FlowingMenu-TS-CSS.json │ │ ├── FlowingMenu-TS-TW.json │ │ ├── FluidGlass-JS-CSS.json │ │ ├── FluidGlass-JS-TW.json │ │ ├── FluidGlass-TS-CSS.json │ │ ├── FluidGlass-TS-TW.json │ │ ├── FlyingPosters-JS-CSS.json │ │ ├── FlyingPosters-JS-TW.json │ │ ├── FlyingPosters-TS-CSS.json │ │ ├── FlyingPosters-TS-TW.json │ │ ├── Folder-JS-CSS.json │ │ ├── Folder-JS-TW.json │ │ ├── Folder-TS-CSS.json │ │ ├── Folder-TS-TW.json │ │ ├── FuzzyText-JS-CSS.json │ │ ├── FuzzyText-JS-TW.json │ │ ├── FuzzyText-TS-CSS.json │ │ ├── FuzzyText-TS-TW.json │ │ ├── Galaxy-JS-CSS.json │ │ ├── Galaxy-JS-TW.json │ │ ├── Galaxy-TS-CSS.json │ │ ├── Galaxy-TS-TW.json │ │ ├── GhostCursor-JS-CSS.json │ │ ├── GhostCursor-JS-TW.json │ │ ├── GhostCursor-TS-CSS.json │ │ ├── GhostCursor-TS-TW.json │ │ ├── GlareHover-JS-CSS.json │ │ ├── GlareHover-JS-TW.json │ │ ├── GlareHover-TS-CSS.json │ │ ├── GlareHover-TS-TW.json │ │ ├── GlassIcons-JS-CSS.json │ │ ├── GlassIcons-JS-TW.json │ │ ├── GlassIcons-TS-CSS.json │ │ ├── GlassIcons-TS-TW.json │ │ ├── GlassSurface-JS-CSS.json │ │ ├── GlassSurface-JS-TW.json │ │ ├── GlassSurface-TS-CSS.json │ │ ├── GlassSurface-TS-TW.json │ │ ├── GlitchText-JS-CSS.json │ │ ├── GlitchText-JS-TW.json │ │ ├── GlitchText-TS-CSS.json │ │ ├── GlitchText-TS-TW.json │ │ ├── GooeyNav-JS-CSS.json │ │ ├── GooeyNav-JS-TW.json │ │ ├── GooeyNav-TS-CSS.json │ │ ├── GooeyNav-TS-TW.json │ │ ├── GradientBlinds-JS-CSS.json │ │ ├── GradientBlinds-JS-TW.json │ │ ├── GradientBlinds-TS-CSS.json │ │ ├── GradientBlinds-TS-TW.json │ │ ├── GradientText-JS-CSS.json │ │ ├── GradientText-JS-TW.json │ │ ├── GradientText-TS-CSS.json │ │ ├── GradientText-TS-TW.json │ │ ├── GradualBlur-JS-CSS.json │ │ ├── GradualBlur-JS-TW.json │ │ ├── GradualBlur-TS-CSS.json │ │ ├── GradualBlur-TS-TW.json │ │ ├── Grainient-JS-CSS.json │ │ ├── Grainient-JS-TW.json │ │ ├── Grainient-TS-CSS.json │ │ ├── Grainient-TS-TW.json │ │ ├── GridDistortion-JS-CSS.json │ │ ├── GridDistortion-JS-TW.json │ │ ├── GridDistortion-TS-CSS.json │ │ ├── GridDistortion-TS-TW.json │ │ ├── GridMotion-JS-CSS.json │ │ ├── GridMotion-JS-TW.json │ │ ├── GridMotion-TS-CSS.json │ │ ├── GridMotion-TS-TW.json │ │ ├── GridScan-JS-CSS.json │ │ ├── GridScan-JS-TW.json │ │ ├── GridScan-TS-CSS.json │ │ ├── GridScan-TS-TW.json │ │ ├── Hyperspeed-JS-CSS.json │ │ ├── Hyperspeed-JS-TW.json │ │ ├── Hyperspeed-TS-CSS.json │ │ ├── Hyperspeed-TS-TW.json │ │ ├── ImageTrail-JS-CSS.json │ │ ├── ImageTrail-JS-TW.json │ │ ├── ImageTrail-TS-CSS.json │ │ ├── ImageTrail-TS-TW.json │ │ ├── InfiniteMenu-JS-CSS.json │ │ ├── InfiniteMenu-JS-TW.json │ │ ├── InfiniteMenu-TS-CSS.json │ │ ├── InfiniteMenu-TS-TW.json │ │ ├── Iridescence-JS-CSS.json │ │ ├── Iridescence-JS-TW.json │ │ ├── Iridescence-TS-CSS.json │ │ ├── Iridescence-TS-TW.json │ │ ├── Lanyard-JS-CSS.json │ │ ├── Lanyard-JS-TW.json │ │ ├── Lanyard-TS-CSS.json │ │ ├── Lanyard-TS-TW.json │ │ ├── LaserFlow-JS-CSS.json │ │ ├── LaserFlow-JS-TW.json │ │ ├── LaserFlow-TS-CSS.json │ │ ├── LaserFlow-TS-TW.json │ │ ├── LetterGlitch-JS-CSS.json │ │ ├── LetterGlitch-JS-TW.json │ │ ├── LetterGlitch-TS-CSS.json │ │ ├── LetterGlitch-TS-TW.json │ │ ├── LightPillar-JS-CSS.json │ │ ├── LightPillar-JS-TW.json │ │ ├── LightPillar-TS-CSS.json │ │ ├── LightPillar-TS-TW.json │ │ ├── LightRays-JS-CSS.json │ │ ├── LightRays-JS-TW.json │ │ ├── LightRays-TS-CSS.json │ │ ├── LightRays-TS-TW.json │ │ ├── Lightning-JS-CSS.json │ │ ├── Lightning-JS-TW.json │ │ ├── Lightning-TS-CSS.json │ │ ├── Lightning-TS-TW.json │ │ ├── LineWaves-JS-CSS.json │ │ ├── LineWaves-JS-TW.json │ │ ├── LineWaves-TS-CSS.json │ │ ├── LineWaves-TS-TW.json │ │ ├── LiquidChrome-JS-CSS.json │ │ ├── LiquidChrome-JS-TW.json │ │ ├── LiquidChrome-TS-CSS.json │ │ ├── LiquidChrome-TS-TW.json │ │ ├── LiquidEther-JS-CSS.json │ │ ├── LiquidEther-JS-TW.json │ │ ├── LiquidEther-TS-CSS.json │ │ ├── LiquidEther-TS-TW.json │ │ ├── LogoLoop-JS-CSS.json │ │ ├── LogoLoop-JS-TW.json │ │ ├── LogoLoop-TS-CSS.json │ │ ├── LogoLoop-TS-TW.json │ │ ├── MagicBento-JS-CSS.json │ │ ├── MagicBento-JS-TW.json │ │ ├── MagicBento-TS-CSS.json │ │ ├── MagicBento-TS-TW.json │ │ ├── MagicRings-JS-CSS.json │ │ ├── MagicRings-JS-TW.json │ │ ├── MagicRings-TS-CSS.json │ │ ├── MagicRings-TS-TW.json │ │ ├── Magnet-JS-CSS.json │ │ ├── Magnet-JS-TW.json │ │ ├── Magnet-TS-CSS.json │ │ ├── Magnet-TS-TW.json │ │ ├── MagnetLines-JS-CSS.json │ │ ├── MagnetLines-JS-TW.json │ │ ├── MagnetLines-TS-CSS.json │ │ ├── MagnetLines-TS-TW.json │ │ ├── Masonry-JS-CSS.json │ │ ├── Masonry-JS-TW.json │ │ ├── Masonry-TS-CSS.json │ │ ├── Masonry-TS-TW.json │ │ ├── MetaBalls-JS-CSS.json │ │ ├── MetaBalls-JS-TW.json │ │ ├── MetaBalls-TS-CSS.json │ │ ├── MetaBalls-TS-TW.json │ │ ├── MetallicPaint-JS-CSS.json │ │ ├── MetallicPaint-JS-TW.json │ │ ├── MetallicPaint-TS-CSS.json │ │ ├── MetallicPaint-TS-TW.json │ │ ├── ModelViewer-JS-CSS.json │ │ ├── ModelViewer-JS-TW.json │ │ ├── ModelViewer-TS-CSS.json │ │ ├── ModelViewer-TS-TW.json │ │ ├── Noise-JS-CSS.json │ │ ├── Noise-JS-TW.json │ │ ├── Noise-TS-CSS.json │ │ ├── Noise-TS-TW.json │ │ ├── Orb-JS-CSS.json │ │ ├── Orb-JS-TW.json │ │ ├── Orb-TS-CSS.json │ │ ├── Orb-TS-TW.json │ │ ├── OrbitImages-JS-CSS.json │ │ ├── OrbitImages-JS-TW.json │ │ ├── OrbitImages-TS-CSS.json │ │ ├── OrbitImages-TS-TW.json │ │ ├── Particles-JS-CSS.json │ │ ├── Particles-JS-TW.json │ │ ├── Particles-TS-CSS.json │ │ ├── Particles-TS-TW.json │ │ ├── PillNav-JS-CSS.json │ │ ├── PillNav-JS-TW.json │ │ ├── PillNav-TS-CSS.json │ │ ├── PillNav-TS-TW.json │ │ ├── PixelBlast-JS-CSS.json │ │ ├── PixelBlast-JS-TW.json │ │ ├── PixelBlast-TS-CSS.json │ │ ├── PixelBlast-TS-TW.json │ │ ├── PixelCard-JS-CSS.json │ │ ├── PixelCard-JS-TW.json │ │ ├── PixelCard-TS-CSS.json │ │ ├── PixelCard-TS-TW.json │ │ ├── PixelSnow-JS-CSS.json │ │ ├── PixelSnow-JS-TW.json │ │ ├── PixelSnow-TS-CSS.json │ │ ├── PixelSnow-TS-TW.json │ │ ├── PixelTrail-JS-CSS.json │ │ ├── PixelTrail-JS-TW.json │ │ ├── PixelTrail-TS-CSS.json │ │ ├── PixelTrail-TS-TW.json │ │ ├── PixelTransition-JS-CSS.json │ │ ├── PixelTransition-JS-TW.json │ │ ├── PixelTransition-TS-CSS.json │ │ ├── PixelTransition-TS-TW.json │ │ ├── Plasma-JS-CSS.json │ │ ├── Plasma-JS-TW.json │ │ ├── Plasma-TS-CSS.json │ │ ├── Plasma-TS-TW.json │ │ ├── Prism-JS-CSS.json │ │ ├── Prism-JS-TW.json │ │ ├── Prism-TS-CSS.json │ │ ├── Prism-TS-TW.json │ │ ├── PrismaticBurst-JS-CSS.json │ │ ├── PrismaticBurst-JS-TW.json │ │ ├── PrismaticBurst-TS-CSS.json │ │ ├── PrismaticBurst-TS-TW.json │ │ ├── ProfileCard-JS-CSS.json │ │ ├── ProfileCard-JS-TW.json │ │ ├── ProfileCard-TS-CSS.json │ │ ├── ProfileCard-TS-TW.json │ │ ├── Radar-JS-CSS.json │ │ ├── Radar-JS-TW.json │ │ ├── Radar-TS-CSS.json │ │ ├── Radar-TS-TW.json │ │ ├── ReflectiveCard-JS-CSS.json │ │ ├── ReflectiveCard-JS-TW.json │ │ ├── ReflectiveCard-TS-CSS.json │ │ ├── ReflectiveCard-TS-TW.json │ │ ├── Ribbons-JS-CSS.json │ │ ├── Ribbons-JS-TW.json │ │ ├── Ribbons-TS-CSS.json │ │ ├── Ribbons-TS-TW.json │ │ ├── RippleGrid-JS-CSS.json │ │ ├── RippleGrid-JS-TW.json │ │ ├── RippleGrid-TS-CSS.json │ │ ├── RippleGrid-TS-TW.json │ │ ├── RotatingText-JS-CSS.json │ │ ├── RotatingText-JS-TW.json │ │ ├── RotatingText-TS-CSS.json │ │ ├── RotatingText-TS-TW.json │ │ ├── ScrambledText-JS-CSS.json │ │ ├── ScrambledText-JS-TW.json │ │ ├── ScrambledText-TS-CSS.json │ │ ├── ScrambledText-TS-TW.json │ │ ├── ScrollFloat-JS-CSS.json │ │ ├── ScrollFloat-JS-TW.json │ │ ├── ScrollFloat-TS-CSS.json │ │ ├── ScrollFloat-TS-TW.json │ │ ├── ScrollReveal-JS-CSS.json │ │ ├── ScrollReveal-JS-TW.json │ │ ├── ScrollReveal-TS-CSS.json │ │ ├── ScrollReveal-TS-TW.json │ │ ├── ScrollStack-JS-CSS.json │ │ ├── ScrollStack-JS-TW.json │ │ ├── ScrollStack-TS-CSS.json │ │ ├── ScrollStack-TS-TW.json │ │ ├── ScrollVelocity-JS-CSS.json │ │ ├── ScrollVelocity-JS-TW.json │ │ ├── ScrollVelocity-TS-CSS.json │ │ ├── ScrollVelocity-TS-TW.json │ │ ├── ShapeBlur-JS-CSS.json │ │ ├── ShapeBlur-JS-TW.json │ │ ├── ShapeBlur-TS-CSS.json │ │ ├── ShapeBlur-TS-TW.json │ │ ├── ShapeGrid-JS-CSS.json │ │ ├── ShapeGrid-JS-TW.json │ │ ├── ShapeGrid-TS-CSS.json │ │ ├── ShapeGrid-TS-TW.json │ │ ├── ShinyText-JS-CSS.json │ │ ├── ShinyText-JS-TW.json │ │ ├── ShinyText-TS-CSS.json │ │ ├── ShinyText-TS-TW.json │ │ ├── Shuffle-JS-CSS.json │ │ ├── Shuffle-JS-TW.json │ │ ├── Shuffle-TS-CSS.json │ │ ├── Shuffle-TS-TW.json │ │ ├── Silk-JS-CSS.json │ │ ├── Silk-JS-TW.json │ │ ├── Silk-TS-CSS.json │ │ ├── Silk-TS-TW.json │ │ ├── SoftAurora-JS-CSS.json │ │ ├── SoftAurora-JS-TW.json │ │ ├── SoftAurora-TS-CSS.json │ │ ├── SoftAurora-TS-TW.json │ │ ├── SplashCursor-JS-CSS.json │ │ ├── SplashCursor-JS-TW.json │ │ ├── SplashCursor-TS-CSS.json │ │ ├── SplashCursor-TS-TW.json │ │ ├── SplitText-JS-CSS.json │ │ ├── SplitText-JS-TW.json │ │ ├── SplitText-TS-CSS.json │ │ ├── SplitText-TS-TW.json │ │ ├── SpotlightCard-JS-CSS.json │ │ ├── SpotlightCard-JS-TW.json │ │ ├── SpotlightCard-TS-CSS.json │ │ ├── SpotlightCard-TS-TW.json │ │ ├── Stack-JS-CSS.json │ │ ├── Stack-JS-TW.json │ │ ├── Stack-TS-CSS.json │ │ ├── Stack-TS-TW.json │ │ ├── StaggeredMenu-JS-CSS.json │ │ ├── StaggeredMenu-JS-TW.json │ │ ├── StaggeredMenu-TS-CSS.json │ │ ├── StaggeredMenu-TS-TW.json │ │ ├── StarBorder-JS-CSS.json │ │ ├── StarBorder-JS-TW.json │ │ ├── StarBorder-TS-CSS.json │ │ ├── StarBorder-TS-TW.json │ │ ├── Stepper-JS-CSS.json │ │ ├── Stepper-JS-TW.json │ │ ├── Stepper-TS-CSS.json │ │ ├── Stepper-TS-TW.json │ │ ├── StickerPeel-JS-CSS.json │ │ ├── StickerPeel-JS-TW.json │ │ ├── StickerPeel-TS-CSS.json │ │ ├── StickerPeel-TS-TW.json │ │ ├── TargetCursor-JS-CSS.json │ │ ├── TargetCursor-JS-TW.json │ │ ├── TargetCursor-TS-CSS.json │ │ ├── TargetCursor-TS-TW.json │ │ ├── TextCursor-JS-CSS.json │ │ ├── TextCursor-JS-TW.json │ │ ├── TextCursor-TS-CSS.json │ │ ├── TextCursor-TS-TW.json │ │ ├── TextPressure-JS-CSS.json │ │ ├── TextPressure-JS-TW.json │ │ ├── TextPressure-TS-CSS.json │ │ ├── TextPressure-TS-TW.json │ │ ├── TextType-JS-CSS.json │ │ ├── TextType-JS-TW.json │ │ ├── TextType-TS-CSS.json │ │ ├── TextType-TS-TW.json │ │ ├── Threads-JS-CSS.json │ │ ├── Threads-JS-TW.json │ │ ├── Threads-TS-CSS.json │ │ ├── Threads-TS-TW.json │ │ ├── TiltedCard-JS-CSS.json │ │ ├── TiltedCard-JS-TW.json │ │ ├── TiltedCard-TS-CSS.json │ │ ├── TiltedCard-TS-TW.json │ │ ├── TrueFocus-JS-CSS.json │ │ ├── TrueFocus-JS-TW.json │ │ ├── TrueFocus-TS-CSS.json │ │ ├── TrueFocus-TS-TW.json │ │ ├── VariableProximity-JS-CSS.json │ │ ├── VariableProximity-JS-TW.json │ │ ├── VariableProximity-TS-CSS.json │ │ ├── VariableProximity-TS-TW.json │ │ ├── Waves-JS-CSS.json │ │ ├── Waves-JS-TW.json │ │ ├── Waves-TS-CSS.json │ │ ├── Waves-TS-TW.json │ │ └── registry.json │ ├── robots.txt │ ├── site.webmanifest │ └── sitemap.xml ├── scripts/ │ ├── generateComponent.js │ ├── generateLlmsText.js │ └── generateSitemap.js ├── src/ │ ├── App.jsx │ ├── assets/ │ │ └── lanyard/ │ │ └── card.glb │ ├── components/ │ │ ├── code/ │ │ │ ├── CliInstallation.jsx │ │ │ ├── CodeExample.jsx │ │ │ ├── CodeHighlighter.jsx │ │ │ ├── CodeOptions.jsx │ │ │ ├── Dependencies.jsx │ │ │ └── IconSelect.jsx │ │ ├── common/ │ │ │ ├── Announcement.jsx │ │ │ ├── AnnouncementBar/ │ │ │ │ ├── AnnouncementBar.css │ │ │ │ ├── AnnouncementBar.jsx │ │ │ │ └── index.js │ │ │ ├── AnnouncementModal/ │ │ │ │ ├── AnnouncementModal.css │ │ │ │ └── AnnouncementModal.jsx │ │ │ ├── BackToTopButton.jsx │ │ │ ├── ComponentList.jsx │ │ │ ├── ContributionSection.jsx │ │ │ ├── GitHub/ │ │ │ │ └── ContributionSection.jsx │ │ │ ├── Misc/ │ │ │ │ ├── Announcement.jsx │ │ │ │ ├── BackToTopButton.jsx │ │ │ │ ├── SearchDialog.jsx │ │ │ │ └── SkeletonLoader.jsx │ │ │ ├── Preview/ │ │ │ │ ├── BackgroundContent.jsx │ │ │ │ ├── Customize.jsx │ │ │ │ ├── OpenInStudioButton.jsx │ │ │ │ ├── PreviewColorPicker.jsx │ │ │ │ ├── PreviewInput.jsx │ │ │ │ ├── PreviewSelect.jsx │ │ │ │ ├── PreviewSlider.jsx │ │ │ │ ├── PreviewSliderVertical.jsx │ │ │ │ ├── PreviewSwitch.jsx │ │ │ │ ├── PropTable.jsx │ │ │ │ ├── RefreshButton.jsx │ │ │ │ └── ResetPropsButton.jsx │ │ │ ├── ProCard.jsx │ │ │ ├── SVGComponents.jsx │ │ │ ├── SearchDialog.jsx │ │ │ ├── SimpleMarquee.jsx │ │ │ ├── SkeletonLoader.jsx │ │ │ ├── SponsorsCard.jsx │ │ │ ├── SponsorsCircle.jsx │ │ │ ├── TabsFooter.jsx │ │ │ └── TabsLayout.jsx │ │ ├── context/ │ │ │ ├── ActiveRouteContext/ │ │ │ │ └── ActiveRouteContext.jsx │ │ │ ├── ComponentPropsContext.jsx │ │ │ ├── InstallationContext/ │ │ │ │ └── InstallationContext.jsx │ │ │ ├── OptionsContext/ │ │ │ │ ├── OptionsContext.jsx │ │ │ │ └── useOptions.js │ │ │ ├── SearchContext/ │ │ │ │ ├── SearchContext.jsx │ │ │ │ └── useSearch.js │ │ │ └── TransitionContext/ │ │ │ └── TransitionContext.jsx │ │ ├── landing/ │ │ │ ├── DisplayHeader/ │ │ │ │ ├── DisplayHeader.css │ │ │ │ └── DisplayHeader.jsx │ │ │ ├── FeatureCards/ │ │ │ │ ├── FeatureCards.css │ │ │ │ └── FeatureCards.jsx │ │ │ ├── Footer/ │ │ │ │ ├── Footer.css │ │ │ │ └── Footer.jsx │ │ │ ├── Hero/ │ │ │ │ └── Hero.jsx │ │ │ ├── PlasmaWave/ │ │ │ │ └── PlasmaWaveV2.jsx │ │ │ ├── Sponsors/ │ │ │ │ ├── Sponsors.css │ │ │ │ └── Sponsors.jsx │ │ │ ├── StartBuilding/ │ │ │ │ ├── StartBuilding.css │ │ │ │ └── StartBuilding.jsx │ │ │ ├── Testimonials/ │ │ │ │ ├── Testimonials.css │ │ │ │ └── Testimonials.jsx │ │ │ └── ToolsShowcase/ │ │ │ ├── ToolsShowcase.css │ │ │ └── ToolsShowcase.jsx │ │ ├── layout/ │ │ │ ├── Providers.jsx │ │ │ └── SidebarLayout.jsx │ │ ├── navs/ │ │ │ ├── Header.jsx │ │ │ └── Sidebar.jsx │ │ └── setup/ │ │ ├── color-mode.jsx │ │ ├── provider.jsx │ │ ├── toaster.jsx │ │ └── tooltip.jsx │ ├── constants/ │ │ ├── Categories.js │ │ ├── Components.js │ │ ├── Information.js │ │ ├── Showcase.js │ │ ├── Site.js │ │ ├── Sponsors.js │ │ ├── Tools.js │ │ ├── code/ │ │ │ ├── Animations/ │ │ │ │ ├── animatedContentCode.js │ │ │ │ ├── antigravityCode.js │ │ │ │ ├── blobCursorCode.js │ │ │ │ ├── clickSparkCode.js │ │ │ │ ├── crosshairCode.js │ │ │ │ ├── cubesCode.js │ │ │ │ ├── electricBorderCode.js │ │ │ │ ├── fadeContentCode.js │ │ │ │ ├── ghostCursorCode.js │ │ │ │ ├── glareHoverCode.js │ │ │ │ ├── gradualblurCode.js │ │ │ │ ├── imageTrailCode.js │ │ │ │ ├── laserFlowCode.js │ │ │ │ ├── logoLoopCode.js │ │ │ │ ├── magicRingsCode.js │ │ │ │ ├── magnetCode.js │ │ │ │ ├── magnetLinesCode.js │ │ │ │ ├── metaBallsCode.js │ │ │ │ ├── metallicPaintCode.js │ │ │ │ ├── noiseCode.js │ │ │ │ ├── orbitImagesCode.js │ │ │ │ ├── pixelTrailCode.js │ │ │ │ ├── pixelTransitionCode.js │ │ │ │ ├── ribbonsCode.js │ │ │ │ ├── shapeBlurCode.js │ │ │ │ ├── splashCursorCode.js │ │ │ │ ├── starBorderCode.js │ │ │ │ ├── stickerPeelCode.js │ │ │ │ └── targetCursorCode.js │ │ │ ├── Backgrounds/ │ │ │ │ ├── auroraCode.js │ │ │ │ ├── balatroCode.js │ │ │ │ ├── ballpitCode.js │ │ │ │ ├── beamsCode.js │ │ │ │ ├── colorBendsCode.js │ │ │ │ ├── darkVeilCode.js │ │ │ │ ├── ditherCode.js │ │ │ │ ├── dotGridCode.js │ │ │ │ ├── evilEyeCode.js │ │ │ │ ├── faultyTerminalCode.js │ │ │ │ ├── floatingLinesCode.js │ │ │ │ ├── galaxyCode.js │ │ │ │ ├── gradientBlindsCode.js │ │ │ │ ├── grainientCode.js │ │ │ │ ├── gridDistortionCode.js │ │ │ │ ├── gridMotionCode.js │ │ │ │ ├── gridScanCode.js │ │ │ │ ├── hyperspeedCode.js │ │ │ │ ├── iridescenceCode.js │ │ │ │ ├── letterGlitchCode.js │ │ │ │ ├── lightPillarCode.js │ │ │ │ ├── lightRaysCode.js │ │ │ │ ├── lightningCode.js │ │ │ │ ├── lineWavesCode.js │ │ │ │ ├── liquidChromeCode.js │ │ │ │ ├── liquidEtherCode.js │ │ │ │ ├── orbCode.js │ │ │ │ ├── particlesCode.js │ │ │ │ ├── pixelBlastCode.js │ │ │ │ ├── pixelSnowCode.js │ │ │ │ ├── plasmaCode.js │ │ │ │ ├── prismCode.js │ │ │ │ ├── prismaticBurstCode.js │ │ │ │ ├── radarCode.js │ │ │ │ ├── rippleGridCode.js │ │ │ │ ├── shapeGridCode.js │ │ │ │ ├── silkCode.js │ │ │ │ ├── softAuroraCode.js │ │ │ │ ├── threadsCode.js │ │ │ │ └── wavesCode.js │ │ │ ├── Components/ │ │ │ │ ├── animatedListCode.js │ │ │ │ ├── borderGlowCode.js │ │ │ │ ├── bounceCardsCode.js │ │ │ │ ├── bubbleMenuCode.js │ │ │ │ ├── cardNavCode.js │ │ │ │ ├── cardSwapCode.js │ │ │ │ ├── carouselCode.js │ │ │ │ ├── chromaGridCode.js │ │ │ │ ├── circularGalleryCode.js │ │ │ │ ├── counterCode.js │ │ │ │ ├── decayCardCode.js │ │ │ │ ├── dockCode.js │ │ │ │ ├── domeGalleryCode.js │ │ │ │ ├── elasticSliderCode.js │ │ │ │ ├── flowingMenuCode.js │ │ │ │ ├── fluidGlassCode.js │ │ │ │ ├── flyingPostersCode.js │ │ │ │ ├── folderCode.js │ │ │ │ ├── glassIconsCode.js │ │ │ │ ├── glassSurfaceCode.js │ │ │ │ ├── gooeyNavCode.js │ │ │ │ ├── infiniteMenuCode.js │ │ │ │ ├── lanyardCode.js │ │ │ │ ├── magicBentoCode.js │ │ │ │ ├── masonryCode.js │ │ │ │ ├── modelViewerCode.js │ │ │ │ ├── pillNavCode.js │ │ │ │ ├── pixelCardCode.js │ │ │ │ ├── profileCardCode.js │ │ │ │ ├── reflectiveCardCode.js │ │ │ │ ├── scrollStackCode.js │ │ │ │ ├── spotlightCardCode.js │ │ │ │ ├── stackCode.js │ │ │ │ ├── staggeredMenuCode.js │ │ │ │ ├── stepperCode.js │ │ │ │ └── tiltedCardCode.js │ │ │ └── TextAnimations/ │ │ │ ├── asciiTextCode.js │ │ │ ├── blurTextCode.js │ │ │ ├── circularTextCode.js │ │ │ ├── countUpCode.js │ │ │ ├── curvedLoopCode.js │ │ │ ├── decryptedTextCode.js │ │ │ ├── fallingTextCode.js │ │ │ ├── fuzzyTextCode.js │ │ │ ├── glitchTextCode.js │ │ │ ├── gradientTextCode.js │ │ │ ├── rotatingTextCode.js │ │ │ ├── scrambledTextCode.js │ │ │ ├── scrollFloatCode.js │ │ │ ├── scrollRevealCode.js │ │ │ ├── scrollVelocityCode.js │ │ │ ├── shinyTextCode.js │ │ │ ├── shuffleCode.js │ │ │ ├── splitTextCode.js │ │ │ ├── textCursorCode.js │ │ │ ├── textPressureCode.js │ │ │ ├── textTypeCode.js │ │ │ ├── trueFocusCode.js │ │ │ └── variableProximityCode.js │ │ └── colors.js │ ├── content/ │ │ ├── Animations/ │ │ │ ├── AnimatedContent/ │ │ │ │ └── AnimatedContent.jsx │ │ │ ├── Antigravity/ │ │ │ │ └── Antigravity.jsx │ │ │ ├── BlobCursor/ │ │ │ │ ├── BlobCursor.css │ │ │ │ └── BlobCursor.jsx │ │ │ ├── ClickSpark/ │ │ │ │ └── ClickSpark.jsx │ │ │ ├── Crosshair/ │ │ │ │ └── Crosshair.jsx │ │ │ ├── Cubes/ │ │ │ │ ├── Cubes.css │ │ │ │ └── Cubes.jsx │ │ │ ├── ElectricBorder/ │ │ │ │ ├── ElectricBorder.css │ │ │ │ └── ElectricBorder.jsx │ │ │ ├── FadeContent/ │ │ │ │ └── FadeContent.jsx │ │ │ ├── GhostCursor/ │ │ │ │ ├── GhostCursor.css │ │ │ │ └── GhostCursor.jsx │ │ │ ├── GlareHover/ │ │ │ │ ├── GlareHover.css │ │ │ │ └── GlareHover.jsx │ │ │ ├── GradualBlur/ │ │ │ │ ├── GradualBlur.css │ │ │ │ └── GradualBlur.jsx │ │ │ ├── ImageTrail/ │ │ │ │ ├── ImageTrail.css │ │ │ │ └── ImageTrail.jsx │ │ │ ├── LaserFlow/ │ │ │ │ ├── LaserFlow.css │ │ │ │ └── LaserFlow.jsx │ │ │ ├── LogoLoop/ │ │ │ │ ├── LogoLoop.css │ │ │ │ └── LogoLoop.jsx │ │ │ ├── MagicRings/ │ │ │ │ ├── MagicRings.css │ │ │ │ └── MagicRings.jsx │ │ │ ├── Magnet/ │ │ │ │ └── Magnet.jsx │ │ │ ├── MagnetLines/ │ │ │ │ ├── MagnetLines.css │ │ │ │ └── MagnetLines.jsx │ │ │ ├── MetaBalls/ │ │ │ │ ├── MetaBalls.css │ │ │ │ └── MetaBalls.jsx │ │ │ ├── MetallicPaint/ │ │ │ │ ├── MetallicPaint.css │ │ │ │ └── MetallicPaint.jsx │ │ │ ├── Noise/ │ │ │ │ ├── Noise.css │ │ │ │ └── Noise.jsx │ │ │ ├── OrbitImages/ │ │ │ │ ├── OrbitImages.css │ │ │ │ └── OrbitImages.jsx │ │ │ ├── PixelTrail/ │ │ │ │ ├── PixelTrail.css │ │ │ │ └── PixelTrail.jsx │ │ │ ├── PixelTransition/ │ │ │ │ ├── PixelTransition.css │ │ │ │ └── PixelTransition.jsx │ │ │ ├── Ribbons/ │ │ │ │ ├── Ribbons.css │ │ │ │ └── Ribbons.jsx │ │ │ ├── ShapeBlur/ │ │ │ │ └── ShapeBlur.jsx │ │ │ ├── SplashCursor/ │ │ │ │ └── SplashCursor.jsx │ │ │ ├── StarBorder/ │ │ │ │ ├── StarBorder.css │ │ │ │ └── StarBorder.jsx │ │ │ ├── StickerPeel/ │ │ │ │ ├── StickerPeel.css │ │ │ │ └── StickerPeel.jsx │ │ │ └── TargetCursor/ │ │ │ ├── TargetCursor.css │ │ │ └── TargetCursor.jsx │ │ ├── Backgrounds/ │ │ │ ├── Aurora/ │ │ │ │ ├── Aurora.css │ │ │ │ └── Aurora.jsx │ │ │ ├── Balatro/ │ │ │ │ ├── Balatro.css │ │ │ │ └── Balatro.jsx │ │ │ ├── Ballpit/ │ │ │ │ └── Ballpit.jsx │ │ │ ├── Beams/ │ │ │ │ ├── Beams.css │ │ │ │ └── Beams.jsx │ │ │ ├── ColorBends/ │ │ │ │ ├── ColorBends.css │ │ │ │ └── ColorBends.jsx │ │ │ ├── DarkVeil/ │ │ │ │ ├── DarkVeil.css │ │ │ │ └── DarkVeil.jsx │ │ │ ├── Dither/ │ │ │ │ ├── Dither.css │ │ │ │ └── Dither.jsx │ │ │ ├── DotGrid/ │ │ │ │ ├── DotGrid.css │ │ │ │ └── DotGrid.jsx │ │ │ ├── EvilEye/ │ │ │ │ ├── EvilEye.css │ │ │ │ └── EvilEye.jsx │ │ │ ├── FaultyTerminal/ │ │ │ │ ├── FaultyTerminal.css │ │ │ │ └── FaultyTerminal.jsx │ │ │ ├── FloatingLines/ │ │ │ │ ├── FloatingLines.css │ │ │ │ └── FloatingLines.jsx │ │ │ ├── Galaxy/ │ │ │ │ ├── Galaxy.css │ │ │ │ └── Galaxy.jsx │ │ │ ├── GradientBlinds/ │ │ │ │ ├── GradientBlinds.css │ │ │ │ └── GradientBlinds.jsx │ │ │ ├── Grainient/ │ │ │ │ ├── Grainient.css │ │ │ │ └── Grainient.jsx │ │ │ ├── GridDistortion/ │ │ │ │ ├── GridDistortion.css │ │ │ │ └── GridDistortion.jsx │ │ │ ├── GridMotion/ │ │ │ │ ├── GridMotion.css │ │ │ │ └── GridMotion.jsx │ │ │ ├── GridScan/ │ │ │ │ ├── GridScan.css │ │ │ │ └── GridScan.jsx │ │ │ ├── Hyperspeed/ │ │ │ │ ├── HyperSpeedPresets.js │ │ │ │ ├── Hyperspeed.css │ │ │ │ └── Hyperspeed.jsx │ │ │ ├── Iridescence/ │ │ │ │ ├── Iridescence.css │ │ │ │ └── Iridescence.jsx │ │ │ ├── LetterGlitch/ │ │ │ │ └── LetterGlitch.jsx │ │ │ ├── LightPillar/ │ │ │ │ ├── LightPillar.css │ │ │ │ └── LightPillar.jsx │ │ │ ├── LightRays/ │ │ │ │ ├── LightRays.css │ │ │ │ └── LightRays.jsx │ │ │ ├── Lightning/ │ │ │ │ ├── Lightning.css │ │ │ │ └── Lightning.jsx │ │ │ ├── LineWaves/ │ │ │ │ ├── LineWaves.css │ │ │ │ └── LineWaves.jsx │ │ │ ├── LiquidChrome/ │ │ │ │ ├── LiquidChrome.css │ │ │ │ └── LiquidChrome.jsx │ │ │ ├── LiquidEther/ │ │ │ │ ├── LiquidEther.css │ │ │ │ └── LiquidEther.jsx │ │ │ ├── Orb/ │ │ │ │ ├── Orb.css │ │ │ │ └── Orb.jsx │ │ │ ├── Particles/ │ │ │ │ ├── Particles.css │ │ │ │ └── Particles.jsx │ │ │ ├── PixelBlast/ │ │ │ │ ├── PixelBlast.css │ │ │ │ └── PixelBlast.jsx │ │ │ ├── PixelSnow/ │ │ │ │ ├── PixelSnow.css │ │ │ │ └── PixelSnow.jsx │ │ │ ├── Plasma/ │ │ │ │ ├── Plasma.css │ │ │ │ └── Plasma.jsx │ │ │ ├── Prism/ │ │ │ │ ├── Prism.css │ │ │ │ └── Prism.jsx │ │ │ ├── PrismaticBurst/ │ │ │ │ ├── PrismaticBurst.css │ │ │ │ └── PrismaticBurst.jsx │ │ │ ├── Radar/ │ │ │ │ ├── Radar.css │ │ │ │ └── Radar.jsx │ │ │ ├── RippleGrid/ │ │ │ │ ├── RippleGrid.css │ │ │ │ └── RippleGrid.jsx │ │ │ ├── ShapeGrid/ │ │ │ │ ├── ShapeGrid.css │ │ │ │ └── ShapeGrid.jsx │ │ │ ├── Silk/ │ │ │ │ └── Silk.jsx │ │ │ ├── SoftAurora/ │ │ │ │ ├── SoftAurora.css │ │ │ │ └── SoftAurora.jsx │ │ │ ├── Threads/ │ │ │ │ ├── Threads.css │ │ │ │ └── Threads.jsx │ │ │ └── Waves/ │ │ │ ├── Waves.css │ │ │ └── Waves.jsx │ │ ├── Components/ │ │ │ ├── AnimatedList/ │ │ │ │ ├── AnimatedList.css │ │ │ │ └── AnimatedList.jsx │ │ │ ├── BorderGlow/ │ │ │ │ ├── BorderGlow.css │ │ │ │ └── BorderGlow.jsx │ │ │ ├── BounceCards/ │ │ │ │ ├── BounceCards.css │ │ │ │ └── BounceCards.jsx │ │ │ ├── BubbleMenu/ │ │ │ │ ├── BubbleMenu.css │ │ │ │ └── BubbleMenu.jsx │ │ │ ├── CardNav/ │ │ │ │ ├── CardNav.css │ │ │ │ └── CardNav.jsx │ │ │ ├── CardSwap/ │ │ │ │ ├── CardSwap.css │ │ │ │ └── CardSwap.jsx │ │ │ ├── Carousel/ │ │ │ │ ├── Carousel.css │ │ │ │ └── Carousel.jsx │ │ │ ├── ChromaGrid/ │ │ │ │ ├── ChromaGrid.css │ │ │ │ └── ChromaGrid.jsx │ │ │ ├── CircularGallery/ │ │ │ │ ├── CircularGallery.css │ │ │ │ └── CircularGallery.jsx │ │ │ ├── Counter/ │ │ │ │ ├── Counter.css │ │ │ │ └── Counter.jsx │ │ │ ├── DecayCard/ │ │ │ │ ├── DecayCard.css │ │ │ │ └── DecayCard.jsx │ │ │ ├── Dock/ │ │ │ │ ├── Dock.css │ │ │ │ └── Dock.jsx │ │ │ ├── DomeGallery/ │ │ │ │ ├── DomeGallery.css │ │ │ │ └── DomeGallery.jsx │ │ │ ├── ElasticSlider/ │ │ │ │ ├── ElasticSlider.css │ │ │ │ └── ElasticSlider.jsx │ │ │ ├── FlowingMenu/ │ │ │ │ ├── FlowingMenu.css │ │ │ │ └── FlowingMenu.jsx │ │ │ ├── FluidGlass/ │ │ │ │ └── FluidGlass.jsx │ │ │ ├── FlyingPosters/ │ │ │ │ ├── FlyingPosters.css │ │ │ │ └── FlyingPosters.jsx │ │ │ ├── Folder/ │ │ │ │ ├── Folder.css │ │ │ │ └── Folder.jsx │ │ │ ├── GlassIcons/ │ │ │ │ ├── GlassIcons.css │ │ │ │ └── GlassIcons.jsx │ │ │ ├── GlassSurface/ │ │ │ │ ├── GlassSurface.css │ │ │ │ └── GlassSurface.jsx │ │ │ ├── GooeyNav/ │ │ │ │ ├── GooeyNav.css │ │ │ │ └── GooeyNav.jsx │ │ │ ├── InfiniteMenu/ │ │ │ │ ├── InfiniteMenu.css │ │ │ │ └── InfiniteMenu.jsx │ │ │ ├── Lanyard/ │ │ │ │ ├── Lanyard.css │ │ │ │ ├── Lanyard.jsx │ │ │ │ └── card.glb │ │ │ ├── MagicBento/ │ │ │ │ ├── MagicBento.css │ │ │ │ └── MagicBento.jsx │ │ │ ├── Masonry/ │ │ │ │ ├── Masonry.css │ │ │ │ └── Masonry.jsx │ │ │ ├── ModelViewer/ │ │ │ │ └── ModelViewer.jsx │ │ │ ├── PillNav/ │ │ │ │ ├── PillNav.css │ │ │ │ └── PillNav.jsx │ │ │ ├── PixelCard/ │ │ │ │ ├── PixelCard.css │ │ │ │ └── PixelCard.jsx │ │ │ ├── ProfileCard/ │ │ │ │ ├── ProfileCard.css │ │ │ │ └── ProfileCard.jsx │ │ │ ├── ReflectiveCard/ │ │ │ │ ├── ReflectiveCard.css │ │ │ │ └── ReflectiveCard.jsx │ │ │ ├── ScrollStack/ │ │ │ │ ├── ScrollStack.css │ │ │ │ └── ScrollStack.jsx │ │ │ ├── SpotlightCard/ │ │ │ │ ├── SpotlightCard.css │ │ │ │ └── SpotlightCard.jsx │ │ │ ├── Stack/ │ │ │ │ ├── Stack.css │ │ │ │ └── Stack.jsx │ │ │ ├── StaggeredMenu/ │ │ │ │ ├── StaggeredMenu.css │ │ │ │ └── StaggeredMenu.jsx │ │ │ ├── Stepper/ │ │ │ │ ├── Stepper.css │ │ │ │ └── Stepper.jsx │ │ │ └── TiltedCard/ │ │ │ ├── TiltedCard.css │ │ │ └── TiltedCard.jsx │ │ └── TextAnimations/ │ │ ├── ASCIIText/ │ │ │ └── ASCIIText.jsx │ │ ├── BlurText/ │ │ │ └── BlurText.jsx │ │ ├── CircularText/ │ │ │ ├── CircularText.css │ │ │ └── CircularText.jsx │ │ ├── CountUp/ │ │ │ └── CountUp.jsx │ │ ├── CurvedLoop/ │ │ │ ├── CurvedLoop.css │ │ │ └── CurvedLoop.jsx │ │ ├── DecryptedText/ │ │ │ └── DecryptedText.jsx │ │ ├── FallingText/ │ │ │ ├── FallingText.css │ │ │ └── FallingText.jsx │ │ ├── FuzzyText/ │ │ │ └── FuzzyText.jsx │ │ ├── GlitchText/ │ │ │ ├── GlitchText.css │ │ │ └── GlitchText.jsx │ │ ├── GradientText/ │ │ │ ├── GradientText.css │ │ │ └── GradientText.jsx │ │ ├── RotatingText/ │ │ │ ├── RotatingText.css │ │ │ └── RotatingText.jsx │ │ ├── ScrambledText/ │ │ │ ├── ScrambledText.css │ │ │ └── ScrambledText.jsx │ │ ├── ScrollFloat/ │ │ │ ├── ScrollFloat.css │ │ │ └── ScrollFloat.jsx │ │ ├── ScrollReveal/ │ │ │ ├── ScrollReveal.css │ │ │ └── ScrollReveal.jsx │ │ ├── ScrollVelocity/ │ │ │ ├── ScrollVelocity.css │ │ │ └── ScrollVelocity.jsx │ │ ├── ShinyText/ │ │ │ ├── ShinyText.css │ │ │ └── ShinyText.jsx │ │ ├── Shuffle/ │ │ │ ├── Shuffle.css │ │ │ └── Shuffle.jsx │ │ ├── SplitText/ │ │ │ └── SplitText.jsx │ │ ├── TextCursor/ │ │ │ ├── TextCursor.css │ │ │ └── TextCursor.jsx │ │ ├── TextPressure/ │ │ │ └── TextPressure.jsx │ │ ├── TextType/ │ │ │ ├── TextType.css │ │ │ └── TextType.jsx │ │ ├── TrueFocus/ │ │ │ ├── TrueFocus.css │ │ │ └── TrueFocus.jsx │ │ └── VariableProximity/ │ │ ├── VariableProximity.css │ │ └── VariableProximity.jsx │ ├── css/ │ │ ├── category.css │ │ ├── docs.css │ │ ├── landing.css │ │ ├── misc.css │ │ ├── showcase.css │ │ ├── sidebar.css │ │ ├── skeleton.css │ │ ├── transitions.css │ │ └── variables.css │ ├── demo/ │ │ ├── Animations/ │ │ │ ├── AnimatedContentDemo.jsx │ │ │ ├── AntigravityDemo.jsx │ │ │ ├── BlobCursorDemo.jsx │ │ │ ├── ClickSparkDemo.jsx │ │ │ ├── CrosshairDemo.jsx │ │ │ ├── CubesDemo.jsx │ │ │ ├── ElectricBorderDemo.jsx │ │ │ ├── FadeContentDemo.jsx │ │ │ ├── GhostCursorDemo.jsx │ │ │ ├── GlareHoverDemo.jsx │ │ │ ├── GradualBlurDemo.jsx │ │ │ ├── ImageTrailDemo.jsx │ │ │ ├── LaserFlowDemo.jsx │ │ │ ├── LogoLoopDemo.jsx │ │ │ ├── MagicRingsDemo.jsx │ │ │ ├── MagnetDemo.jsx │ │ │ ├── MagnetLinesDemo.jsx │ │ │ ├── MetaBallsDemo.jsx │ │ │ ├── MetallicPaintDemo.jsx │ │ │ ├── NoiseDemo.jsx │ │ │ ├── OrbitImagesDemo.jsx │ │ │ ├── PixelTrailDemo.jsx │ │ │ ├── PixelTransitionDemo.jsx │ │ │ ├── RibbonsDemo.jsx │ │ │ ├── ShapeBlurDemo.jsx │ │ │ ├── SplashCursorDemo.jsx │ │ │ ├── StarBorderDemo.jsx │ │ │ ├── StickerPeelDemo.jsx │ │ │ └── TargetCursorDemo.jsx │ │ ├── Backgrounds/ │ │ │ ├── AuroraDemo.jsx │ │ │ ├── BalatroDemo.jsx │ │ │ ├── BallpitDemo.jsx │ │ │ ├── BeamsDemo.jsx │ │ │ ├── ColorBendsDemo.jsx │ │ │ ├── DarkVeilDemo.jsx │ │ │ ├── DitherDemo.jsx │ │ │ ├── DotGridDemo.jsx │ │ │ ├── EvilEyeDemo.jsx │ │ │ ├── FaultyTerminalDemo.jsx │ │ │ ├── FloatingLinesDemo.jsx │ │ │ ├── GalaxyDemo.jsx │ │ │ ├── GradientBlindsDemo.jsx │ │ │ ├── GrainientDemo.jsx │ │ │ ├── GridDistortionDemo.jsx │ │ │ ├── GridMotionDemo.jsx │ │ │ ├── GridScanDemo.jsx │ │ │ ├── HyperspeedDemo.jsx │ │ │ ├── IridescenceDemo.jsx │ │ │ ├── LetterGlitchDemo.jsx │ │ │ ├── LightPillarDemo.jsx │ │ │ ├── LightRaysDemo.jsx │ │ │ ├── LightningDemo.jsx │ │ │ ├── LineWavesDemo.jsx │ │ │ ├── LiquidChromeDemo.jsx │ │ │ ├── LiquidEtherDemo.jsx │ │ │ ├── OrbDemo.jsx │ │ │ ├── ParticlesDemo.jsx │ │ │ ├── PixelBlastDemo.jsx │ │ │ ├── PixelSnowDemo.jsx │ │ │ ├── PlasmaDemo.jsx │ │ │ ├── PrismDemo.jsx │ │ │ ├── PrismaticBurstDemo.jsx │ │ │ ├── RadarDemo.jsx │ │ │ ├── RippleGridDemo.jsx │ │ │ ├── ShapeGridDemo.jsx │ │ │ ├── SilkDemo.jsx │ │ │ ├── SoftAuroraDemo.jsx │ │ │ ├── ThreadsDemo.jsx │ │ │ └── WavesDemo.jsx │ │ ├── Components/ │ │ │ ├── AnimatedListDemo.jsx │ │ │ ├── BorderGlowDemo.jsx │ │ │ ├── BounceCardsDemo.jsx │ │ │ ├── BubbleMenuDemo.jsx │ │ │ ├── CardNavDemo.jsx │ │ │ ├── CardSwapDemo.jsx │ │ │ ├── CarouselDemo.jsx │ │ │ ├── ChromaGridDemo.jsx │ │ │ ├── CircularGalleryDemo.jsx │ │ │ ├── CounterDemo.jsx │ │ │ ├── DecayCardDemo.jsx │ │ │ ├── DockDemo.jsx │ │ │ ├── DomeGalleryDemo.jsx │ │ │ ├── ElasticSliderDemo.jsx │ │ │ ├── FlowingMenuDemo.jsx │ │ │ ├── FluidGlassDemo.jsx │ │ │ ├── FlyingPostersDemo.jsx │ │ │ ├── FolderDemo.jsx │ │ │ ├── GlassIconsDemo.jsx │ │ │ ├── GlassSurfaceDemo.jsx │ │ │ ├── GooeyNavDemo.jsx │ │ │ ├── InfiniteMenuDemo.jsx │ │ │ ├── LanyardDemo.jsx │ │ │ ├── MagicBentoDemo.jsx │ │ │ ├── MasonryDemo.jsx │ │ │ ├── ModelViewerDemo.jsx │ │ │ ├── PillNavDemo.jsx │ │ │ ├── PixelCardDemo.jsx │ │ │ ├── ProfileCardDemo.jsx │ │ │ ├── ReflectiveCardDemo.jsx │ │ │ ├── ScrollStackDemo.jsx │ │ │ ├── SpotlightCardDemo.jsx │ │ │ ├── StackDemo.jsx │ │ │ ├── StaggeredMenuDemo.jsx │ │ │ ├── StepperDemo.jsx │ │ │ └── TiltedCardDemo.jsx │ │ └── TextAnimations/ │ │ ├── ASCIITextDemo.jsx │ │ ├── BlurTextDemo.jsx │ │ ├── CircularTextDemo.jsx │ │ ├── CountUpDemo.jsx │ │ ├── CurvedLoopDemo.jsx │ │ ├── DecryptedTextDemo.jsx │ │ ├── FallingTextDemo.jsx │ │ ├── FuzzyTextDemo.jsx │ │ ├── GlitchTextDemo.jsx │ │ ├── GradientTextDemo.jsx │ │ ├── RotatingTextDemo.jsx │ │ ├── ScrambledTextDemo.jsx │ │ ├── ScrollFloatDemo.jsx │ │ ├── ScrollRevealDemo.jsx │ │ ├── ScrollVelocityDemo.jsx │ │ ├── ShinyTextDemo.jsx │ │ ├── ShuffleDemo.jsx │ │ ├── SplitTextDemo.jsx │ │ ├── TextCursorDemo.jsx │ │ ├── TextPressureDemo.jsx │ │ ├── TextTypeDemo.jsx │ │ ├── TrueFocusDemo.jsx │ │ └── VariableProximityDemo.jsx │ ├── docs/ │ │ ├── CodeBlock.jsx │ │ ├── DocsButtonBar.jsx │ │ ├── Installation.jsx │ │ ├── Introduction.jsx │ │ ├── McpServer.jsx │ │ └── MethodSelector.jsx │ ├── global.d.ts │ ├── hooks/ │ │ ├── useActiveRoute.js │ │ ├── useComponentProps.js │ │ ├── useComponentPropsContext.js │ │ ├── useForceRerender.js │ │ ├── useInstallation.js │ │ ├── useScrollToTop.js │ │ ├── useStars.js │ │ └── useTransition.js │ ├── main.jsx │ ├── pages/ │ │ ├── CategoryPage.jsx │ │ ├── FavoritesPage.jsx │ │ ├── IndexPage.jsx │ │ ├── LandingPage.jsx │ │ ├── ShowcasePage.jsx │ │ ├── SponsorsPage.jsx │ │ └── ToolsPage.jsx │ ├── styles.css │ ├── tailwind/ │ │ ├── Animations/ │ │ │ ├── AnimatedContent/ │ │ │ │ └── AnimatedContent.jsx │ │ │ ├── Antigravity/ │ │ │ │ └── Antigravity.jsx │ │ │ ├── BlobCursor/ │ │ │ │ └── BlobCursor.jsx │ │ │ ├── ClickSpark/ │ │ │ │ └── ClickSpark.jsx │ │ │ ├── Crosshair/ │ │ │ │ └── Crosshair.jsx │ │ │ ├── Cubes/ │ │ │ │ └── Cubes.jsx │ │ │ ├── ElectricBorder/ │ │ │ │ └── ElectricBorder.jsx │ │ │ ├── FadeContent/ │ │ │ │ └── FadeContent.jsx │ │ │ ├── GhostCursor/ │ │ │ │ └── GhostCursor.jsx │ │ │ ├── GlareHover/ │ │ │ │ └── GlareHover.jsx │ │ │ ├── GradualBlur/ │ │ │ │ └── GradualBlur.jsx │ │ │ ├── ImageTrail/ │ │ │ │ └── ImageTrail.jsx │ │ │ ├── LaserFlow/ │ │ │ │ └── LaserFlow.jsx │ │ │ ├── LogoLoop/ │ │ │ │ └── LogoLoop.jsx │ │ │ ├── MagicRings/ │ │ │ │ └── MagicRings.jsx │ │ │ ├── Magnet/ │ │ │ │ └── Magnet.jsx │ │ │ ├── MagnetLines/ │ │ │ │ └── MagnetLines.jsx │ │ │ ├── MetaBalls/ │ │ │ │ └── MetaBalls.jsx │ │ │ ├── MetallicPaint/ │ │ │ │ └── MetallicPaint.jsx │ │ │ ├── Noise/ │ │ │ │ └── Noise.jsx │ │ │ ├── OrbitImages/ │ │ │ │ └── OrbitImages.jsx │ │ │ ├── PixelTrail/ │ │ │ │ └── PixelTrail.jsx │ │ │ ├── PixelTransition/ │ │ │ │ └── PixelTransition.jsx │ │ │ ├── Ribbons/ │ │ │ │ └── Ribbons.jsx │ │ │ ├── ShapeBlur/ │ │ │ │ └── ShapeBlur.jsx │ │ │ ├── SplashCursor/ │ │ │ │ └── SplashCursor.jsx │ │ │ ├── StarBorder/ │ │ │ │ └── StarBorder.jsx │ │ │ ├── StickerPeel/ │ │ │ │ └── StickerPeel.jsx │ │ │ └── TargetCursor/ │ │ │ └── TargetCursor.jsx │ │ ├── Backgrounds/ │ │ │ ├── Aurora/ │ │ │ │ └── Aurora.jsx │ │ │ ├── Balatro/ │ │ │ │ └── Balatro.jsx │ │ │ ├── Ballpit/ │ │ │ │ └── Ballpit.jsx │ │ │ ├── Beams/ │ │ │ │ └── Beams.jsx │ │ │ ├── ColorBends/ │ │ │ │ └── ColorBends.jsx │ │ │ ├── DarkVeil/ │ │ │ │ └── DarkVeil.jsx │ │ │ ├── Dither/ │ │ │ │ └── Dither.jsx │ │ │ ├── DotGrid/ │ │ │ │ └── DotGrid.jsx │ │ │ ├── EvilEye/ │ │ │ │ └── EvilEye.jsx │ │ │ ├── FaultyTerminal/ │ │ │ │ └── FaultyTerminal.jsx │ │ │ ├── FloatingLines/ │ │ │ │ └── FloatingLines.jsx │ │ │ ├── Galaxy/ │ │ │ │ └── Galaxy.jsx │ │ │ ├── GradientBlinds/ │ │ │ │ └── GradientBlinds.jsx │ │ │ ├── Grainient/ │ │ │ │ └── Grainient.jsx │ │ │ ├── GridDistortion/ │ │ │ │ └── GridDistortion.jsx │ │ │ ├── GridMotion/ │ │ │ │ └── GridMotion.jsx │ │ │ ├── GridScan/ │ │ │ │ └── GridScan.jsx │ │ │ ├── Hyperspeed/ │ │ │ │ ├── HyperSpeedPresets.js │ │ │ │ └── Hyperspeed.jsx │ │ │ ├── Iridescence/ │ │ │ │ └── Iridescence.jsx │ │ │ ├── LetterGlitch/ │ │ │ │ └── LetterGlitch.jsx │ │ │ ├── LightPillar/ │ │ │ │ └── LightPillar.jsx │ │ │ ├── LightRays/ │ │ │ │ └── LightRays.jsx │ │ │ ├── Lightning/ │ │ │ │ └── Lightning.jsx │ │ │ ├── LineWaves/ │ │ │ │ └── LineWaves.jsx │ │ │ ├── LiquidChrome/ │ │ │ │ └── LiquidChrome.jsx │ │ │ ├── LiquidEther/ │ │ │ │ └── LiquidEther.jsx │ │ │ ├── Orb/ │ │ │ │ └── Orb.jsx │ │ │ ├── Particles/ │ │ │ │ └── Particles.jsx │ │ │ ├── PixelBlast/ │ │ │ │ └── PixelBlast.jsx │ │ │ ├── PixelSnow/ │ │ │ │ └── PixelSnow.jsx │ │ │ ├── Plasma/ │ │ │ │ └── Plasma.jsx │ │ │ ├── Prism/ │ │ │ │ └── Prism.jsx │ │ │ ├── PrismaticBurst/ │ │ │ │ └── PrismaticBurst.jsx │ │ │ ├── Radar/ │ │ │ │ └── Radar.jsx │ │ │ ├── RippleGrid/ │ │ │ │ └── RippleGrid.jsx │ │ │ ├── ShapeGrid/ │ │ │ │ └── ShapeGrid.jsx │ │ │ ├── Silk/ │ │ │ │ └── Silk.jsx │ │ │ ├── SoftAurora/ │ │ │ │ └── SoftAurora.jsx │ │ │ ├── Threads/ │ │ │ │ └── Threads.jsx │ │ │ └── Waves/ │ │ │ └── Waves.jsx │ │ ├── Components/ │ │ │ ├── AnimatedList/ │ │ │ │ └── AnimatedList.jsx │ │ │ ├── BorderGlow/ │ │ │ │ └── BorderGlow.jsx │ │ │ ├── BounceCards/ │ │ │ │ └── BounceCards.jsx │ │ │ ├── BubbleMenu/ │ │ │ │ └── BubbleMenu.jsx │ │ │ ├── CardNav/ │ │ │ │ └── CardNav.jsx │ │ │ ├── CardSwap/ │ │ │ │ └── CardSwap.jsx │ │ │ ├── Carousel/ │ │ │ │ └── Carousel.jsx │ │ │ ├── ChromaGrid/ │ │ │ │ └── ChromaGrid.jsx │ │ │ ├── CircularGallery/ │ │ │ │ └── CircularGallery.jsx │ │ │ ├── Counter/ │ │ │ │ └── Counter.jsx │ │ │ ├── DecayCard/ │ │ │ │ └── DecayCard.jsx │ │ │ ├── Dock/ │ │ │ │ └── Dock.jsx │ │ │ ├── DomeGallery/ │ │ │ │ └── DomeGallery.jsx │ │ │ ├── ElasticSlider/ │ │ │ │ └── ElasticSlider.jsx │ │ │ ├── FlowingMenu/ │ │ │ │ └── FlowingMenu.jsx │ │ │ ├── FluidGlass/ │ │ │ │ └── FluidGlass.jsx │ │ │ ├── FlyingPosters/ │ │ │ │ └── FlyingPosters.jsx │ │ │ ├── Folder/ │ │ │ │ └── Folder.jsx │ │ │ ├── GlassIcons/ │ │ │ │ └── GlassIcons.jsx │ │ │ ├── GlassSurface/ │ │ │ │ └── GlassSurface.jsx │ │ │ ├── GooeyNav/ │ │ │ │ └── GooeyNav.jsx │ │ │ ├── InfiniteMenu/ │ │ │ │ └── InfiniteMenu.jsx │ │ │ ├── Lanyard/ │ │ │ │ ├── Lanyard.jsx │ │ │ │ └── card.glb │ │ │ ├── MagicBento/ │ │ │ │ └── MagicBento.jsx │ │ │ ├── Masonry/ │ │ │ │ └── Masonry.jsx │ │ │ ├── ModelViewer/ │ │ │ │ └── ModelViewer.jsx │ │ │ ├── PillNav/ │ │ │ │ └── PillNav.jsx │ │ │ ├── PixelCard/ │ │ │ │ └── PixelCard.jsx │ │ │ ├── ProfileCard/ │ │ │ │ └── ProfileCard.jsx │ │ │ ├── ReflectiveCard/ │ │ │ │ └── ReflectiveCard.jsx │ │ │ ├── ScrollStack/ │ │ │ │ └── ScrollStack.jsx │ │ │ ├── SpotlightCard/ │ │ │ │ └── SpotlightCard.jsx │ │ │ ├── Stack/ │ │ │ │ └── Stack.jsx │ │ │ ├── StaggeredMenu/ │ │ │ │ └── StaggeredMenu.jsx │ │ │ ├── Stepper/ │ │ │ │ └── Stepper.jsx │ │ │ └── TiltedCard/ │ │ │ └── TiltedCard.jsx │ │ └── TextAnimations/ │ │ ├── ASCIIText/ │ │ │ └── ASCIIText.jsx │ │ ├── BlurText/ │ │ │ └── BlurText.jsx │ │ ├── CircularText/ │ │ │ └── CircularText.jsx │ │ ├── CountUp/ │ │ │ └── CountUp.jsx │ │ ├── CurvedLoop/ │ │ │ └── CurvedLoop.jsx │ │ ├── DecryptedText/ │ │ │ └── DecryptedText.jsx │ │ ├── FallingText/ │ │ │ └── FallingText.jsx │ │ ├── FuzzyText/ │ │ │ └── FuzzyText.jsx │ │ ├── GlitchText/ │ │ │ └── GlitchText.jsx │ │ ├── GradientText/ │ │ │ └── GradientText.jsx │ │ ├── RotatingText/ │ │ │ └── RotatingText.jsx │ │ ├── ScrambledText/ │ │ │ └── ScrambledText.jsx │ │ ├── ScrollFloat/ │ │ │ └── ScrollFloat.jsx │ │ ├── ScrollReveal/ │ │ │ └── ScrollReveal.jsx │ │ ├── ScrollVelocity/ │ │ │ └── ScrollVelocity.jsx │ │ ├── ShinyText/ │ │ │ └── ShinyText.jsx │ │ ├── Shuffle/ │ │ │ └── Shuffle.jsx │ │ ├── SplitText/ │ │ │ └── SplitText.jsx │ │ ├── TextCursor/ │ │ │ └── TextCursor.jsx │ │ ├── TextPressure/ │ │ │ └── TextPressure.jsx │ │ ├── TextType/ │ │ │ └── TextType.jsx │ │ ├── TrueFocus/ │ │ │ └── TrueFocus.jsx │ │ └── VariableProximity/ │ │ └── VariableProximity.jsx │ ├── tools/ │ │ ├── background-studio/ │ │ │ ├── BackgroundStudio.jsx │ │ │ ├── Controls.jsx │ │ │ ├── backgrounds/ │ │ │ │ └── index.js │ │ │ ├── hooks/ │ │ │ │ └── useBackgroundState.js │ │ │ └── utils/ │ │ │ └── exportCode.js │ │ ├── shape-magic/ │ │ │ ├── Canvas.jsx │ │ │ ├── Controls.jsx │ │ │ ├── ShapeMagic.jsx │ │ │ ├── computeBridges.js │ │ │ ├── svgRenderers.js │ │ │ └── types.js │ │ ├── texture-lab/ │ │ │ ├── Canvas.jsx │ │ │ ├── Controls.jsx │ │ │ ├── TextureLab.jsx │ │ │ ├── renderer.js │ │ │ ├── shaders.js │ │ │ ├── types.js │ │ │ └── utils.js │ │ └── tools.css │ ├── ts-default/ │ │ ├── Animations/ │ │ │ ├── AnimatedContent/ │ │ │ │ └── AnimatedContent.tsx │ │ │ ├── Antigravity/ │ │ │ │ └── Antigravity.tsx │ │ │ ├── BlobCursor/ │ │ │ │ ├── BlobCursor.css │ │ │ │ └── BlobCursor.tsx │ │ │ ├── ClickSpark/ │ │ │ │ └── ClickSpark.tsx │ │ │ ├── Crosshair/ │ │ │ │ └── Crosshair.tsx │ │ │ ├── Cubes/ │ │ │ │ ├── Cubes.css │ │ │ │ └── Cubes.tsx │ │ │ ├── ElectricBorder/ │ │ │ │ ├── ElectricBorder.css │ │ │ │ └── ElectricBorder.tsx │ │ │ ├── FadeContent/ │ │ │ │ └── FadeContent.tsx │ │ │ ├── GhostCursor/ │ │ │ │ ├── GhostCursor.css │ │ │ │ └── GhostCursor.tsx │ │ │ ├── GlareHover/ │ │ │ │ ├── GlareHover.css │ │ │ │ └── GlareHover.tsx │ │ │ ├── GradualBlur/ │ │ │ │ ├── GradualBlur.css │ │ │ │ └── GradualBlur.tsx │ │ │ ├── ImageTrail/ │ │ │ │ ├── ImageTrail.css │ │ │ │ └── ImageTrail.tsx │ │ │ ├── LaserFlow/ │ │ │ │ ├── LaserFlow.css │ │ │ │ └── LaserFlow.tsx │ │ │ ├── LogoLoop/ │ │ │ │ ├── LogoLoop.css │ │ │ │ └── LogoLoop.tsx │ │ │ ├── MagicRings/ │ │ │ │ ├── MagicRings.css │ │ │ │ └── MagicRings.tsx │ │ │ ├── Magnet/ │ │ │ │ └── Magnet.tsx │ │ │ ├── MagnetLines/ │ │ │ │ ├── MagnetLines.css │ │ │ │ └── MagnetLines.tsx │ │ │ ├── MetaBalls/ │ │ │ │ ├── MetaBalls.css │ │ │ │ └── MetaBalls.tsx │ │ │ ├── MetallicPaint/ │ │ │ │ ├── MetallicPaint.css │ │ │ │ └── MetallicPaint.tsx │ │ │ ├── Noise/ │ │ │ │ ├── Noise.css │ │ │ │ └── Noise.tsx │ │ │ ├── OrbitImages/ │ │ │ │ ├── OrbitImages.css │ │ │ │ └── OrbitImages.tsx │ │ │ ├── PixelTrail/ │ │ │ │ ├── PixelTrail.css │ │ │ │ └── PixelTrail.tsx │ │ │ ├── PixelTransition/ │ │ │ │ ├── PixelTransition.css │ │ │ │ └── PixelTransition.tsx │ │ │ ├── Ribbons/ │ │ │ │ ├── Ribbons.css │ │ │ │ └── Ribbons.tsx │ │ │ ├── ShapeBlur/ │ │ │ │ └── ShapeBlur.tsx │ │ │ ├── SplashCursor/ │ │ │ │ └── SplashCursor.tsx │ │ │ ├── StarBorder/ │ │ │ │ ├── StarBorder.css │ │ │ │ └── StarBorder.tsx │ │ │ ├── StickerPeel/ │ │ │ │ ├── StickerPeel.css │ │ │ │ └── StickerPeel.tsx │ │ │ └── TargetCursor/ │ │ │ ├── TargetCursor.css │ │ │ └── TargetCursor.tsx │ │ ├── Backgrounds/ │ │ │ ├── Aurora/ │ │ │ │ ├── Aurora.css │ │ │ │ └── Aurora.tsx │ │ │ ├── Balatro/ │ │ │ │ ├── Balatro.css │ │ │ │ └── Balatro.tsx │ │ │ ├── Ballpit/ │ │ │ │ └── Ballpit.tsx │ │ │ ├── Beams/ │ │ │ │ ├── Beams.css │ │ │ │ └── Beams.tsx │ │ │ ├── ColorBends/ │ │ │ │ ├── ColorBends.css │ │ │ │ └── ColorBends.tsx │ │ │ ├── DarkVeil/ │ │ │ │ ├── DarkVeil.css │ │ │ │ └── DarkVeil.tsx │ │ │ ├── Dither/ │ │ │ │ ├── Dither.css │ │ │ │ └── Dither.tsx │ │ │ ├── DotGrid/ │ │ │ │ ├── DotGrid.css │ │ │ │ └── DotGrid.tsx │ │ │ ├── EvilEye/ │ │ │ │ ├── EvilEye.css │ │ │ │ └── EvilEye.tsx │ │ │ ├── FaultyTerminal/ │ │ │ │ ├── FaultyTerminal.css │ │ │ │ └── FaultyTerminal.tsx │ │ │ ├── FloatingLines/ │ │ │ │ ├── FloatingLines.css │ │ │ │ └── FloatingLines.tsx │ │ │ ├── Galaxy/ │ │ │ │ ├── Galaxy.css │ │ │ │ └── Galaxy.tsx │ │ │ ├── GradientBlinds/ │ │ │ │ ├── GradientBlinds.css │ │ │ │ └── GradientBlinds.tsx │ │ │ ├── Grainient/ │ │ │ │ ├── Grainient.css │ │ │ │ └── Grainient.tsx │ │ │ ├── GridDistortion/ │ │ │ │ ├── GridDistortion.css │ │ │ │ └── GridDistortion.tsx │ │ │ ├── GridMotion/ │ │ │ │ ├── GridMotion.css │ │ │ │ └── GridMotion.tsx │ │ │ ├── GridScan/ │ │ │ │ ├── GridScan.css │ │ │ │ └── GridScan.tsx │ │ │ ├── Hyperspeed/ │ │ │ │ ├── HyperSpeedPresets.ts │ │ │ │ ├── Hyperspeed.css │ │ │ │ └── Hyperspeed.tsx │ │ │ ├── Iridescence/ │ │ │ │ ├── Iridescence.css │ │ │ │ └── Iridescence.tsx │ │ │ ├── LetterGlitch/ │ │ │ │ └── LetterGlitch.tsx │ │ │ ├── LightPillar/ │ │ │ │ ├── LightPillar.css │ │ │ │ └── LightPillar.tsx │ │ │ ├── LightRays/ │ │ │ │ ├── LightRays.css │ │ │ │ └── LightRays.tsx │ │ │ ├── Lightning/ │ │ │ │ ├── Lightning.css │ │ │ │ └── Lightning.tsx │ │ │ ├── LineWaves/ │ │ │ │ ├── LineWaves.css │ │ │ │ └── LineWaves.tsx │ │ │ ├── LiquidChrome/ │ │ │ │ ├── LiquidChrome.css │ │ │ │ └── LiquidChrome.tsx │ │ │ ├── LiquidEther/ │ │ │ │ ├── LiquidEther.css │ │ │ │ └── LiquidEther.tsx │ │ │ ├── Orb/ │ │ │ │ ├── Orb.css │ │ │ │ └── Orb.tsx │ │ │ ├── Particles/ │ │ │ │ ├── Particles.css │ │ │ │ └── Particles.tsx │ │ │ ├── PixelBlast/ │ │ │ │ ├── PixelBlast.css │ │ │ │ └── PixelBlast.tsx │ │ │ ├── PixelSnow/ │ │ │ │ ├── PixelSnow.css │ │ │ │ └── PixelSnow.tsx │ │ │ ├── Plasma/ │ │ │ │ ├── Plasma.css │ │ │ │ └── Plasma.tsx │ │ │ ├── Prism/ │ │ │ │ ├── Prism.css │ │ │ │ └── Prism.tsx │ │ │ ├── PrismaticBurst/ │ │ │ │ ├── PrismaticBurst.css │ │ │ │ └── PrismaticBurst.tsx │ │ │ ├── Radar/ │ │ │ │ ├── Radar.css │ │ │ │ └── Radar.tsx │ │ │ ├── RippleGrid/ │ │ │ │ ├── RippleGrid.css │ │ │ │ └── RippleGrid.tsx │ │ │ ├── ShapeGrid/ │ │ │ │ ├── ShapeGrid.css │ │ │ │ └── ShapeGrid.tsx │ │ │ ├── Silk/ │ │ │ │ └── Silk.tsx │ │ │ ├── SoftAurora/ │ │ │ │ ├── SoftAurora.css │ │ │ │ └── SoftAurora.tsx │ │ │ ├── Threads/ │ │ │ │ ├── Threads.css │ │ │ │ └── Threads.tsx │ │ │ └── Waves/ │ │ │ ├── Waves.css │ │ │ └── Waves.tsx │ │ ├── Components/ │ │ │ ├── AnimatedList/ │ │ │ │ ├── AnimatedList.css │ │ │ │ └── AnimatedList.tsx │ │ │ ├── BorderGlow/ │ │ │ │ ├── BorderGlow.css │ │ │ │ └── BorderGlow.tsx │ │ │ ├── BounceCards/ │ │ │ │ ├── BounceCards.css │ │ │ │ └── BounceCards.tsx │ │ │ ├── BubbleMenu/ │ │ │ │ ├── BubbleMenu.css │ │ │ │ └── BubbleMenu.tsx │ │ │ ├── CardNav/ │ │ │ │ ├── CardNav.css │ │ │ │ └── CardNav.tsx │ │ │ ├── CardSwap/ │ │ │ │ ├── CardSwap.css │ │ │ │ └── CardSwap.tsx │ │ │ ├── Carousel/ │ │ │ │ ├── Carousel.css │ │ │ │ └── Carousel.tsx │ │ │ ├── ChromaGrid/ │ │ │ │ ├── ChromaGrid.css │ │ │ │ └── ChromaGrid.tsx │ │ │ ├── CircularGallery/ │ │ │ │ ├── CircularGallery.css │ │ │ │ └── CircularGallery.tsx │ │ │ ├── Counter/ │ │ │ │ ├── Counter.css │ │ │ │ └── Counter.tsx │ │ │ ├── DecayCard/ │ │ │ │ ├── DecayCard.css │ │ │ │ └── DecayCard.tsx │ │ │ ├── Dock/ │ │ │ │ ├── Dock.css │ │ │ │ └── Dock.tsx │ │ │ ├── DomeGallery/ │ │ │ │ ├── DomeGallery.css │ │ │ │ └── DomeGallery.tsx │ │ │ ├── ElasticSlider/ │ │ │ │ ├── ElasticSlider.css │ │ │ │ └── ElasticSlider.tsx │ │ │ ├── FlowingMenu/ │ │ │ │ ├── FlowingMenu.css │ │ │ │ └── FlowingMenu.tsx │ │ │ ├── FluidGlass/ │ │ │ │ └── FluidGlass.tsx │ │ │ ├── FlyingPosters/ │ │ │ │ ├── FlyingPosters.css │ │ │ │ └── FlyingPosters.tsx │ │ │ ├── Folder/ │ │ │ │ ├── Folder.css │ │ │ │ └── Folder.tsx │ │ │ ├── GlassIcons/ │ │ │ │ ├── GlassIcons.css │ │ │ │ └── GlassIcons.tsx │ │ │ ├── GlassSurface/ │ │ │ │ ├── GlassSurface.css │ │ │ │ └── GlassSurface.tsx │ │ │ ├── GooeyNav/ │ │ │ │ ├── GooeyNav.css │ │ │ │ └── GooeyNav.tsx │ │ │ ├── InfiniteMenu/ │ │ │ │ ├── InfiniteMenu.css │ │ │ │ └── InfiniteMenu.tsx │ │ │ ├── Lanyard/ │ │ │ │ ├── Lanyard.css │ │ │ │ ├── Lanyard.tsx │ │ │ │ └── card.glb │ │ │ ├── MagicBento/ │ │ │ │ ├── MagicBento.css │ │ │ │ └── MagicBento.tsx │ │ │ ├── Masonry/ │ │ │ │ ├── Masonry.css │ │ │ │ └── Masonry.tsx │ │ │ ├── ModelViewer/ │ │ │ │ └── ModelViewer.tsx │ │ │ ├── PillNav/ │ │ │ │ ├── PillNav.css │ │ │ │ └── PillNav.tsx │ │ │ ├── PixelCard/ │ │ │ │ ├── PixelCard.css │ │ │ │ └── PixelCard.tsx │ │ │ ├── ProfileCard/ │ │ │ │ ├── ProfileCard.css │ │ │ │ └── ProfileCard.tsx │ │ │ ├── ReflectiveCard/ │ │ │ │ ├── ReflectiveCard.css │ │ │ │ └── ReflectiveCard.tsx │ │ │ ├── ScrollStack/ │ │ │ │ ├── ScrollStack.css │ │ │ │ └── ScrollStack.tsx │ │ │ ├── SpotlightCard/ │ │ │ │ ├── SpotlightCard.css │ │ │ │ └── SpotlightCard.tsx │ │ │ ├── Stack/ │ │ │ │ ├── Stack.css │ │ │ │ └── Stack.tsx │ │ │ ├── StaggeredMenu/ │ │ │ │ ├── StaggeredMenu.css │ │ │ │ └── StaggeredMenu.tsx │ │ │ ├── Stepper/ │ │ │ │ ├── Stepper.css │ │ │ │ └── Stepper.tsx │ │ │ └── TiltedCard/ │ │ │ ├── TiltedCard.css │ │ │ └── TiltedCard.tsx │ │ └── TextAnimations/ │ │ ├── ASCIIText/ │ │ │ └── ASCIIText.tsx │ │ ├── BlurText/ │ │ │ └── BlurText.tsx │ │ ├── CircularText/ │ │ │ ├── CircularText.css │ │ │ └── CircularText.tsx │ │ ├── CountUp/ │ │ │ └── CountUp.tsx │ │ ├── CurvedLoop/ │ │ │ ├── CurvedLoop.css │ │ │ └── CurvedLoop.tsx │ │ ├── DecryptedText/ │ │ │ └── DecryptedText.tsx │ │ ├── FallingText/ │ │ │ ├── FallingText.css │ │ │ └── FallingText.tsx │ │ ├── FuzzyText/ │ │ │ └── FuzzyText.tsx │ │ ├── GlitchText/ │ │ │ ├── GlitchText.css │ │ │ └── GlitchText.tsx │ │ ├── GradientText/ │ │ │ ├── GradientText.css │ │ │ └── GradientText.tsx │ │ ├── RotatingText/ │ │ │ ├── RotatingText.css │ │ │ └── RotatingText.tsx │ │ ├── ScrambledText/ │ │ │ ├── ScrambledText.css │ │ │ └── ScrambledText.tsx │ │ ├── ScrollFloat/ │ │ │ ├── ScrollFloat.css │ │ │ └── ScrollFloat.tsx │ │ ├── ScrollReveal/ │ │ │ ├── ScrollReveal.css │ │ │ └── ScrollReveal.tsx │ │ ├── ScrollVelocity/ │ │ │ ├── ScrollVelocity.css │ │ │ └── ScrollVelocity.tsx │ │ ├── ShinyText/ │ │ │ ├── ShinyText.css │ │ │ └── ShinyText.tsx │ │ ├── Shuffle/ │ │ │ ├── Shuffle.css │ │ │ └── Shuffle.tsx │ │ ├── SplitText/ │ │ │ └── SplitText.tsx │ │ ├── TextCursor/ │ │ │ ├── TextCursor.css │ │ │ └── TextCursor.tsx │ │ ├── TextPressure/ │ │ │ └── TextPressure.tsx │ │ ├── TextType/ │ │ │ ├── TextType.css │ │ │ └── TextType.tsx │ │ ├── TrueFocus/ │ │ │ ├── TrueFocus.css │ │ │ └── TrueFocus.tsx │ │ └── VariableProximity/ │ │ ├── VariableProximity.css │ │ └── VariableProximity.tsx │ ├── ts-tailwind/ │ │ ├── Animations/ │ │ │ ├── AnimatedContent/ │ │ │ │ └── AnimatedContent.tsx │ │ │ ├── Antigravity/ │ │ │ │ └── Antigravity.tsx │ │ │ ├── BlobCursor/ │ │ │ │ └── BlobCursor.tsx │ │ │ ├── ClickSpark/ │ │ │ │ └── ClickSpark.tsx │ │ │ ├── Crosshair/ │ │ │ │ └── Crosshair.tsx │ │ │ ├── Cubes/ │ │ │ │ └── Cubes.tsx │ │ │ ├── ElectricBorder/ │ │ │ │ └── ElectricBorder.tsx │ │ │ ├── FadeContent/ │ │ │ │ └── FadeContent.tsx │ │ │ ├── GhostCursor/ │ │ │ │ └── GhostCursor.tsx │ │ │ ├── GlareHover/ │ │ │ │ └── GlareHover.tsx │ │ │ ├── GradualBlur/ │ │ │ │ └── GradualBlur.tsx │ │ │ ├── ImageTrail/ │ │ │ │ └── ImageTrail.tsx │ │ │ ├── LaserFlow/ │ │ │ │ └── LaserFlow.tsx │ │ │ ├── LogoLoop/ │ │ │ │ └── LogoLoop.tsx │ │ │ ├── MagicRings/ │ │ │ │ └── MagicRings.tsx │ │ │ ├── Magnet/ │ │ │ │ └── Magnet.tsx │ │ │ ├── MagnetLines/ │ │ │ │ └── MagnetLines.tsx │ │ │ ├── MetaBalls/ │ │ │ │ └── MetaBalls.tsx │ │ │ ├── MetallicPaint/ │ │ │ │ └── MetallicPaint.tsx │ │ │ ├── Noise/ │ │ │ │ └── Noise.tsx │ │ │ ├── OrbitImages/ │ │ │ │ └── OrbitImages.tsx │ │ │ ├── PixelTrail/ │ │ │ │ └── PixelTrail.tsx │ │ │ ├── PixelTransition/ │ │ │ │ └── PixelTransition.tsx │ │ │ ├── Ribbons/ │ │ │ │ └── Ribbons.tsx │ │ │ ├── ShapeBlur/ │ │ │ │ └── ShapeBlur.tsx │ │ │ ├── SplashCursor/ │ │ │ │ └── SplashCursor.tsx │ │ │ ├── StarBorder/ │ │ │ │ └── StarBorder.tsx │ │ │ ├── StickerPeel/ │ │ │ │ └── StickerPeel.tsx │ │ │ └── TargetCursor/ │ │ │ └── TargetCursor.tsx │ │ ├── Backgrounds/ │ │ │ ├── Aurora/ │ │ │ │ └── Aurora.tsx │ │ │ ├── Balatro/ │ │ │ │ └── Balatro.tsx │ │ │ ├── Ballpit/ │ │ │ │ └── Ballpit.tsx │ │ │ ├── Beams/ │ │ │ │ └── Beams.tsx │ │ │ ├── ColorBends/ │ │ │ │ └── ColorBends.tsx │ │ │ ├── DarkVeil/ │ │ │ │ └── DarkVeil.tsx │ │ │ ├── Dither/ │ │ │ │ └── Dither.tsx │ │ │ ├── DotGrid/ │ │ │ │ └── DotGrid.tsx │ │ │ ├── EvilEye/ │ │ │ │ └── EvilEye.tsx │ │ │ ├── FaultyTerminal/ │ │ │ │ └── FaultyTerminal.tsx │ │ │ ├── FloatingLines/ │ │ │ │ └── FloatingLines.tsx │ │ │ ├── Galaxy/ │ │ │ │ └── Galaxy.tsx │ │ │ ├── GradientBlinds/ │ │ │ │ └── GradientBlinds.tsx │ │ │ ├── Grainient/ │ │ │ │ └── Grainient.tsx │ │ │ ├── GridDistortion/ │ │ │ │ └── GridDistortion.tsx │ │ │ ├── GridMotion/ │ │ │ │ └── GridMotion.tsx │ │ │ ├── GridScan/ │ │ │ │ └── GridScan.tsx │ │ │ ├── Hyperspeed/ │ │ │ │ ├── HyperSpeedPresets.ts │ │ │ │ └── Hyperspeed.tsx │ │ │ ├── Iridescence/ │ │ │ │ └── Iridescence.tsx │ │ │ ├── LetterGlitch/ │ │ │ │ └── LetterGlitch.tsx │ │ │ ├── LightPillar/ │ │ │ │ └── LightPillar.tsx │ │ │ ├── LightRays/ │ │ │ │ └── LightRays.tsx │ │ │ ├── Lightning/ │ │ │ │ └── Lightning.tsx │ │ │ ├── LineWaves/ │ │ │ │ └── LineWaves.tsx │ │ │ ├── LiquidChrome/ │ │ │ │ └── LiquidChrome.tsx │ │ │ ├── LiquidEther/ │ │ │ │ └── LiquidEther.tsx │ │ │ ├── Orb/ │ │ │ │ └── Orb.tsx │ │ │ ├── Particles/ │ │ │ │ └── Particles.tsx │ │ │ ├── PixelBlast/ │ │ │ │ └── PixelBlast.tsx │ │ │ ├── PixelSnow/ │ │ │ │ └── PixelSnow.tsx │ │ │ ├── Plasma/ │ │ │ │ └── Plasma.tsx │ │ │ ├── Prism/ │ │ │ │ └── Prism.tsx │ │ │ ├── PrismaticBurst/ │ │ │ │ └── PrismaticBurst.tsx │ │ │ ├── Radar/ │ │ │ │ └── Radar.tsx │ │ │ ├── RippleGrid/ │ │ │ │ └── RippleGrid.tsx │ │ │ ├── ShapeGrid/ │ │ │ │ └── ShapeGrid.tsx │ │ │ ├── Silk/ │ │ │ │ └── Silk.tsx │ │ │ ├── SoftAurora/ │ │ │ │ └── SoftAurora.tsx │ │ │ ├── Threads/ │ │ │ │ └── Threads.tsx │ │ │ └── Waves/ │ │ │ └── Waves.tsx │ │ ├── Components/ │ │ │ ├── AnimatedList/ │ │ │ │ └── AnimatedList.tsx │ │ │ ├── BorderGlow/ │ │ │ │ └── BorderGlow.tsx │ │ │ ├── BounceCards/ │ │ │ │ └── BounceCards.tsx │ │ │ ├── BubbleMenu/ │ │ │ │ └── BubbleMenu.tsx │ │ │ ├── CardNav/ │ │ │ │ └── CardNav.tsx │ │ │ ├── CardSwap/ │ │ │ │ └── CardSwap.tsx │ │ │ ├── Carousel/ │ │ │ │ └── Carousel.tsx │ │ │ ├── ChromaGrid/ │ │ │ │ └── ChromaGrid.tsx │ │ │ ├── CircularGallery/ │ │ │ │ └── CircularGallery.tsx │ │ │ ├── Counter/ │ │ │ │ └── Counter.tsx │ │ │ ├── DecayCard/ │ │ │ │ └── DecayCard.tsx │ │ │ ├── Dock/ │ │ │ │ └── Dock.tsx │ │ │ ├── DomeGallery/ │ │ │ │ └── DomeGallery.tsx │ │ │ ├── ElasticSlider/ │ │ │ │ └── ElasticSlider.tsx │ │ │ ├── FlowingMenu/ │ │ │ │ └── FlowingMenu.tsx │ │ │ ├── FluidGlass/ │ │ │ │ └── FluidGlass.tsx │ │ │ ├── FlyingPosters/ │ │ │ │ └── FlyingPosters.tsx │ │ │ ├── Folder/ │ │ │ │ └── Folder.tsx │ │ │ ├── GlassIcons/ │ │ │ │ └── GlassIcons.tsx │ │ │ ├── GlassSurface/ │ │ │ │ └── GlassSurface.tsx │ │ │ ├── GooeyNav/ │ │ │ │ └── GooeyNav.tsx │ │ │ ├── InfiniteMenu/ │ │ │ │ └── InfiniteMenu.tsx │ │ │ ├── Lanyard/ │ │ │ │ ├── Lanyard.tsx │ │ │ │ └── card.glb │ │ │ ├── MagicBento/ │ │ │ │ └── MagicBento.tsx │ │ │ ├── Masonry/ │ │ │ │ └── Masonry.tsx │ │ │ ├── ModelViewer/ │ │ │ │ └── ModelViewer.tsx │ │ │ ├── PillNav/ │ │ │ │ └── PillNav.tsx │ │ │ ├── PixelCard/ │ │ │ │ └── PixelCard.tsx │ │ │ ├── ProfileCard/ │ │ │ │ └── ProfileCard.tsx │ │ │ ├── ReflectiveCard/ │ │ │ │ └── ReflectiveCard.tsx │ │ │ ├── ScrollStack/ │ │ │ │ └── ScrollStack.tsx │ │ │ ├── SpotlightCard/ │ │ │ │ └── SpotlightCard.tsx │ │ │ ├── Stack/ │ │ │ │ └── Stack.tsx │ │ │ ├── StaggeredMenu/ │ │ │ │ └── StaggeredMenu.tsx │ │ │ ├── Stepper/ │ │ │ │ └── Stepper.tsx │ │ │ └── TiltedCard/ │ │ │ └── TiltedCard.tsx │ │ └── TextAnimations/ │ │ ├── ASCIIText/ │ │ │ └── ASCIIText.tsx │ │ ├── BlurText/ │ │ │ └── BlurText.tsx │ │ ├── CircularText/ │ │ │ └── CircularText.tsx │ │ ├── CountUp/ │ │ │ └── CountUp.tsx │ │ ├── CurvedLoop/ │ │ │ └── CurvedLoop.tsx │ │ ├── DecryptedText/ │ │ │ └── DecryptedText.tsx │ │ ├── FallingText/ │ │ │ └── FallingText.tsx │ │ ├── FuzzyText/ │ │ │ └── FuzzyText.tsx │ │ ├── GlitchText/ │ │ │ └── GlitchText.tsx │ │ ├── GradientText/ │ │ │ └── GradientText.tsx │ │ ├── RotatingText/ │ │ │ └── RotatingText.tsx │ │ ├── ScrambledText/ │ │ │ └── ScrambledText.tsx │ │ ├── ScrollFloat/ │ │ │ └── ScrollFloat.tsx │ │ ├── ScrollReveal/ │ │ │ └── ScrollReveal.tsx │ │ ├── ScrollVelocity/ │ │ │ └── ScrollVelocity.tsx │ │ ├── ShinyText/ │ │ │ └── ShinyText.tsx │ │ ├── Shuffle/ │ │ │ └── Shuffle.tsx │ │ ├── SplitText/ │ │ │ └── SplitText.tsx │ │ ├── TextCursor/ │ │ │ └── TextCursor.tsx │ │ ├── TextPressure/ │ │ │ └── TextPressure.tsx │ │ ├── TextType/ │ │ │ └── TextType.tsx │ │ ├── TrueFocus/ │ │ │ └── TrueFocus.tsx │ │ └── VariableProximity/ │ │ └── VariableProximity.tsx │ ├── utils/ │ │ ├── animationUtils.js │ │ ├── cli.js │ │ ├── codeGeneration.js │ │ ├── codeTheme.js │ │ ├── customTheme.js │ │ ├── favorites.js │ │ ├── fuzzy.js │ │ └── utils.js │ └── vite-env.d.ts ├── tsconfig.json ├── vite.config.js └── wrangler.jsonc ================================================ FILE CONTENTS ================================================ ================================================ FILE: .assetsignore ================================================ default/** tailwind/** ts/** ================================================ FILE: .context/new-component.md ================================================ # React Bits – New Component Creation Context This file provides complete, concrete context for an AI agent to reliably create new components in this repository. It is based on the OrbitImages component as a reference implementation. --- ## 0. Prerequisites Run the scaffolding script first. It creates empty files and registers the component in `Components.js`, `Categories.js`, and `Information.js` automatically. ```bash npm run new:component -- # Example: npm run new:component -- Animations OrbitImages ``` This creates: - `src/content///.jsx` (empty) - `src/content///.css` (empty) - `src/tailwind///.jsx` (empty) - `src/ts-default///.tsx` (empty) - `src/ts-default///.css` (empty) - `src/ts-tailwind///.tsx` (empty) - `src/demo//Demo.jsx` (scaffold with Noise component placeholder) - `src/constants/code//Code.js` (empty) - Entries in `Components.js`, `Categories.js`, `Information.js` After scaffolding, you fill in the 8 files (4 variants + demo + code metadata + 2 CSS files for CSS variants). --- ## 1. Four Variant Rules All four variants must produce **identical visual output and behavior**. The differences are only: | Variant | Path | Language | Styling | |---|---|---|---| | JS + CSS | `src/content/…/.jsx` + `.css` | JavaScript | CSS classes imported via `./Name.css` | | JS + Tailwind | `src/tailwind/…/.jsx` | JavaScript | Tailwind utility classes inline | | TS + CSS | `src/ts-default/…/.tsx` + `.css` | TypeScript | CSS classes imported via `./Name.css` | | TS + Tailwind | `src/ts-tailwind/…/.tsx` | TypeScript | Tailwind utility classes inline | ### JS + CSS variant rules - Import `'./ComponentName.css'` - Use named CSS classes for layout/styling (e.g. `.orbit-container`, `.orbit-item`) - No TypeScript, no type annotations - Props destructured with defaults in function signature - `export default function ComponentName({ ... }) {}` ### JS + Tailwind variant rules - **No** CSS import - Replace every CSS class with Tailwind utility classes inline - Same logic, same props, same defaults as JS+CSS ### TS + CSS variant rules - Same CSS file content (duplicated into `ts-default/`) - Import `'./ComponentName.css'` - Add TypeScript `interface` for props - Add `type` aliases for union types - Type all refs: `useRef(null)` - Type function params and return types for helpers - Type motion values: `MotionValue` ### TS + Tailwind variant rules - **No** CSS import - TypeScript interfaces + types (same as TS+CSS) - Tailwind utility classes inline (same as JS+Tailwind) - **No `cn()` utility** ### CSS file conventions - Use component-scoped class names prefixed with component name (e.g. `.orbit-container`, `.orbit-item`) - The CSS file in `ts-default/` is an exact copy of the one in `content/` - Keep styles minimal – only what's needed for layout/positioning --- ## 2. Demo File Pattern Location: `src/demo//Demo.jsx` ### Standard imports ```jsx import { useMemo } from 'react'; import { Flex } from '@chakra-ui/react'; // or Box, depending on layout needs import { CodeTab, PreviewTab, TabsLayout } from '../../components/common/TabsLayout'; import Customize from '../../components/common/Preview/Customize'; import PreviewSlider from '../../components/common/Preview/PreviewSlider'; import PreviewSwitch from '../../components/common/Preview/PreviewSwitch'; import PreviewSelect from '../../components/common/Preview/PreviewSelect'; import CodeExample from '../../components/code/CodeExample'; import RefreshButton from '../../components/common/Preview/RefreshButton'; import PropTable from '../../components/common/Preview/PropTable'; import Dependencies from '../../components/code/Dependencies'; import useForceRerender from '../../hooks/useForceRerender'; import useComponentProps from '../../hooks/useComponentProps'; import { ComponentPropsProvider } from '../../components/context/ComponentPropsContext'; // Import the JS+CSS component (always from content/) import ComponentName from '../../content///'; // Import code metadata import { camelCaseName } from '../../constants/code//Code'; ``` ### Demo structure ```jsx const DEFAULT_PROPS = { // Only include props that have demo controls someProp: defaultValue, }; const ComponentNameDemo = () => { const [key, forceRerender] = useForceRerender(); const { props, updateProp, resetProps, hasChanges } = useComponentProps(DEFAULT_PROPS); const { someProp } = props; const propData = useMemo(() => [ // ALL public props documented, not just controlled ones { name: 'propName', type: 'type', default: 'value', description: 'Description.' }, ], []); return ( {/* Controls here */} ); }; export default ComponentNameDemo; ``` ### Control types ```jsx // Slider { updateProp('propName', val); forceRerender(); }} /> // Switch (boolean toggle) { updateProp('propName', checked); forceRerender(); }} /> // Select (dropdown) { updateProp('propName', val); forceRerender(); }} /> ``` ### When to call `forceRerender()` - Always call it for props that affect animation initialization or layout - For live-updating props (like autoplay toggle), it may not be needed - When in doubt, call it --- ## 3. Code Metadata File Location: `src/constants/code//Code.js` ```js import code from '@content///.jsx?raw'; import css from '@content///.css?raw'; import tailwind from '@tailwind///.jsx?raw'; import tsCode from '@ts-default///.tsx?raw'; import tsTailwind from '@ts-tailwind///.tsx?raw'; export const camelCaseName = { dependencies: `dep1 dep2`, // space-separated npm package names usage: `import ComponentName from './ComponentName' `, code, css, tailwind, tsCode, tsTailwind }; ``` - `dependencies`: space-separated string of npm packages (e.g. `"motion"`, `"gsap"`) - `usage`: JSX code snippet showing basic usage (imports + JSX) - Path aliases: `@content`, `@tailwind`, `@ts-default`, `@ts-tailwind` map to `src/content`, `src/tailwind`, etc. - The `?raw` suffix imports file contents as a raw string (Vite feature) --- ## 4. Registration (Auto-generated by scaffolding) These are handled by `npm run new:component` but for reference: ### `src/constants/Components.js` ```js 'kebab-case-name': () => import('../demo//Demo') ``` ### `src/constants/Categories.js` Component name added to the category's subcategories array and optionally to `NEW` array: ```js export const NEW = ['Component Name', ...]; // And in the subcategories: { heading: 'Category', subcategories: ['Component Name', ...] } ``` ### `src/constants/Information.js` ```js 'Category/ComponentName': { videoUrl: '/assets/video/componentname.webm', description: 'Short description.', category: 'Category', name: 'ComponentName', docsUrl: 'https://reactbits.dev/category/kebab-case-name', tags: [] }, ``` --- ## 5. Background Studio (backgrounds only) If the component is in the `Backgrounds` category, also register it in: `src/tools/background-studio/backgrounds/index.js` And add `OpenInStudioButton` to the demo: ```jsx import OpenInStudioButton from '../../components/common/Preview/OpenInStudioButton'; // After the preview, before : ``` --- ## 6. Naming Conventions | Context | Format | Example | |---|---|---| | Component name | PascalCase | `OrbitImages` | | File names | PascalCase matching component | `OrbitImages.jsx` | | CSS class prefix | kebab-case component name | `.orbit-container` | | Route slug | kebab-case | `orbit-images` | | Code metadata export | camelCase | `orbitImages` | | Code metadata file | camelCase + `Code.js` | `orbitImagesCode.js` | | Category display name | Space-separated | `Orbit Images` | | Folder name | PascalCase | `OrbitImages/` | --- ## 7. Checklist Before considering a component complete: - [ ] All 4 variant files are implemented with identical behavior - [ ] CSS files in `content/` and `ts-default/` are identical - [ ] TS variants have proper interfaces and type annotations - [ ] Demo imports from `content/` (JS+CSS variant) - [ ] Demo has `DEFAULT_PROPS`, `useComponentProps`, `ComponentPropsProvider` - [ ] Demo has `RefreshButton`, `Customize` controls, `PropTable`, `Dependencies` - [ ] Demo has `CodeTab` with `CodeExample` - [ ] Code metadata uses `?raw` imports with correct path aliases - [ ] Code metadata has `dependencies`, `usage`, `code`, `css`, `tailwind`, `tsCode`, `tsTailwind` - [ ] Component is registered in `Components.js`, `Categories.js`, and `Information.js` - [ ] Props/defaults are consistent across component, demo, and code metadata usage example ================================================ FILE: .eslintrc.cjs ================================================ module.exports = { root: true, env: { browser: true, es2020: true }, extends: [ 'eslint:recommended', 'plugin:react/recommended', 'plugin:react/jsx-runtime', 'plugin:react-hooks/recommended' ], ignorePatterns: ['dist', '.eslintrc.cjs', 'public/default', 'public/tailwind', 'public/ts', 'public/r'], parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, settings: { react: { version: '19.0' } }, plugins: ['react-refresh'], rules: { 'react/prop-types': 'off', 'react/jsx-no-target-blank': 'off', 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }] } }; ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry polar: # Replace with a single Polar username buy_me_a_coffee: # Replace with a single Buy Me a Coffee username thanks_dev: # Replace with a single thanks.dev username custom: ['https://reactbits.dev/sponsors'] ================================================ FILE: .github/ISSUE_TEMPLATE/1-bug-report.yml ================================================ name: 🐞 Bug report description: Help improve React Bits. labels: ['bug'] title: '[BUG]: ' body: - type: markdown attributes: value: | ## Thanks for trying to improve React Bits! Before continuing make sure you have checked other issues to see if your issue has already been reported / addressed. - type: textarea id: desc attributes: label: Describe the issue description: What is happening right now? What is supposed to happen? placeholder: When I do ..., it does ... but it should do ... validations: required: true - type: markdown attributes: value: | ## Reproduction Please provide code snippets/screenshots and, if possible/needed, a codesandbox environment where your bug can be reproduced. - type: input id: reproduction-link attributes: label: Reproduction Link description: Link a codesandbox environment you used to reproduce. placeholder: https://github.com/DavidHDev/react-bits validations: required: false - type: textarea id: repro-steps attributes: label: Steps to reproduce description: What steps should be taken to reproduce your issue. validations: required: true - type: checkboxes id: terms attributes: label: Validations description: Please make sure you have checked all of the following. options: - label: I have checked other issues to see if my issue was already reported or addressed required: true ================================================ FILE: .github/ISSUE_TEMPLATE/2-feature-request.yml ================================================ name: 💡 Feature Request description: Suggest something for React Bits. labels: ['enhancement'] title: '[FEAT]: ' body: - type: markdown attributes: value: | ## Thanks for trying to improve React Bits! Before continuing make sure you have checked other issues to see if your idea has already been discussed / addressed. - type: textarea id: desc attributes: label: Share your suggestion description: What would you like to see in React Bits? placeholder: I want flying pigs in a component please validations: required: true - type: checkboxes id: terms attributes: label: Validations description: Please make sure you have checked all of the following. options: - label: I have checked other issues to see if my issue was already discussed or addressed required: true ================================================ FILE: .gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log* components-mcp.json scripts/mcp.js node_modules dist dist-ssr *.local components.json public/default public/tailwind public/ts/default public/ts/tailwind AGENTS.md # Editor directories and files .vscode/* !.vscode/extensions.json .idea .wrangler .DS_Store *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: .prettierignore ================================================ # Dependencies node_modules/ # Build outputs dist/ build/ public/ # OS files .DS_Store Thumbs.db # IDE files .vscode/ .idea/ # Logs *.log # Cache .cache/ .parcel-cache/ .next/ .vite/ # Environment files .env .env.local .env.*.local # Generated files coverage/ *.tsbuildinfo registry.json # Other Components.js Categories.js Information.js ================================================ FILE: .prettierrc ================================================ { "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "none", "printWidth": 120, "bracketSpacing": true, "arrowParens": "avoid", "endOfLine": "lf", "vueIndentScriptAndStyle": false, "htmlWhitespaceSensitivity": "ignore", "bracketSameLine": false, "singleAttributePerLine": false } ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to React Bits Thank you for considering contributing to React Bits! I appreciate your interest in making this project better. To ensure a smooth collaboration, please read the following guidelines before getting started. ## Issue Tracker We use the GitHub issue tracker to keep track of bugs, feature requests, and other project-related discussions. Before starting to work on a new feature or bug fix, please check the issue tracker to see if there's an existing issue or discussion related to it. If not, feel free to create a new issue. ## Branch Naming When creating branches for your contributions, please follow the following naming convention: `feat/` For example, if you are working on a feature related to adding a new component, your branch name could be `feat/fix-x-component`. This naming convention helps us to easily track and associate contributions with their respective features. ## Pull Requests We welcome pull requests from everyone as long as they respect the quality standards of this project. To submit a pull request, please follow these steps: 1. Fork the repository and create a new branch based on the branch naming convention mentioned above. 2. Make your changes in the new branch. 3. Submit a pull request to the main repository's `main` branch. 4. Provide a clear and descriptive title for your pull request, along with a detailed description of the changes you have made, and screenshots/videos where possible. 5. For components updates, ensure that changes are reflected in all related files. Each component change must be updated in all 4 variants of that particular component. 6. Before you open a pull request, please make sure that your changes are tested locally, and everything looks good on desktop and mobile, also check the browser console for errors, and so on, so that we can keep this library at the highest quality possible. 7. Any pull requests that fail to meet these requirements will be denied, so please make sure you respect them so that your work can go through. ## Note New components from the community are currently not being accepted into the library, only component enhancements and bug fixes are open for contributions. ## Conclusion I appreciate your interest in contributing! By following these guidelines, you can help us maintain a healthy and productive open-source community. I value your contributions and look forward to your pull requests! If you have any questions or need further assistance, please don't hesitate to reach out to us through the issue tracker or other communication channels mentioned in the project's documentation. Happy contributing! ================================================ FILE: LICENSE.md ================================================ MIT + Commons Clause License Condition v1.0 Copyright (c) 2026 David Haz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, and distribute the Software **as part of an application, website, or product**, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. ## Commons Clause Restriction You may use this Software, including for any commercial purpose, **so long as you do not sell, sublicense, or redistribute the components themselves-whether alone, in a bundle, or as a ported version.** ## No Warranty THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================


react-bits logo

The largest & most creative library of animated React components.
Stand out with 110+ free, customizable animations for text, backgrounds, and UI.

GitHub Repo stars License

📖 Documentation · ⚡ Quick Start · 🛠️ Tools

React Bits component showcase

## ✨ Why React Bits? React Bits helps you **ship stunning interfaces faster**. Instead of spending hours crafting animations from scratch, grab a polished component and customize it to fit your vision. > 💬 **Text Animations** · 🌀 **Animations** · 🧩 **Components** · 🖼️ **Backgrounds** ## 🚀 Features - **110+ components** — text animations, UI elements, and backgrounds, growing weekly - **Minimal dependencies** — lightweight and tree-shakeable - **Fully customizable** — tweak everything via props or edit the source directly - **4 variants per component** — JS-CSS, JS-TW, TS-CSS, TS-TW (everyone's happy) - **Copy-paste ready** — works with any modern React project ## 🛠️ Creative Tools
React Bits Tools

### Beyond components, React Bits offers **free creative tools** to supercharge your workflow: | Tool | What it does | | ---------------------------------------------------- | ---------------------------------------------------------------------------------------- | | **[Background Studio](https://reactbits.dev/tools)** | Explore animated backgrounds, customize effects, export as video/image/code | | **[Shape Magic](https://reactbits.dev/tools)** | Create inner rounded corners between shapes, export as SVG, React code or clip-path code | | **[Texture Lab](https://reactbits.dev/tools)** | Apply 20+ effects (noise, dithering, ASCII) to images/videos and export in high quality | ## 📦 Installation React Bits supports [shadcn](https://ui.shadcn.com/) and [jsrepo](https://jsrepo.dev) for quick CLI installs. ```bash # Example: Add a component via shadcn npx shadcn@latest add @react-bits/BlurText-TS-TW ``` Each component page includes copy-ready CLI commands. See the [installation guide](https://reactbits.dev/get-started/installation) for full details. You can also select your preferred technologies, and copy the code manually. ## 🚀 Sponsors React Bits is proudly supported by these amazing sponsors: ### Diamond shadcnblocks.com     shadcn studio ### Silver Next.js Weekly     Shadcraft     shadcnspace
**[Become a sponsor](https://reactbits.dev/sponsors)** — Get your brand in front of 500K+ developers monthly. ## 🤝 Contributing We'd love your help! Check the [open issues](https://github.com/DavidHDev/react-bits/issues) or submit ideas via the [feature request template](https://github.com/DavidHDev/react-bits/issues/new?template=2-feature-request.yml). Please read the [contribution guide](https://github.com/DavidHDev/react-bits/blob/main/CONTRIBUTING.md) first — thanks for making React Bits better! ## 🙌 Contributors ## 👤 Maintainer **[David Haz](https://github.com/DavidHDev)** — creator & lead maintainer ## 🌐 Official Ports | Framework | Link | | --------- | ------------------------------------- | | Vue.js | [vue-bits.dev](https://vue-bits.dev/) | ## 📊 Stats ![Repobeats analytics](https://repobeats.axiom.co/api/embed/b1bf4dc0226458617adbdbf5586f2df953eb0922.svg 'Repobeats analytics image') ## 🗳️ Credit React Bits occasionally draws inspiration from publicly available code examples. These are rewritten as full-fledged, customizable components for JS, TS, CSS, and Tailwind. If you recognize your work, [open an issue](https://github.com/DavidHDev/react-bits/issues) to request credit. ## 📄 License [MIT + Commons Clause](https://github.com/davidhdev/react-bits/blob/main/LICENSE.md) — free for personal and commercial use. ================================================ FILE: index.html ================================================ React Bits - Animated UI Components For React
================================================ FILE: jsrepo.config.ts ================================================ import { defineConfig, type RegistryItem } from 'jsrepo'; import { output } from '@jsrepo/shadcn'; import { type Category, componentMetadata, type Variant } from './src/constants/Information'; // Components that have non-code assets which should use manual dependency resolution. // Key format: `${category}/${title}` to keep it unique. const MANUAL_ASSETS: Record = { 'Components/Lanyard': [ { path: 'card.glb', dependencyResolution: 'manual' }, { path: 'lanyard.png', dependencyResolution: 'manual' } ] // Example: if ModelViewer had a model file you wanted to mark as manual too: // 'Components/ModelViewer': [ // { path: 'model.glb', dependencyResolution: 'manual' } // ] }; export default defineConfig({ registry: { name: '@react-bits', description: 'An open source collection of animated, interactive & fully customizable React components for building stunning, memorable user interfaces.', homepage: 'https://reactbits.dev', authors: ['David Haz'], bugs: 'https://github.com/DavidHDev/react-bits/issues', repository: 'https://github.com/DavidHDev/react-bits', tags: [ 'react', 'javascript', 'components', 'web', 'reactjs', 'css-animations', 'component-library', 'ui-components', '3d', 'ui-library', 'tailwind', 'tailwindcss', 'components', 'components-library' ], excludeDeps: ['react'], outputs: [output({ dir: 'public/r', format: true })], items: [ ...Object.values(componentMetadata).map(component => defineComponent({ title: component.name, description: component.description, category: component.category, categories: [component.category], meta: component.meta, variants: component.variants }) ) ].flat() } }); /** * Define a component to be exposed from the registry. Creates the 4 different variants of the component and ensures the correct files are included. * * @param title The title of the component. * @param description The description of the component. * @param category The category of the component. * @param categories Organize the component into multiple categories. * @param meta Optional meta data for the component. * @param variants The variants of the component that are available through the registry (default: all variants) * @returns An array of RegistryItem objects. */ function defineComponent({ title, description, category, categories, meta, variants = ['JS-CSS', 'JS-TW', 'TS-CSS', 'TS-TW'] }: { title: string; description: string; category: Category; categories?: string[]; meta?: Record; variants?: readonly Variant[]; }): RegistryItem[] { const baseItem: Omit = { title, description, type: 'registry:component', categories: [category, ...(categories ?? [])], meta }; // Unique key for this component in MANUAL_ASSETS const key = `${category}/${title}`; const manualFiles = MANUAL_ASSETS[key] && MANUAL_ASSETS[key].length > 0 ? MANUAL_ASSETS[key] : []; const withManualFiles = (basePath: string) => manualFiles.length > 0 ? [ { path: basePath, files: manualFiles } ] : [ { path: basePath } ]; // this might warrant a bit of explanation // basically we check if the variant is included in the variants array and if so we return the item as part of an array // otherwise we return an empty array // we then spread that array empty or otherwise into the return array return [ // JS + CSS ...(variants.includes('JS-CSS') ? [ { ...baseItem, name: `${baseItem.title}-JS-CSS`, files: withManualFiles(`src/content/${category}/${title}`) } ] : []), // JS + Tailwind ...(variants.includes('JS-TW') ? [ { ...baseItem, name: `${baseItem.title}-JS-TW`, files: withManualFiles(`src/tailwind/${category}/${title}`) } ] : []), // TS + CSS ...(variants.includes('TS-CSS') ? [ { ...baseItem, name: `${baseItem.title}-TS-CSS`, files: withManualFiles(`src/ts-default/${category}/${title}`) } ] : []), // TS + Tailwind ...(variants.includes('TS-TW') ? [ { ...baseItem, name: `${baseItem.title}-TS-TW`, files: withManualFiles(`src/ts-tailwind/${category}/${title}`) } ] : []) ]; } ================================================ FILE: package.json ================================================ { "name": "react-bits", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "concurrently -n \"registry,docs\" -c \"blue,green\" \"npm run registry:dev\" \"vite\"", "build": "npm run registry:build && npm run llms:text && npm run sitemap && vite build", "new:component": "node scripts/generateComponent.js", "llms:text": "node ./scripts/generateLlmsText.js", "sitemap": "node ./scripts/generateSitemap.js", "registry:build": "jsrepo build", "registry:dev": "jsrepo build --watch", "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", "format": "prettier --write ." }, "dependencies": { "@chakra-ui/icons": "^2.2.4", "@chakra-ui/react": "^3.20.0", "@emotion/react": "^11.14.0", "@gsap/react": "^2.1.2", "@react-three/drei": "^10.7.4", "@react-three/fiber": "^9.3.0", "@react-three/postprocessing": "^3.0.4", "@react-three/rapier": "^2.1.0", "@tailwindcss/vite": "^4.0.3", "@use-gesture/react": "^10.2.27", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "face-api.js": "^0.22.2", "gl-matrix": "^3.4.3", "gsap": "^3.13.0", "lenis": "^1.3.13", "lucide-react": "^0.542.0", "maath": "^0.10.8", "mathjs": "^14.6.0", "matter-js": "^0.20.0", "meshline": "^3.3.1", "motion": "^12.23.12", "next-themes": "^0.4.6", "nuqs": "^2.8.6", "ogl": "^1.0.11", "postprocessing": "^6.36.0", "react": "^19.0.0", "react-confetti": "^6.2.2", "react-dom": "^19.0.0", "react-haiku": "^2.2.0", "react-icons": "^5.5.0", "react-router-dom": "^6.30.1", "react-syntax-highlighter": "^15.6.1", "react-virtualized": "^9.22.6", "sonner": "^1.7.1", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.0.3", "three": "^0.167.1" }, "devDependencies": { "@jsrepo/shadcn": "^2.0.0", "@types/matter-js": "^0.19.8", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "@types/three": "^0.180.0", "@vitejs/plugin-react": "^4.3.4", "concurrently": "^9.1.2", "eslint": "^8.57.0", "eslint-plugin-react": "^7.34.3", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.7", "jsrepo": "^3.2.0", "postcss-safe-parser": "^7.0.1", "prettier": "^3.6.2", "tw-animate-css": "^1.4.0", "typescript": "^5.7.3", "vite": "^5.3.4" } } ================================================ FILE: public/README.md ================================================
react-bits logo
Welcome to React Bits, the go-to open source library for high quality animated React components!
================================================ FILE: public/llms.txt ================================================ # React Bits > React Bits is an open source collection of memorable UI elements - Components, Animations, Backgrounds, and Text Animations - provided in four implementation variants: JavaScript + CSS, JavaScript + Tailwind, TypeScript + CSS, and TypeScript + Tailwind. Components are copy-friendly and installable via CLI (jsrepo or shadcn). Important notes for agents: - Components are organized by semantics first: UI Components, Animations, Backgrounds, Text Animations. - Each component has 4 variants. All variants are kept in sync when updated. - Dependencies vary by component (e.g., gsap, motion, three, ogl). Always check and install dependencies before usage. ## Docs - [Homepage](https://www.reactbits.dev): Landing page, quick presentation of the library, testimonials. - [Introduction](https://www.reactbits.dev/get-started/introduction): Project mission and principles. - [Installation](https://www.reactbits.dev/get-started/installation): Manual copy and CLI commands (jsrepo, shadcn). - [MCP Setup](https://www.reactbits.dev/get-started/mcp): Set up a MCP server to help you with development. ## CLI - shadcn: `npx shadcn@latest add https://reactbits.dev/r/--\n \n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "three@^0.167.1" ] } ================================================ FILE: public/r/ASCIIText-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "ASCIIText-JS-TW", "title": "ASCIIText", "description": "Renders text with an animated ASCII background for a retro feel.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "ASCIIText/ASCIIText.jsx", "content": "// Component ported and enhanced from https://codepen.io/JuanFuentes/pen/eYEeoyE\n\nimport { useEffect, useRef } from 'react';\nimport * as THREE from 'three';\n\nconst vertexShader = `\nvarying vec2 vUv;\nuniform float uTime;\nuniform float mouse;\nuniform float uEnableWaves;\n\nvoid main() {\n vUv = uv;\n float time = uTime * 5.;\n\n float waveFactor = uEnableWaves;\n\n vec3 transformed = position;\n\n transformed.x += sin(time + position.y) * 0.5 * waveFactor;\n transformed.y += cos(time + position.z) * 0.15 * waveFactor;\n transformed.z += sin(time + position.x) * waveFactor;\n\n gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);\n}\n`;\n\nconst fragmentShader = `\nvarying vec2 vUv;\nuniform float mouse;\nuniform float uTime;\nuniform sampler2D uTexture;\n\nvoid main() {\n float time = uTime;\n vec2 pos = vUv;\n \n float move = sin(time + mouse) * 0.01;\n float r = texture2D(uTexture, pos + cos(time * 2. - time + pos.x) * .01).r;\n float g = texture2D(uTexture, pos + tan(time * .5 + pos.x - time) * .01).g;\n float b = texture2D(uTexture, pos - cos(time * 2. + time + pos.y) * .01).b;\n float a = texture2D(uTexture, pos).a;\n gl_FragColor = vec4(r, g, b, a);\n}\n`;\n\nMath.map = function (n, start, stop, start2, stop2) {\n return ((n - start) / (stop - start)) * (stop2 - start2) + start2;\n};\n\nconst PX_RATIO = typeof window !== 'undefined' ? window.devicePixelRatio : 1;\n\nclass AsciiFilter {\n constructor(renderer, { fontSize, fontFamily, charset, invert } = {}) {\n this.renderer = renderer;\n this.domElement = document.createElement('div');\n this.domElement.style.position = 'absolute';\n this.domElement.style.top = '0';\n this.domElement.style.left = '0';\n this.domElement.style.width = '100%';\n this.domElement.style.height = '100%';\n\n this.pre = document.createElement('pre');\n this.domElement.appendChild(this.pre);\n\n this.canvas = document.createElement('canvas');\n this.context = this.canvas.getContext('2d');\n this.domElement.appendChild(this.canvas);\n\n this.deg = 0;\n this.invert = invert ?? true;\n this.fontSize = fontSize ?? 12;\n this.fontFamily = fontFamily ?? \"'Courier New', monospace\";\n this.charset = charset ?? ' .\\'`^\",:;Il!i~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$';\n\n this.context.webkitImageSmoothingEnabled = false;\n this.context.mozImageSmoothingEnabled = false;\n this.context.msImageSmoothingEnabled = false;\n this.context.imageSmoothingEnabled = false;\n\n this.onMouseMove = this.onMouseMove.bind(this);\n document.addEventListener('mousemove', this.onMouseMove);\n }\n\n setSize(width, height) {\n this.width = width;\n this.height = height;\n this.renderer.setSize(width, height);\n this.reset();\n\n this.center = { x: width / 2, y: height / 2 };\n this.mouse = { x: this.center.x, y: this.center.y };\n }\n\n reset() {\n this.context.font = `${this.fontSize}px ${this.fontFamily}`;\n const charWidth = this.context.measureText('A').width;\n\n this.cols = Math.floor(this.width / (this.fontSize * (charWidth / this.fontSize)));\n this.rows = Math.floor(this.height / this.fontSize);\n\n this.canvas.width = this.cols;\n this.canvas.height = this.rows;\n this.pre.style.fontFamily = this.fontFamily;\n this.pre.style.fontSize = `${this.fontSize}px`;\n this.pre.style.margin = '0';\n this.pre.style.padding = '0';\n this.pre.style.lineHeight = '1em';\n this.pre.style.position = 'absolute';\n this.pre.style.left = '0';\n this.pre.style.top = '0';\n this.pre.style.zIndex = '9';\n this.pre.style.backgroundAttachment = 'fixed';\n this.pre.style.mixBlendMode = 'difference';\n }\n\n render(scene, camera) {\n this.renderer.render(scene, camera);\n\n const w = this.canvas.width;\n const h = this.canvas.height;\n this.context.clearRect(0, 0, w, h);\n if (this.context && w && h) {\n this.context.drawImage(this.renderer.domElement, 0, 0, w, h);\n }\n\n this.asciify(this.context, w, h);\n this.hue();\n }\n\n onMouseMove(e) {\n this.mouse = { x: e.clientX * PX_RATIO, y: e.clientY * PX_RATIO };\n }\n\n get dx() {\n return this.mouse.x - this.center.x;\n }\n\n get dy() {\n return this.mouse.y - this.center.y;\n }\n\n hue() {\n const deg = (Math.atan2(this.dy, this.dx) * 180) / Math.PI;\n this.deg += (deg - this.deg) * 0.075;\n this.domElement.style.filter = `hue-rotate(${this.deg.toFixed(1)}deg)`;\n }\n\n asciify(ctx, w, h) {\n if (w && h) {\n const imgData = ctx.getImageData(0, 0, w, h).data;\n let str = '';\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < w; x++) {\n const i = x * 4 + y * 4 * w;\n const [r, g, b, a] = [imgData[i], imgData[i + 1], imgData[i + 2], imgData[i + 3]];\n\n if (a === 0) {\n str += ' ';\n continue;\n }\n\n let gray = (0.3 * r + 0.6 * g + 0.1 * b) / 255;\n let idx = Math.floor((1 - gray) * (this.charset.length - 1));\n if (this.invert) idx = this.charset.length - idx - 1;\n str += this.charset[idx];\n }\n str += '\\n';\n }\n this.pre.innerHTML = str;\n }\n }\n\n dispose() {\n document.removeEventListener('mousemove', this.onMouseMove);\n }\n}\n\nclass CanvasTxt {\n constructor(txt, { fontSize = 200, fontFamily = 'Arial', color = '#fdf9f3' } = {}) {\n this.canvas = document.createElement('canvas');\n this.context = this.canvas.getContext('2d');\n this.txt = txt;\n this.fontSize = fontSize;\n this.fontFamily = fontFamily;\n this.color = color;\n\n this.font = `600 ${this.fontSize}px ${this.fontFamily}`;\n }\n\n resize() {\n this.context.font = this.font;\n const metrics = this.context.measureText(this.txt);\n\n const textWidth = Math.ceil(metrics.width) + 20;\n const textHeight = Math.ceil(metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent) + 20;\n\n this.canvas.width = textWidth;\n this.canvas.height = textHeight;\n }\n\n render() {\n this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);\n this.context.fillStyle = this.color;\n this.context.font = this.font;\n\n const metrics = this.context.measureText(this.txt);\n const yPos = 10 + metrics.actualBoundingBoxAscent;\n\n this.context.fillText(this.txt, 10, yPos);\n }\n\n get width() {\n return this.canvas.width;\n }\n\n get height() {\n return this.canvas.height;\n }\n\n get texture() {\n return this.canvas;\n }\n}\n\nclass CanvAscii {\n constructor(\n { text, asciiFontSize, textFontSize, textColor, planeBaseHeight, enableWaves },\n containerElem,\n width,\n height\n ) {\n this.textString = text;\n this.asciiFontSize = asciiFontSize;\n this.textFontSize = textFontSize;\n this.textColor = textColor;\n this.planeBaseHeight = planeBaseHeight;\n this.container = containerElem;\n this.width = width;\n this.height = height;\n this.enableWaves = enableWaves;\n\n this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 1, 1000);\n this.camera.position.z = 30;\n\n this.scene = new THREE.Scene();\n this.mouse = { x: this.width / 2, y: this.height / 2 };\n\n this.onMouseMove = this.onMouseMove.bind(this);\n }\n\n async init() {\n try {\n await document.fonts.load('600 200px \"IBM Plex Mono\"');\n await document.fonts.load('500 12px \"IBM Plex Mono\"');\n } catch (e) {}\n await document.fonts.ready;\n this.setMesh();\n this.setRenderer();\n }\n\n setMesh() {\n this.textCanvas = new CanvasTxt(this.textString, {\n fontSize: this.textFontSize,\n fontFamily: 'IBM Plex Mono',\n color: this.textColor\n });\n this.textCanvas.resize();\n this.textCanvas.render();\n\n this.texture = new THREE.CanvasTexture(this.textCanvas.texture);\n this.texture.minFilter = THREE.NearestFilter;\n\n const textAspect = this.textCanvas.width / this.textCanvas.height;\n const baseH = this.planeBaseHeight;\n const planeW = baseH * textAspect;\n const planeH = baseH;\n\n this.geometry = new THREE.PlaneGeometry(planeW, planeH, 36, 36);\n this.material = new THREE.ShaderMaterial({\n vertexShader,\n fragmentShader,\n transparent: true,\n uniforms: {\n uTime: { value: 0 },\n mouse: { value: 1.0 },\n uTexture: { value: this.texture },\n uEnableWaves: { value: this.enableWaves ? 1.0 : 0.0 }\n }\n });\n\n this.mesh = new THREE.Mesh(this.geometry, this.material);\n this.scene.add(this.mesh);\n }\n\n setRenderer() {\n this.renderer = new THREE.WebGLRenderer({ antialias: false, alpha: true });\n this.renderer.setPixelRatio(1);\n this.renderer.setClearColor(0x000000, 0);\n\n this.filter = new AsciiFilter(this.renderer, {\n fontFamily: 'IBM Plex Mono',\n fontSize: this.asciiFontSize,\n invert: true\n });\n\n this.container.appendChild(this.filter.domElement);\n this.setSize(this.width, this.height);\n\n this.container.addEventListener('mousemove', this.onMouseMove);\n this.container.addEventListener('touchmove', this.onMouseMove);\n }\n\n setSize(w, h) {\n this.width = w;\n this.height = h;\n\n this.camera.aspect = w / h;\n this.camera.updateProjectionMatrix();\n\n this.filter.setSize(w, h);\n\n this.center = { x: w / 2, y: h / 2 };\n }\n\n load() {\n this.animate();\n }\n\n onMouseMove(evt) {\n const e = evt.touches ? evt.touches[0] : evt;\n const bounds = this.container.getBoundingClientRect();\n const x = e.clientX - bounds.left;\n const y = e.clientY - bounds.top;\n this.mouse = { x, y };\n }\n\n animate() {\n const animateFrame = () => {\n this.animationFrameId = requestAnimationFrame(animateFrame);\n this.render();\n };\n animateFrame();\n }\n\n render() {\n const time = new Date().getTime() * 0.001;\n\n this.textCanvas.render();\n this.texture.needsUpdate = true;\n\n this.mesh.material.uniforms.uTime.value = Math.sin(time);\n\n this.updateRotation();\n this.filter.render(this.scene, this.camera);\n }\n\n updateRotation() {\n const x = Math.map(this.mouse.y, 0, this.height, 0.5, -0.5);\n const y = Math.map(this.mouse.x, 0, this.width, -0.5, 0.5);\n\n this.mesh.rotation.x += (x - this.mesh.rotation.x) * 0.05;\n this.mesh.rotation.y += (y - this.mesh.rotation.y) * 0.05;\n }\n\n clear() {\n this.scene.traverse(obj => {\n if (obj.isMesh && typeof obj.material === 'object' && obj.material !== null) {\n Object.keys(obj.material).forEach(key => {\n const matProp = obj.material[key];\n if (matProp !== null && typeof matProp === 'object' && typeof matProp.dispose === 'function') {\n matProp.dispose();\n }\n });\n obj.material.dispose();\n obj.geometry.dispose();\n }\n });\n this.scene.clear();\n }\n\n dispose() {\n cancelAnimationFrame(this.animationFrameId);\n if (this.filter) {\n this.filter.dispose();\n if (this.filter.domElement.parentNode) {\n this.container.removeChild(this.filter.domElement);\n }\n }\n this.container.removeEventListener('mousemove', this.onMouseMove);\n this.container.removeEventListener('touchmove', this.onMouseMove);\n this.clear();\n if (this.renderer) {\n this.renderer.dispose();\n this.renderer.forceContextLoss();\n }\n }\n}\n\nexport default function ASCIIText({\n text = 'David!',\n asciiFontSize = 8,\n textFontSize = 200,\n textColor = '#fdf9f3',\n planeBaseHeight = 8,\n enableWaves = true\n}) {\n const containerRef = useRef(null);\n const asciiRef = useRef(null);\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n let cancelled = false;\n let observer = null;\n let ro = null;\n\n const createAndInit = async (container, w, h) => {\n const instance = new CanvAscii(\n { text, asciiFontSize, textFontSize, textColor, planeBaseHeight, enableWaves },\n container,\n w,\n h\n );\n await instance.init();\n return instance;\n };\n\n const setup = async () => {\n const { width, height } = containerRef.current.getBoundingClientRect();\n\n if (width === 0 || height === 0) {\n observer = new IntersectionObserver(\n async ([entry]) => {\n if (cancelled) return;\n if (entry.isIntersecting && entry.boundingClientRect.width > 0 && entry.boundingClientRect.height > 0) {\n const { width: w, height: h } = entry.boundingClientRect;\n observer.disconnect();\n observer = null;\n\n if (!cancelled) {\n asciiRef.current = await createAndInit(containerRef.current, w, h);\n if (!cancelled && asciiRef.current) {\n asciiRef.current.load();\n }\n }\n }\n },\n { threshold: 0.1 }\n );\n observer.observe(containerRef.current);\n return;\n }\n\n asciiRef.current = await createAndInit(containerRef.current, width, height);\n if (!cancelled && asciiRef.current) {\n asciiRef.current.load();\n\n ro = new ResizeObserver(entries => {\n if (!entries[0] || !asciiRef.current) return;\n const { width: w, height: h } = entries[0].contentRect;\n if (w > 0 && h > 0) {\n asciiRef.current.setSize(w, h);\n }\n });\n ro.observe(containerRef.current);\n }\n };\n\n setup();\n\n return () => {\n cancelled = true;\n if (observer) observer.disconnect();\n if (ro) ro.disconnect();\n if (asciiRef.current) {\n asciiRef.current.dispose();\n asciiRef.current = null;\n }\n };\n }, [text, asciiFontSize, textFontSize, textColor, planeBaseHeight, enableWaves]);\n\n return (\n \n \n \n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "three@^0.167.1" ] } ================================================ FILE: public/r/ASCIIText-TS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "ASCIIText-TS-CSS", "title": "ASCIIText", "description": "Renders text with an animated ASCII background for a retro feel.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "ASCIIText/ASCIIText.tsx", "content": "// Component ported and enhanced from https://codepen.io/JuanFuentes/pen/eYEeoyE\n\nimport { useEffect, useRef } from 'react';\nimport * as THREE from 'three';\n\nconst vertexShader = `\nvarying vec2 vUv;\nuniform float uTime;\nuniform float mouse;\nuniform float uEnableWaves;\n\nvoid main() {\n vUv = uv;\n float time = uTime * 5.;\n\n float waveFactor = uEnableWaves;\n\n vec3 transformed = position;\n\n transformed.x += sin(time + position.y) * 0.5 * waveFactor;\n transformed.y += cos(time + position.z) * 0.15 * waveFactor;\n transformed.z += sin(time + position.x) * waveFactor;\n\n gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);\n}\n`;\n\nconst fragmentShader = `\nvarying vec2 vUv;\nuniform float mouse;\nuniform float uTime;\nuniform sampler2D uTexture;\n\nvoid main() {\n float time = uTime;\n vec2 pos = vUv;\n \n float move = sin(time + mouse) * 0.01;\n float r = texture2D(uTexture, pos + cos(time * 2. - time + pos.x) * .01).r;\n float g = texture2D(uTexture, pos + tan(time * .5 + pos.x - time) * .01).g;\n float b = texture2D(uTexture, pos - cos(time * 2. + time + pos.y) * .01).b;\n float a = texture2D(uTexture, pos).a;\n gl_FragColor = vec4(r, g, b, a);\n}\n`;\n\nfunction map(n: number, start: number, stop: number, start2: number, stop2: number) {\n return ((n - start) / (stop - start)) * (stop2 - start2) + start2;\n}\n\nconst PX_RATIO = typeof window !== 'undefined' ? window.devicePixelRatio : 1;\n\ninterface AsciiFilterOptions {\n fontSize?: number;\n fontFamily?: string;\n charset?: string;\n invert?: boolean;\n}\n\nclass AsciiFilter {\n renderer!: THREE.WebGLRenderer;\n domElement: HTMLDivElement;\n pre: HTMLPreElement;\n canvas: HTMLCanvasElement;\n context: CanvasRenderingContext2D | null;\n deg: number;\n invert: boolean;\n fontSize: number;\n fontFamily: string;\n charset: string;\n width: number = 0;\n height: number = 0;\n center: { x: number; y: number } = { x: 0, y: 0 };\n mouse: { x: number; y: number } = { x: 0, y: 0 };\n cols: number = 0;\n rows: number = 0;\n\n constructor(renderer: THREE.WebGLRenderer, { fontSize, fontFamily, charset, invert }: AsciiFilterOptions = {}) {\n this.renderer = renderer;\n this.domElement = document.createElement('div');\n this.domElement.style.position = 'absolute';\n this.domElement.style.top = '0';\n this.domElement.style.left = '0';\n this.domElement.style.width = '100%';\n this.domElement.style.height = '100%';\n\n this.pre = document.createElement('pre');\n this.domElement.appendChild(this.pre);\n\n this.canvas = document.createElement('canvas');\n this.context = this.canvas.getContext('2d');\n this.domElement.appendChild(this.canvas);\n\n this.deg = 0;\n this.invert = invert ?? true;\n this.fontSize = fontSize ?? 12;\n this.fontFamily = fontFamily ?? \"'Courier New', monospace\";\n this.charset = charset ?? ' .\\'`^\",:;Il!i~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$';\n\n if (this.context) {\n this.context.imageSmoothingEnabled = false;\n this.context.imageSmoothingEnabled = false;\n }\n\n this.onMouseMove = this.onMouseMove.bind(this);\n document.addEventListener('mousemove', this.onMouseMove);\n }\n\n setSize(width: number, height: number) {\n this.width = width;\n this.height = height;\n this.renderer.setSize(width, height);\n this.reset();\n\n this.center = { x: width / 2, y: height / 2 };\n this.mouse = { x: this.center.x, y: this.center.y };\n }\n\n reset() {\n if (this.context) {\n this.context.font = `${this.fontSize}px ${this.fontFamily}`;\n const charWidth = this.context.measureText('A').width;\n\n this.cols = Math.floor(this.width / (this.fontSize * (charWidth / this.fontSize)));\n this.rows = Math.floor(this.height / this.fontSize);\n\n this.canvas.width = this.cols;\n this.canvas.height = this.rows;\n this.pre.style.fontFamily = this.fontFamily;\n this.pre.style.fontSize = `${this.fontSize}px`;\n this.pre.style.margin = '0';\n this.pre.style.padding = '0';\n this.pre.style.lineHeight = '1em';\n this.pre.style.position = 'absolute';\n this.pre.style.left = '50%';\n this.pre.style.top = '50%';\n this.pre.style.transform = 'translate(-50%, -50%)';\n this.pre.style.zIndex = '9';\n this.pre.style.backgroundAttachment = 'fixed';\n this.pre.style.mixBlendMode = 'difference';\n }\n }\n\n render(scene: THREE.Scene, camera: THREE.Camera) {\n this.renderer.render(scene, camera);\n\n const w = this.canvas.width;\n const h = this.canvas.height;\n if (this.context) {\n this.context.clearRect(0, 0, w, h);\n this.context.drawImage(this.renderer.domElement, 0, 0, w, h);\n this.asciify(this.context, w, h);\n this.hue();\n }\n }\n\n onMouseMove(e: MouseEvent) {\n this.mouse = { x: e.clientX * PX_RATIO, y: e.clientY * PX_RATIO };\n }\n\n get dx() {\n return this.mouse.x - this.center.x;\n }\n\n get dy() {\n return this.mouse.y - this.center.y;\n }\n\n hue() {\n const deg = (Math.atan2(this.dy, this.dx) * 180) / Math.PI;\n this.deg += (deg - this.deg) * 0.075;\n this.domElement.style.filter = `hue-rotate(${this.deg.toFixed(1)}deg)`;\n }\n\n asciify(ctx: CanvasRenderingContext2D, w: number, h: number) {\n const imgData = ctx.getImageData(0, 0, w, h).data;\n let str = '';\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < w; x++) {\n const i = x * 4 + y * 4 * w;\n const [r, g, b, a] = [imgData[i], imgData[i + 1], imgData[i + 2], imgData[i + 3]];\n\n if (a === 0) {\n str += ' ';\n continue;\n }\n\n let gray = (0.3 * r + 0.6 * g + 0.1 * b) / 255;\n let idx = Math.floor((1 - gray) * (this.charset.length - 1));\n if (this.invert) idx = this.charset.length - idx - 1;\n str += this.charset[idx];\n }\n str += '\\n';\n }\n this.pre.innerHTML = str;\n }\n\n dispose() {\n document.removeEventListener('mousemove', this.onMouseMove);\n }\n}\n\ninterface CanvasTxtOptions {\n fontSize?: number;\n fontFamily?: string;\n color?: string;\n}\n\nclass CanvasTxt {\n canvas: HTMLCanvasElement;\n context: CanvasRenderingContext2D | null;\n txt: string;\n fontSize: number;\n fontFamily: string;\n color: string;\n font: string;\n\n constructor(txt: string, { fontSize = 200, fontFamily = 'Arial', color = '#fdf9f3' }: CanvasTxtOptions = {}) {\n this.canvas = document.createElement('canvas');\n this.context = this.canvas.getContext('2d');\n this.txt = txt;\n this.fontSize = fontSize;\n this.fontFamily = fontFamily;\n this.color = color;\n\n this.font = `600 ${this.fontSize}px ${this.fontFamily}`;\n }\n\n resize() {\n if (this.context) {\n this.context.font = this.font;\n const metrics = this.context.measureText(this.txt);\n\n const textWidth = Math.ceil(metrics.width) + 20;\n const textHeight = Math.ceil(metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent) + 20;\n\n this.canvas.width = textWidth;\n this.canvas.height = textHeight;\n }\n }\n\n render() {\n if (this.context) {\n this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);\n this.context.fillStyle = this.color;\n this.context.font = this.font;\n\n const metrics = this.context.measureText(this.txt);\n const yPos = 10 + metrics.actualBoundingBoxAscent;\n\n this.context.fillText(this.txt, 10, yPos);\n }\n }\n\n get width() {\n return this.canvas.width;\n }\n\n get height() {\n return this.canvas.height;\n }\n\n get texture() {\n return this.canvas;\n }\n}\n\ninterface CanvAsciiOptions {\n text: string;\n asciiFontSize: number;\n textFontSize: number;\n textColor: string;\n planeBaseHeight: number;\n enableWaves: boolean;\n}\n\nclass CanvAscii {\n textString: string;\n asciiFontSize: number;\n textFontSize: number;\n textColor: string;\n planeBaseHeight: number;\n container: HTMLElement;\n width: number;\n height: number;\n enableWaves: boolean;\n camera: THREE.PerspectiveCamera;\n scene: THREE.Scene;\n mouse: { x: number; y: number };\n textCanvas!: CanvasTxt;\n texture!: THREE.CanvasTexture;\n geometry: THREE.PlaneGeometry | undefined;\n material: THREE.ShaderMaterial | undefined;\n mesh!: THREE.Mesh;\n renderer!: THREE.WebGLRenderer;\n filter!: AsciiFilter;\n center: { x: number; y: number } = { x: 0, y: 0 };\n animationFrameId: number = 0;\n\n constructor(\n { text, asciiFontSize, textFontSize, textColor, planeBaseHeight, enableWaves }: CanvAsciiOptions,\n containerElem: HTMLElement,\n width: number,\n height: number\n ) {\n this.textString = text;\n this.asciiFontSize = asciiFontSize;\n this.textFontSize = textFontSize;\n this.textColor = textColor;\n this.planeBaseHeight = planeBaseHeight;\n this.container = containerElem;\n this.width = width;\n this.height = height;\n this.enableWaves = enableWaves;\n\n this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 1, 1000);\n this.camera.position.z = 30;\n\n this.scene = new THREE.Scene();\n this.mouse = { x: this.width / 2, y: this.height / 2 };\n\n this.onMouseMove = this.onMouseMove.bind(this);\n }\n\n async init() {\n try {\n await document.fonts.load('600 200px \"IBM Plex Mono\"');\n await document.fonts.load('500 12px \"IBM Plex Mono\"');\n } catch (e) {}\n await document.fonts.ready;\n this.setMesh();\n this.setRenderer();\n }\n\n setMesh() {\n this.textCanvas = new CanvasTxt(this.textString, {\n fontSize: this.textFontSize,\n fontFamily: 'IBM Plex Mono',\n color: this.textColor\n });\n this.textCanvas.resize();\n this.textCanvas.render();\n\n this.texture = new THREE.CanvasTexture(this.textCanvas.texture);\n this.texture.minFilter = THREE.NearestFilter;\n\n const textAspect = this.textCanvas.width / this.textCanvas.height;\n const baseH = this.planeBaseHeight;\n const planeW = baseH * textAspect;\n const planeH = baseH;\n\n this.geometry = new THREE.PlaneGeometry(planeW, planeH, 36, 36);\n this.material = new THREE.ShaderMaterial({\n vertexShader,\n fragmentShader,\n transparent: true,\n uniforms: {\n uTime: { value: 0 },\n mouse: { value: 1.0 },\n uTexture: { value: this.texture },\n uEnableWaves: { value: this.enableWaves ? 1.0 : 0.0 }\n }\n });\n\n this.mesh = new THREE.Mesh(this.geometry, this.material);\n this.scene.add(this.mesh);\n }\n\n setRenderer() {\n this.renderer = new THREE.WebGLRenderer({ antialias: false, alpha: true });\n this.renderer.setPixelRatio(1);\n this.renderer.setClearColor(0x000000, 0);\n\n this.filter = new AsciiFilter(this.renderer, {\n fontFamily: 'IBM Plex Mono',\n fontSize: this.asciiFontSize,\n invert: true\n });\n\n this.container.appendChild(this.filter.domElement);\n this.setSize(this.width, this.height);\n\n this.container.addEventListener('mousemove', this.onMouseMove);\n this.container.addEventListener('touchmove', this.onMouseMove);\n }\n\n setSize(w: number, h: number) {\n this.width = w;\n this.height = h;\n\n this.camera.aspect = w / h;\n this.camera.updateProjectionMatrix();\n\n this.filter.setSize(w, h);\n\n this.center = { x: w / 2, y: h / 2 };\n }\n\n load() {\n this.animate();\n }\n\n onMouseMove(evt: MouseEvent | TouchEvent) {\n const e = (evt as TouchEvent).touches ? (evt as TouchEvent).touches[0] : (evt as MouseEvent);\n const bounds = this.container.getBoundingClientRect();\n const x = e.clientX - bounds.left;\n const y = e.clientY - bounds.top;\n this.mouse = { x, y };\n }\n\n animate() {\n const animateFrame = () => {\n this.animationFrameId = requestAnimationFrame(animateFrame);\n this.render();\n };\n animateFrame();\n }\n\n render() {\n const time = new Date().getTime() * 0.001;\n\n this.textCanvas.render();\n this.texture.needsUpdate = true;\n\n (this.mesh.material as THREE.ShaderMaterial).uniforms.uTime.value = Math.sin(time);\n\n this.updateRotation();\n this.filter.render(this.scene, this.camera);\n }\n\n updateRotation() {\n const x = map(this.mouse.y, 0, this.height, 0.5, -0.5);\n const y = map(this.mouse.x, 0, this.width, -0.5, 0.5);\n\n this.mesh.rotation.x += (x - this.mesh.rotation.x) * 0.05;\n this.mesh.rotation.y += (y - this.mesh.rotation.y) * 0.05;\n }\n\n clear() {\n this.scene.traverse(object => {\n const obj = object as unknown as THREE.Mesh;\n if (!obj.isMesh) return;\n [obj.material].flat().forEach(material => {\n material.dispose();\n Object.keys(material).forEach(key => {\n const matProp = material[key as keyof typeof material];\n if (matProp && typeof matProp === 'object' && 'dispose' in matProp && typeof matProp.dispose === 'function') {\n matProp.dispose();\n }\n });\n });\n obj.geometry.dispose();\n });\n this.scene.clear();\n }\n\n dispose() {\n cancelAnimationFrame(this.animationFrameId);\n if (this.filter) {\n this.filter.dispose();\n if (this.filter.domElement.parentNode) {\n this.container.removeChild(this.filter.domElement);\n }\n }\n this.container.removeEventListener('mousemove', this.onMouseMove);\n this.container.removeEventListener('touchmove', this.onMouseMove);\n this.clear();\n if (this.renderer) {\n this.renderer.dispose();\n this.renderer.forceContextLoss();\n }\n }\n}\n\ninterface ASCIITextProps {\n text?: string;\n asciiFontSize?: number;\n textFontSize?: number;\n textColor?: string;\n planeBaseHeight?: number;\n enableWaves?: boolean;\n}\n\nexport default function ASCIIText({\n text = 'David!',\n asciiFontSize = 8,\n textFontSize = 200,\n textColor = '#fdf9f3',\n planeBaseHeight = 8,\n enableWaves = true\n}: ASCIITextProps) {\n const containerRef = useRef(null);\n const asciiRef = useRef(null);\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n let cancelled = false;\n let observer: IntersectionObserver | null = null;\n let ro: ResizeObserver | null = null;\n\n const createAndInit = async (container: HTMLDivElement, w: number, h: number) => {\n const instance = new CanvAscii(\n { text, asciiFontSize, textFontSize, textColor, planeBaseHeight, enableWaves },\n container,\n w,\n h\n );\n await instance.init();\n return instance;\n };\n\n const setup = async () => {\n const { width, height } = containerRef.current!.getBoundingClientRect();\n\n if (width === 0 || height === 0) {\n observer = new IntersectionObserver(\n async ([entry]) => {\n if (cancelled) return;\n if (entry.isIntersecting && entry.boundingClientRect.width > 0 && entry.boundingClientRect.height > 0) {\n const { width: w, height: h } = entry.boundingClientRect;\n observer?.disconnect();\n observer = null;\n\n if (!cancelled) {\n asciiRef.current = await createAndInit(containerRef.current!, w, h);\n if (!cancelled && asciiRef.current) {\n asciiRef.current.load();\n }\n }\n }\n },\n { threshold: 0.1 }\n );\n observer.observe(containerRef.current!);\n return;\n }\n\n asciiRef.current = await createAndInit(containerRef.current!, width, height);\n if (!cancelled && asciiRef.current) {\n asciiRef.current.load();\n\n ro = new ResizeObserver(entries => {\n if (!entries[0] || !asciiRef.current) return;\n const { width: w, height: h } = entries[0].contentRect;\n if (w > 0 && h > 0) {\n asciiRef.current.setSize(w, h);\n }\n });\n ro.observe(containerRef.current!);\n }\n };\n\n setup();\n\n return () => {\n cancelled = true;\n if (observer) observer.disconnect();\n if (ro) ro.disconnect();\n if (asciiRef.current) {\n asciiRef.current.dispose();\n asciiRef.current = null;\n }\n };\n }, [text, asciiFontSize, textFontSize, textColor, planeBaseHeight, enableWaves]);\n\n return (\n \n \n \n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "three@^0.167.1" ] } ================================================ FILE: public/r/ASCIIText-TS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "ASCIIText-TS-TW", "title": "ASCIIText", "description": "Renders text with an animated ASCII background for a retro feel.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "ASCIIText/ASCIIText.tsx", "content": "// Component ported and enhanced from https://codepen.io/JuanFuentes/pen/eYEeoyE\n\nimport { useEffect, useRef } from 'react';\nimport * as THREE from 'three';\n\nconst vertexShader = `\nvarying vec2 vUv;\nuniform float uTime;\nuniform float mouse;\nuniform float uEnableWaves;\n\nvoid main() {\n vUv = uv;\n float time = uTime * 5.;\n\n float waveFactor = uEnableWaves;\n\n vec3 transformed = position;\n\n transformed.x += sin(time + position.y) * 0.5 * waveFactor;\n transformed.y += cos(time + position.z) * 0.15 * waveFactor;\n transformed.z += sin(time + position.x) * waveFactor;\n\n gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);\n}\n`;\n\nconst fragmentShader = `\nvarying vec2 vUv;\nuniform float mouse;\nuniform float uTime;\nuniform sampler2D uTexture;\n\nvoid main() {\n float time = uTime;\n vec2 pos = vUv;\n \n float move = sin(time + mouse) * 0.01;\n float r = texture2D(uTexture, pos + cos(time * 2. - time + pos.x) * .01).r;\n float g = texture2D(uTexture, pos + tan(time * .5 + pos.x - time) * .01).g;\n float b = texture2D(uTexture, pos - cos(time * 2. + time + pos.y) * .01).b;\n float a = texture2D(uTexture, pos).a;\n gl_FragColor = vec4(r, g, b, a);\n}\n`;\n\nfunction map(n: number, start: number, stop: number, start2: number, stop2: number) {\n return ((n - start) / (stop - start)) * (stop2 - start2) + start2;\n}\n\nconst PX_RATIO = typeof window !== 'undefined' ? window.devicePixelRatio : 1;\n\ninterface AsciiFilterOptions {\n fontSize?: number;\n fontFamily?: string;\n charset?: string;\n invert?: boolean;\n}\n\nclass AsciiFilter {\n renderer: THREE.WebGLRenderer;\n domElement: HTMLDivElement;\n pre: HTMLPreElement;\n canvas: HTMLCanvasElement;\n context: CanvasRenderingContext2D | null;\n deg: number;\n invert: boolean;\n fontSize: number;\n fontFamily: string;\n charset: string;\n width: number = 0;\n height: number = 0;\n center: { x: number; y: number } = { x: 0, y: 0 };\n mouse: { x: number; y: number } = { x: 0, y: 0 };\n cols: number = 0;\n rows: number = 0;\n\n constructor(renderer: THREE.WebGLRenderer, { fontSize, fontFamily, charset, invert }: AsciiFilterOptions = {}) {\n this.renderer = renderer;\n this.domElement = document.createElement('div');\n this.domElement.style.position = 'absolute';\n this.domElement.style.top = '0';\n this.domElement.style.left = '0';\n this.domElement.style.width = '100%';\n this.domElement.style.height = '100%';\n\n this.pre = document.createElement('pre');\n this.domElement.appendChild(this.pre);\n\n this.canvas = document.createElement('canvas');\n this.context = this.canvas.getContext('2d');\n this.domElement.appendChild(this.canvas);\n\n this.deg = 0;\n this.invert = invert ?? true;\n this.fontSize = fontSize ?? 12;\n this.fontFamily = fontFamily ?? \"'Courier New', monospace\";\n this.charset = charset ?? ' .\\'`^\",:;Il!i~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$';\n\n if (this.context) {\n this.context.imageSmoothingEnabled = false;\n this.context.imageSmoothingEnabled = false;\n }\n\n this.onMouseMove = this.onMouseMove.bind(this);\n document.addEventListener('mousemove', this.onMouseMove);\n }\n\n setSize(width: number, height: number) {\n this.width = width;\n this.height = height;\n this.renderer.setSize(width, height);\n this.reset();\n\n this.center = { x: width / 2, y: height / 2 };\n this.mouse = { x: this.center.x, y: this.center.y };\n }\n\n reset() {\n if (this.context) {\n this.context.font = `${this.fontSize}px ${this.fontFamily}`;\n const charWidth = this.context.measureText('A').width;\n\n this.cols = Math.floor(this.width / (this.fontSize * (charWidth / this.fontSize)));\n this.rows = Math.floor(this.height / this.fontSize);\n\n this.canvas.width = this.cols;\n this.canvas.height = this.rows;\n this.pre.style.fontFamily = this.fontFamily;\n this.pre.style.fontSize = `${this.fontSize}px`;\n this.pre.style.margin = '0';\n this.pre.style.padding = '0';\n this.pre.style.lineHeight = '1em';\n this.pre.style.position = 'absolute';\n this.pre.style.left = '50%';\n this.pre.style.top = '50%';\n this.pre.style.transform = 'translate(-50%, -50%)';\n this.pre.style.zIndex = '9';\n this.pre.style.backgroundAttachment = 'fixed';\n this.pre.style.mixBlendMode = 'difference';\n }\n }\n\n render(scene: THREE.Scene, camera: THREE.Camera) {\n this.renderer.render(scene, camera);\n\n const w = this.canvas.width;\n const h = this.canvas.height;\n if (this.context) {\n this.context.clearRect(0, 0, w, h);\n if (this.context && w && h) {\n this.context.drawImage(this.renderer.domElement, 0, 0, w, h);\n }\n\n this.asciify(this.context, w, h);\n this.hue();\n }\n }\n\n onMouseMove(e: MouseEvent) {\n this.mouse = { x: e.clientX * PX_RATIO, y: e.clientY * PX_RATIO };\n }\n\n get dx() {\n return this.mouse.x - this.center.x;\n }\n\n get dy() {\n return this.mouse.y - this.center.y;\n }\n\n hue() {\n const deg = (Math.atan2(this.dy, this.dx) * 180) / Math.PI;\n this.deg += (deg - this.deg) * 0.075;\n this.domElement.style.filter = `hue-rotate(${this.deg.toFixed(1)}deg)`;\n }\n\n asciify(ctx: CanvasRenderingContext2D, w: number, h: number) {\n if (w && h) {\n const imgData = ctx.getImageData(0, 0, w, h).data;\n let str = '';\n for (let y = 0; y < h; y++) {\n for (let x = 0; x < w; x++) {\n const i = x * 4 + y * 4 * w;\n const [r, g, b, a] = [imgData[i], imgData[i + 1], imgData[i + 2], imgData[i + 3]];\n\n if (a === 0) {\n str += ' ';\n continue;\n }\n\n let gray = (0.3 * r + 0.6 * g + 0.1 * b) / 255;\n let idx = Math.floor((1 - gray) * (this.charset.length - 1));\n if (this.invert) idx = this.charset.length - idx - 1;\n str += this.charset[idx];\n }\n str += '\\n';\n }\n this.pre.innerHTML = str;\n }\n }\n\n dispose() {\n document.removeEventListener('mousemove', this.onMouseMove);\n }\n}\n\ninterface CanvasTxtOptions {\n fontSize?: number;\n fontFamily?: string;\n color?: string;\n}\n\nclass CanvasTxt {\n canvas: HTMLCanvasElement;\n context: CanvasRenderingContext2D | null;\n txt: string;\n fontSize: number;\n fontFamily: string;\n color: string;\n font: string;\n\n constructor(txt: string, { fontSize = 200, fontFamily = 'Arial', color = '#fdf9f3' }: CanvasTxtOptions = {}) {\n this.canvas = document.createElement('canvas');\n this.context = this.canvas.getContext('2d');\n this.txt = txt;\n this.fontSize = fontSize;\n this.fontFamily = fontFamily;\n this.color = color;\n\n this.font = `600 ${this.fontSize}px ${this.fontFamily}`;\n }\n\n resize() {\n if (this.context) {\n this.context.font = this.font;\n const metrics = this.context.measureText(this.txt);\n\n const textWidth = Math.ceil(metrics.width) + 20;\n const textHeight = Math.ceil(metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent) + 20;\n\n this.canvas.width = textWidth;\n this.canvas.height = textHeight;\n }\n }\n\n render() {\n if (this.context) {\n this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);\n this.context.fillStyle = this.color;\n this.context.font = this.font;\n\n const metrics = this.context.measureText(this.txt);\n const yPos = 10 + metrics.actualBoundingBoxAscent;\n\n this.context.fillText(this.txt, 10, yPos);\n }\n }\n\n get width() {\n return this.canvas.width;\n }\n\n get height() {\n return this.canvas.height;\n }\n\n get texture() {\n return this.canvas;\n }\n}\n\ninterface CanvAsciiOptions {\n text: string;\n asciiFontSize: number;\n textFontSize: number;\n textColor: string;\n planeBaseHeight: number;\n enableWaves: boolean;\n}\n\nclass CanvAscii {\n textString: string;\n asciiFontSize: number;\n textFontSize: number;\n textColor: string;\n planeBaseHeight: number;\n container: HTMLElement;\n width: number;\n height: number;\n enableWaves: boolean;\n camera: THREE.PerspectiveCamera;\n scene: THREE.Scene;\n mouse: { x: number; y: number };\n textCanvas!: CanvasTxt;\n texture!: THREE.CanvasTexture;\n geometry!: THREE.PlaneGeometry;\n material!: THREE.ShaderMaterial;\n mesh!: THREE.Mesh;\n renderer!: THREE.WebGLRenderer;\n filter!: AsciiFilter;\n center!: { x: number; y: number };\n animationFrameId: number = 0;\n\n constructor(\n { text, asciiFontSize, textFontSize, textColor, planeBaseHeight, enableWaves }: CanvAsciiOptions,\n containerElem: HTMLElement,\n width: number,\n height: number\n ) {\n this.textString = text;\n this.asciiFontSize = asciiFontSize;\n this.textFontSize = textFontSize;\n this.textColor = textColor;\n this.planeBaseHeight = planeBaseHeight;\n this.container = containerElem;\n this.width = width;\n this.height = height;\n this.enableWaves = enableWaves;\n\n this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 1, 1000);\n this.camera.position.z = 30;\n\n this.scene = new THREE.Scene();\n this.mouse = { x: this.width / 2, y: this.height / 2 };\n\n this.onMouseMove = this.onMouseMove.bind(this);\n }\n\n async init() {\n try {\n await document.fonts.load('600 200px \"IBM Plex Mono\"');\n await document.fonts.load('500 12px \"IBM Plex Mono\"');\n } catch (e) {}\n await document.fonts.ready;\n this.setMesh();\n this.setRenderer();\n }\n\n setMesh() {\n this.textCanvas = new CanvasTxt(this.textString, {\n fontSize: this.textFontSize,\n fontFamily: 'IBM Plex Mono',\n color: this.textColor\n });\n this.textCanvas.resize();\n this.textCanvas.render();\n\n this.texture = new THREE.CanvasTexture(this.textCanvas.texture);\n this.texture.minFilter = THREE.NearestFilter;\n\n const textAspect = this.textCanvas.width / this.textCanvas.height;\n const baseH = this.planeBaseHeight;\n const planeW = baseH * textAspect;\n const planeH = baseH;\n\n this.geometry = new THREE.PlaneGeometry(planeW, planeH, 36, 36);\n this.material = new THREE.ShaderMaterial({\n vertexShader,\n fragmentShader,\n transparent: true,\n uniforms: {\n uTime: { value: 0 },\n mouse: { value: 1.0 },\n uTexture: { value: this.texture },\n uEnableWaves: { value: this.enableWaves ? 1.0 : 0.0 }\n }\n });\n\n this.mesh = new THREE.Mesh(this.geometry, this.material);\n this.scene.add(this.mesh);\n }\n\n setRenderer() {\n this.renderer = new THREE.WebGLRenderer({ antialias: false, alpha: true });\n this.renderer.setPixelRatio(1);\n this.renderer.setClearColor(0x000000, 0);\n\n this.filter = new AsciiFilter(this.renderer, {\n fontFamily: 'IBM Plex Mono',\n fontSize: this.asciiFontSize,\n invert: true\n });\n\n this.container.appendChild(this.filter.domElement);\n this.setSize(this.width, this.height);\n\n this.container.addEventListener('mousemove', this.onMouseMove);\n this.container.addEventListener('touchmove', this.onMouseMove);\n }\n\n setSize(w: number, h: number) {\n this.width = w;\n this.height = h;\n\n this.camera.aspect = w / h;\n this.camera.updateProjectionMatrix();\n\n this.filter.setSize(w, h);\n\n this.center = { x: w / 2, y: h / 2 };\n }\n\n load() {\n this.animate();\n }\n\n onMouseMove(evt: MouseEvent | TouchEvent) {\n const e = (evt as TouchEvent).touches ? (evt as TouchEvent).touches[0] : (evt as MouseEvent);\n const bounds = this.container.getBoundingClientRect();\n const x = e.clientX - bounds.left;\n const y = e.clientY - bounds.top;\n this.mouse = { x, y };\n }\n\n animate() {\n const animateFrame = () => {\n this.animationFrameId = requestAnimationFrame(animateFrame);\n this.render();\n };\n animateFrame();\n }\n\n render() {\n const time = new Date().getTime() * 0.001;\n\n this.textCanvas.render();\n this.texture.needsUpdate = true;\n\n (this.mesh.material as THREE.ShaderMaterial).uniforms.uTime.value = Math.sin(time);\n\n this.updateRotation();\n this.filter.render(this.scene, this.camera);\n }\n\n updateRotation() {\n const x = map(this.mouse.y, 0, this.height, 0.5, -0.5);\n const y = map(this.mouse.x, 0, this.width, -0.5, 0.5);\n\n this.mesh.rotation.x += (x - this.mesh.rotation.x) * 0.05;\n this.mesh.rotation.y += (y - this.mesh.rotation.y) * 0.05;\n }\n\n clear() {\n this.scene.traverse(object => {\n const obj = object as unknown as THREE.Mesh;\n if (!obj.isMesh) return;\n [obj.material].flat().forEach(material => {\n material.dispose();\n Object.keys(material).forEach(key => {\n const matProp = material[key as keyof typeof material];\n if (matProp && typeof matProp === 'object' && 'dispose' in matProp && typeof matProp.dispose === 'function') {\n matProp.dispose();\n }\n });\n });\n obj.geometry.dispose();\n });\n this.scene.clear();\n }\n\n dispose() {\n cancelAnimationFrame(this.animationFrameId);\n if (this.filter) {\n this.filter.dispose();\n if (this.filter.domElement.parentNode) {\n this.container.removeChild(this.filter.domElement);\n }\n }\n this.container.removeEventListener('mousemove', this.onMouseMove);\n this.container.removeEventListener('touchmove', this.onMouseMove);\n this.clear();\n if (this.renderer) {\n this.renderer.dispose();\n this.renderer.forceContextLoss();\n }\n }\n}\n\ninterface ASCIITextProps {\n text?: string;\n asciiFontSize?: number;\n textFontSize?: number;\n textColor?: string;\n planeBaseHeight?: number;\n enableWaves?: boolean;\n}\n\nexport default function ASCIIText({\n text = 'David!',\n asciiFontSize = 8,\n textFontSize = 200,\n textColor = '#fdf9f3',\n planeBaseHeight = 8,\n enableWaves = true\n}: ASCIITextProps) {\n const containerRef = useRef(null);\n const asciiRef = useRef(null);\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n let cancelled = false;\n let observer: IntersectionObserver | null = null;\n let ro: ResizeObserver | null = null;\n\n const createAndInit = async (container: HTMLDivElement, w: number, h: number) => {\n const instance = new CanvAscii(\n { text, asciiFontSize, textFontSize, textColor, planeBaseHeight, enableWaves },\n container,\n w,\n h\n );\n await instance.init();\n return instance;\n };\n\n const setup = async () => {\n const { width, height } = containerRef.current!.getBoundingClientRect();\n\n if (width === 0 || height === 0) {\n observer = new IntersectionObserver(\n async ([entry]) => {\n if (cancelled) return;\n if (entry.isIntersecting && entry.boundingClientRect.width > 0 && entry.boundingClientRect.height > 0) {\n const { width: w, height: h } = entry.boundingClientRect;\n observer?.disconnect();\n observer = null;\n\n if (!cancelled) {\n asciiRef.current = await createAndInit(containerRef.current!, w, h);\n if (!cancelled && asciiRef.current) {\n asciiRef.current.load();\n }\n }\n }\n },\n { threshold: 0.1 }\n );\n observer.observe(containerRef.current!);\n return;\n }\n\n asciiRef.current = await createAndInit(containerRef.current!, width, height);\n if (!cancelled && asciiRef.current) {\n asciiRef.current.load();\n\n ro = new ResizeObserver(entries => {\n if (!entries[0] || !asciiRef.current) return;\n const { width: w, height: h } = entries[0].contentRect;\n if (w > 0 && h > 0) {\n asciiRef.current.setSize(w, h);\n }\n });\n ro.observe(containerRef.current!);\n }\n };\n\n setup();\n\n return () => {\n cancelled = true;\n if (observer) observer.disconnect();\n if (ro) ro.disconnect();\n if (asciiRef.current) {\n asciiRef.current.dispose();\n asciiRef.current = null;\n }\n };\n }, [text, asciiFontSize, textFontSize, textColor, planeBaseHeight, enableWaves]);\n\n return (\n \n \n \n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "three@^0.167.1" ] } ================================================ FILE: public/r/AnimatedContent-JS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "AnimatedContent-JS-CSS", "title": "AnimatedContent", "description": "Wrapper that animates any children on scroll or mount with configurable direction, distance, duration, easing and disappear options.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "AnimatedContent/AnimatedContent.jsx", "content": "import { useRef, useEffect } from 'react';\nimport { gsap } from 'gsap';\nimport { ScrollTrigger } from 'gsap/ScrollTrigger';\n\ngsap.registerPlugin(ScrollTrigger);\n\nconst AnimatedContent = ({\n children,\n container,\n distance = 100,\n direction = 'vertical',\n reverse = false,\n duration = 0.8,\n ease = 'power3.out',\n initialOpacity = 0,\n animateOpacity = true,\n scale = 1,\n threshold = 0.1,\n delay = 0,\n disappearAfter = 0,\n disappearDuration = 0.5,\n disappearEase = 'power3.in',\n onComplete,\n onDisappearanceComplete,\n className = '',\n ...props\n}) => {\n const ref = useRef(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n let scrollerTarget = container || document.getElementById('snap-main-container') || null;\n\n if (typeof scrollerTarget === 'string') {\n scrollerTarget = document.querySelector(scrollerTarget);\n }\n\n const axis = direction === 'horizontal' ? 'x' : 'y';\n const offset = reverse ? -distance : distance;\n const startPct = (1 - threshold) * 100;\n\n gsap.set(el, {\n [axis]: offset,\n scale,\n opacity: animateOpacity ? initialOpacity : 1,\n visibility: 'visible'\n });\n\n const tl = gsap.timeline({\n paused: true,\n delay,\n onComplete: () => {\n if (onComplete) onComplete();\n if (disappearAfter > 0) {\n gsap.to(el, {\n [axis]: reverse ? distance : -distance,\n scale: 0.8,\n opacity: animateOpacity ? initialOpacity : 0,\n delay: disappearAfter,\n duration: disappearDuration,\n ease: disappearEase,\n onComplete: () => onDisappearanceComplete?.()\n });\n }\n }\n });\n\n tl.to(el, {\n [axis]: 0,\n scale: 1,\n opacity: 1,\n duration,\n ease\n });\n\n const st = ScrollTrigger.create({\n trigger: el,\n scroller: scrollerTarget,\n start: `top ${startPct}%`,\n once: true,\n onEnter: () => tl.play()\n });\n\n return () => {\n st.kill();\n tl.kill();\n };\n }, [\n container,\n distance,\n direction,\n reverse,\n duration,\n ease,\n initialOpacity,\n animateOpacity,\n scale,\n threshold,\n delay,\n disappearAfter,\n disappearDuration,\n disappearEase,\n onComplete,\n onDisappearanceComplete\n ]);\n\n return (\n
\n {children}\n
\n );\n};\n\nexport default AnimatedContent;\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/AnimatedContent-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "AnimatedContent-JS-TW", "title": "AnimatedContent", "description": "Wrapper that animates any children on scroll or mount with configurable direction, distance, duration, easing and disappear options.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "AnimatedContent/AnimatedContent.jsx", "content": "import { useRef, useEffect } from 'react';\nimport { gsap } from 'gsap';\nimport { ScrollTrigger } from 'gsap/ScrollTrigger';\n\ngsap.registerPlugin(ScrollTrigger);\n\nconst AnimatedContent = ({\n children,\n container,\n distance = 100,\n direction = 'vertical',\n reverse = false,\n duration = 0.8,\n ease = 'power3.out',\n initialOpacity = 0,\n animateOpacity = true,\n scale = 1,\n threshold = 0.1,\n delay = 0,\n disappearAfter = 0,\n disappearDuration = 0.5,\n disappearEase = 'power3.in',\n onComplete,\n onDisappearanceComplete,\n className = '',\n ...props\n}) => {\n const ref = useRef(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n let scrollerTarget = container || document.getElementById('snap-main-container') || null;\n\n if (typeof scrollerTarget === 'string') {\n scrollerTarget = document.querySelector(scrollerTarget);\n }\n\n const axis = direction === 'horizontal' ? 'x' : 'y';\n const offset = reverse ? -distance : distance;\n const startPct = (1 - threshold) * 100;\n\n gsap.set(el, {\n [axis]: offset,\n scale,\n opacity: animateOpacity ? initialOpacity : 1,\n visibility: 'visible'\n });\n\n const tl = gsap.timeline({\n paused: true,\n delay,\n onComplete: () => {\n if (onComplete) onComplete();\n if (disappearAfter > 0) {\n gsap.to(el, {\n [axis]: reverse ? distance : -distance,\n scale: 0.8,\n opacity: animateOpacity ? initialOpacity : 0,\n delay: disappearAfter,\n duration: disappearDuration,\n ease: disappearEase,\n onComplete: () => onDisappearanceComplete?.()\n });\n }\n }\n });\n\n tl.to(el, {\n [axis]: 0,\n scale: 1,\n opacity: 1,\n duration,\n ease\n });\n\n const st = ScrollTrigger.create({\n trigger: el,\n scroller: scrollerTarget,\n start: `top ${startPct}%`,\n once: true,\n onEnter: () => tl.play()\n });\n\n return () => {\n st.kill();\n tl.kill();\n };\n }, [\n container,\n distance,\n direction,\n reverse,\n duration,\n ease,\n initialOpacity,\n animateOpacity,\n scale,\n threshold,\n delay,\n disappearAfter,\n disappearDuration,\n disappearEase,\n onComplete,\n onDisappearanceComplete\n ]);\n\n return (\n
\n {children}\n
\n );\n};\n\nexport default AnimatedContent;\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/AnimatedContent-TS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "AnimatedContent-TS-CSS", "title": "AnimatedContent", "description": "Wrapper that animates any children on scroll or mount with configurable direction, distance, duration, easing and disappear options.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "AnimatedContent/AnimatedContent.tsx", "content": "import React, { useRef, useEffect } from 'react';\nimport { gsap } from 'gsap';\nimport { ScrollTrigger } from 'gsap/ScrollTrigger';\n\ngsap.registerPlugin(ScrollTrigger);\n\ninterface AnimatedContentProps extends React.HTMLAttributes {\n children: React.ReactNode;\n container?: Element | string | null;\n distance?: number;\n direction?: 'vertical' | 'horizontal';\n reverse?: boolean;\n duration?: number;\n ease?: string;\n initialOpacity?: number;\n animateOpacity?: boolean;\n scale?: number;\n threshold?: number;\n delay?: number;\n disappearAfter?: number;\n disappearDuration?: number;\n disappearEase?: string;\n onComplete?: () => void;\n onDisappearanceComplete?: () => void;\n}\n\nconst AnimatedContent: React.FC = ({\n children,\n container,\n distance = 100,\n direction = 'vertical',\n reverse = false,\n duration = 0.8,\n ease = 'power3.out',\n initialOpacity = 0,\n animateOpacity = true,\n scale = 1,\n threshold = 0.1,\n delay = 0,\n disappearAfter = 0,\n disappearDuration = 0.5,\n disappearEase = 'power3.in',\n onComplete,\n onDisappearanceComplete,\n className = '',\n style,\n ...props\n}) => {\n const ref = useRef(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n let scrollerTarget: Element | string | null = container || document.getElementById('snap-main-container') || null;\n\n if (typeof scrollerTarget === 'string') {\n scrollerTarget = document.querySelector(scrollerTarget);\n }\n\n const axis = direction === 'horizontal' ? 'x' : 'y';\n const offset = reverse ? -distance : distance;\n const startPct = (1 - threshold) * 100;\n\n gsap.set(el, {\n [axis]: offset,\n scale,\n opacity: animateOpacity ? initialOpacity : 1,\n visibility: 'visible'\n });\n\n const tl = gsap.timeline({\n paused: true,\n delay,\n onComplete: () => {\n if (onComplete) onComplete();\n\n if (disappearAfter > 0) {\n gsap.to(el, {\n [axis]: reverse ? distance : -distance,\n scale: 0.8,\n opacity: animateOpacity ? initialOpacity : 0,\n delay: disappearAfter,\n duration: disappearDuration,\n ease: disappearEase,\n onComplete: () => onDisappearanceComplete?.()\n });\n }\n }\n });\n\n tl.to(el, {\n [axis]: 0,\n scale: 1,\n opacity: 1,\n duration,\n ease\n });\n\n const st = ScrollTrigger.create({\n trigger: el,\n scroller: scrollerTarget || window,\n start: `top ${startPct}%`,\n once: true,\n onEnter: () => tl.play()\n });\n\n return () => {\n st.kill();\n tl.kill();\n };\n }, [\n container,\n distance,\n direction,\n reverse,\n duration,\n ease,\n initialOpacity,\n animateOpacity,\n scale,\n threshold,\n delay,\n disappearAfter,\n disappearDuration,\n disappearEase,\n onComplete,\n onDisappearanceComplete\n ]);\n\n return (\n
\n {children}\n
\n );\n};\n\nexport default AnimatedContent;\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/AnimatedContent-TS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "AnimatedContent-TS-TW", "title": "AnimatedContent", "description": "Wrapper that animates any children on scroll or mount with configurable direction, distance, duration, easing and disappear options.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "AnimatedContent/AnimatedContent.tsx", "content": "import React, { useRef, useEffect } from 'react';\nimport { gsap } from 'gsap';\nimport { ScrollTrigger } from 'gsap/ScrollTrigger';\n\ngsap.registerPlugin(ScrollTrigger);\n\ninterface AnimatedContentProps extends React.HTMLAttributes {\n children: React.ReactNode;\n container?: Element | string | null;\n distance?: number;\n direction?: 'vertical' | 'horizontal';\n reverse?: boolean;\n duration?: number;\n ease?: string;\n initialOpacity?: number;\n animateOpacity?: boolean;\n scale?: number;\n threshold?: number;\n delay?: number;\n disappearAfter?: number;\n disappearDuration?: number;\n disappearEase?: string;\n onComplete?: () => void;\n onDisappearanceComplete?: () => void;\n}\n\nconst AnimatedContent: React.FC = ({\n children,\n container,\n distance = 100,\n direction = 'vertical',\n reverse = false,\n duration = 0.8,\n ease = 'power3.out',\n initialOpacity = 0,\n animateOpacity = true,\n scale = 1,\n threshold = 0.1,\n delay = 0,\n disappearAfter = 0,\n disappearDuration = 0.5,\n disappearEase = 'power3.in',\n onComplete,\n onDisappearanceComplete,\n className = '',\n ...props\n}) => {\n const ref = useRef(null);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n\n let scrollerTarget: Element | string | null = container || document.getElementById('snap-main-container') || null;\n\n if (typeof scrollerTarget === 'string') {\n scrollerTarget = document.querySelector(scrollerTarget);\n }\n\n const axis = direction === 'horizontal' ? 'x' : 'y';\n const offset = reverse ? -distance : distance;\n const startPct = (1 - threshold) * 100;\n\n gsap.set(el, {\n [axis]: offset,\n scale,\n opacity: animateOpacity ? initialOpacity : 1,\n visibility: 'visible'\n });\n\n const tl = gsap.timeline({\n paused: true,\n delay,\n onComplete: () => {\n if (onComplete) onComplete();\n if (disappearAfter > 0) {\n gsap.to(el, {\n [axis]: reverse ? distance : -distance,\n scale: 0.8,\n opacity: animateOpacity ? initialOpacity : 0,\n delay: disappearAfter,\n duration: disappearDuration,\n ease: disappearEase,\n onComplete: () => onDisappearanceComplete?.()\n });\n }\n }\n });\n\n tl.to(el, {\n [axis]: 0,\n scale: 1,\n opacity: 1,\n duration,\n ease\n });\n\n const st = ScrollTrigger.create({\n trigger: el,\n scroller: scrollerTarget || window,\n start: `top ${startPct}%`,\n once: true,\n onEnter: () => tl.play()\n });\n\n return () => {\n st.kill();\n tl.kill();\n };\n }, [\n container,\n distance,\n direction,\n reverse,\n duration,\n ease,\n initialOpacity,\n animateOpacity,\n scale,\n threshold,\n delay,\n disappearAfter,\n disappearDuration,\n disappearEase,\n onComplete,\n onDisappearanceComplete\n ]);\n\n return (\n
\n {children}\n
\n );\n};\n\nexport default AnimatedContent;\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/AnimatedList-JS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "AnimatedList-JS-CSS", "title": "AnimatedList", "description": "List items enter with staggered motion variants for polished reveals.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "AnimatedList/AnimatedList.css", "content": ".scroll-list-container {\n position: relative;\n width: 500px;\n}\n\n.scroll-list {\n max-height: 400px;\n overflow-y: auto;\n padding: 16px;\n}\n\n.scroll-list::-webkit-scrollbar {\n width: 8px;\n}\n\n.scroll-list::-webkit-scrollbar-track {\n background: #060010;\n}\n\n.scroll-list::-webkit-scrollbar-thumb {\n background: #271e37;\n border-radius: 4px;\n}\n\n.no-scrollbar::-webkit-scrollbar {\n display: none;\n}\n\n.no-scrollbar {\n -ms-overflow-style: none;\n scrollbar-width: none;\n}\n\n.item {\n padding: 16px;\n background-color: #170d27;\n border-radius: 8px;\n margin-bottom: 1rem;\n}\n\n.item.selected {\n background-color: #271e37;\n}\n\n.item-text {\n color: white;\n margin: 0;\n}\n\n.top-gradient {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 50px;\n background: linear-gradient(to bottom, #060010, transparent);\n pointer-events: none;\n transition: opacity 0.3s ease;\n}\n\n.bottom-gradient {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 100px;\n background: linear-gradient(to top, #060010, transparent);\n pointer-events: none;\n transition: opacity 0.3s ease;\n}\n" }, { "type": "registry:component", "path": "AnimatedList/AnimatedList.jsx", "content": "import { useRef, useState, useEffect, useCallback } from 'react';\nimport { motion, useInView } from 'motion/react';\nimport './AnimatedList.css';\n\nconst AnimatedItem = ({ children, delay = 0, index, onMouseEnter, onClick }) => {\n const ref = useRef(null);\n const inView = useInView(ref, { amount: 0.5, triggerOnce: false });\n return (\n \n {children}\n \n );\n};\n\nconst AnimatedList = ({\n items = [\n 'Item 1',\n 'Item 2',\n 'Item 3',\n 'Item 4',\n 'Item 5',\n 'Item 6',\n 'Item 7',\n 'Item 8',\n 'Item 9',\n 'Item 10',\n 'Item 11',\n 'Item 12',\n 'Item 13',\n 'Item 14',\n 'Item 15'\n ],\n onItemSelect,\n showGradients = true,\n enableArrowNavigation = true,\n className = '',\n itemClassName = '',\n displayScrollbar = true,\n initialSelectedIndex = -1\n}) => {\n const listRef = useRef(null);\n const [selectedIndex, setSelectedIndex] = useState(initialSelectedIndex);\n const [keyboardNav, setKeyboardNav] = useState(false);\n const [topGradientOpacity, setTopGradientOpacity] = useState(0);\n const [bottomGradientOpacity, setBottomGradientOpacity] = useState(1);\n\n const handleItemMouseEnter = useCallback(index => {\n setSelectedIndex(index);\n }, []);\n\n const handleItemClick = useCallback(\n (item, index) => {\n setSelectedIndex(index);\n if (onItemSelect) {\n onItemSelect(item, index);\n }\n },\n [onItemSelect]\n );\n\n const handleScroll = useCallback(e => {\n const { scrollTop, scrollHeight, clientHeight } = e.target;\n setTopGradientOpacity(Math.min(scrollTop / 50, 1));\n const bottomDistance = scrollHeight - (scrollTop + clientHeight);\n setBottomGradientOpacity(scrollHeight <= clientHeight ? 0 : Math.min(bottomDistance / 50, 1));\n }, []);\n\n useEffect(() => {\n if (!enableArrowNavigation) return;\n const handleKeyDown = e => {\n if (e.key === 'ArrowDown' || (e.key === 'Tab' && !e.shiftKey)) {\n e.preventDefault();\n setKeyboardNav(true);\n setSelectedIndex(prev => Math.min(prev + 1, items.length - 1));\n } else if (e.key === 'ArrowUp' || (e.key === 'Tab' && e.shiftKey)) {\n e.preventDefault();\n setKeyboardNav(true);\n setSelectedIndex(prev => Math.max(prev - 1, 0));\n } else if (e.key === 'Enter') {\n if (selectedIndex >= 0 && selectedIndex < items.length) {\n e.preventDefault();\n if (onItemSelect) {\n onItemSelect(items[selectedIndex], selectedIndex);\n }\n }\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [items, selectedIndex, onItemSelect, enableArrowNavigation]);\n\n useEffect(() => {\n if (!keyboardNav || selectedIndex < 0 || !listRef.current) return;\n const container = listRef.current;\n const selectedItem = container.querySelector(`[data-index=\"${selectedIndex}\"]`);\n if (selectedItem) {\n const extraMargin = 50;\n const containerScrollTop = container.scrollTop;\n const containerHeight = container.clientHeight;\n const itemTop = selectedItem.offsetTop;\n const itemBottom = itemTop + selectedItem.offsetHeight;\n if (itemTop < containerScrollTop + extraMargin) {\n container.scrollTo({ top: itemTop - extraMargin, behavior: 'smooth' });\n } else if (itemBottom > containerScrollTop + containerHeight - extraMargin) {\n container.scrollTo({\n top: itemBottom - containerHeight + extraMargin,\n behavior: 'smooth'\n });\n }\n }\n setKeyboardNav(false);\n }, [selectedIndex, keyboardNav]);\n\n return (\n
\n
\n {items.map((item, index) => (\n handleItemMouseEnter(index)}\n onClick={() => handleItemClick(item, index)}\n >\n
\n

{item}

\n
\n \n ))}\n
\n {showGradients && (\n <>\n
\n
\n \n )}\n
\n );\n};\n\nexport default AnimatedList;\n" } ], "registryDependencies": [], "dependencies": [ "motion@^12.23.12" ] } ================================================ FILE: public/r/AnimatedList-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "AnimatedList-JS-TW", "title": "AnimatedList", "description": "List items enter with staggered motion variants for polished reveals.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "AnimatedList/AnimatedList.jsx", "content": "import { useCallback, useEffect, useRef, useState } from 'react';\nimport { motion, useInView } from 'motion/react';\n\nconst AnimatedItem = ({ children, delay = 0, index, onMouseEnter, onClick }) => {\n const ref = useRef(null);\n const inView = useInView(ref, { amount: 0.5, triggerOnce: false });\n return (\n \n {children}\n \n );\n};\n\nconst AnimatedList = ({\n items = [\n 'Item 1',\n 'Item 2',\n 'Item 3',\n 'Item 4',\n 'Item 5',\n 'Item 6',\n 'Item 7',\n 'Item 8',\n 'Item 9',\n 'Item 10',\n 'Item 11',\n 'Item 12',\n 'Item 13',\n 'Item 14',\n 'Item 15'\n ],\n onItemSelect,\n showGradients = true,\n enableArrowNavigation = true,\n className = '',\n itemClassName = '',\n displayScrollbar = true,\n initialSelectedIndex = -1\n}) => {\n const listRef = useRef(null);\n const [selectedIndex, setSelectedIndex] = useState(initialSelectedIndex);\n const [keyboardNav, setKeyboardNav] = useState(false);\n const [topGradientOpacity, setTopGradientOpacity] = useState(0);\n const [bottomGradientOpacity, setBottomGradientOpacity] = useState(1);\n\n const handleItemMouseEnter = useCallback(index => {\n setSelectedIndex(index);\n }, []);\n\n const handleItemClick = useCallback(\n (item, index) => {\n setSelectedIndex(index);\n if (onItemSelect) {\n onItemSelect(item, index);\n }\n },\n [onItemSelect]\n );\n\n const handleScroll = useCallback(e => {\n const { scrollTop, scrollHeight, clientHeight } = e.target;\n setTopGradientOpacity(Math.min(scrollTop / 50, 1));\n const bottomDistance = scrollHeight - (scrollTop + clientHeight);\n setBottomGradientOpacity(scrollHeight <= clientHeight ? 0 : Math.min(bottomDistance / 50, 1));\n }, []);\n\n useEffect(() => {\n if (!enableArrowNavigation) return;\n const handleKeyDown = e => {\n if (e.key === 'ArrowDown' || (e.key === 'Tab' && !e.shiftKey)) {\n e.preventDefault();\n setKeyboardNav(true);\n setSelectedIndex(prev => Math.min(prev + 1, items.length - 1));\n } else if (e.key === 'ArrowUp' || (e.key === 'Tab' && e.shiftKey)) {\n e.preventDefault();\n setKeyboardNav(true);\n setSelectedIndex(prev => Math.max(prev - 1, 0));\n } else if (e.key === 'Enter') {\n if (selectedIndex >= 0 && selectedIndex < items.length) {\n e.preventDefault();\n if (onItemSelect) {\n onItemSelect(items[selectedIndex], selectedIndex);\n }\n }\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [items, selectedIndex, onItemSelect, enableArrowNavigation]);\n\n useEffect(() => {\n if (!keyboardNav || selectedIndex < 0 || !listRef.current) return;\n const container = listRef.current;\n const selectedItem = container.querySelector(`[data-index=\"${selectedIndex}\"]`);\n if (selectedItem) {\n const extraMargin = 50;\n const containerScrollTop = container.scrollTop;\n const containerHeight = container.clientHeight;\n const itemTop = selectedItem.offsetTop;\n const itemBottom = itemTop + selectedItem.offsetHeight;\n if (itemTop < containerScrollTop + extraMargin) {\n container.scrollTo({ top: itemTop - extraMargin, behavior: 'smooth' });\n } else if (itemBottom > containerScrollTop + containerHeight - extraMargin) {\n container.scrollTo({\n top: itemBottom - containerHeight + extraMargin,\n behavior: 'smooth'\n });\n }\n }\n setKeyboardNav(false);\n }, [selectedIndex, keyboardNav]);\n\n return (\n
\n \n {items.map((item, index) => (\n handleItemMouseEnter(index)}\n onClick={() => handleItemClick(item, index)}\n >\n
\n

{item}

\n
\n \n ))}\n
\n {showGradients && (\n <>\n \n \n \n )}\n \n );\n};\n\nexport default AnimatedList;\n" } ], "registryDependencies": [], "dependencies": [ "motion@^12.23.12" ] } ================================================ FILE: public/r/AnimatedList-TS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "AnimatedList-TS-CSS", "title": "AnimatedList", "description": "List items enter with staggered motion variants for polished reveals.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "AnimatedList/AnimatedList.css", "content": ".scroll-list-container {\n position: relative;\n width: 500px;\n}\n\n.scroll-list {\n max-height: 400px;\n overflow-y: auto;\n padding: 16px;\n}\n\n.scroll-list::-webkit-scrollbar {\n width: 8px;\n}\n\n.scroll-list::-webkit-scrollbar-track {\n background: #060606;\n}\n\n.scroll-list::-webkit-scrollbar-thumb {\n background: #222;\n border-radius: 4px;\n}\n\n.no-scrollbar::-webkit-scrollbar {\n display: none;\n}\n\n.no-scrollbar {\n -ms-overflow-style: none;\n scrollbar-width: none;\n}\n\n.item {\n padding: 16px;\n background-color: #111;\n border-radius: 8px;\n margin-bottom: 1rem;\n}\n\n.item.selected {\n background-color: #222;\n}\n\n.item-text {\n color: white;\n margin: 0;\n}\n\n.top-gradient {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 50px;\n background: linear-gradient(to bottom, #060010, transparent);\n pointer-events: none;\n transition: opacity 0.3s ease;\n}\n\n.bottom-gradient {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: 100px;\n background: linear-gradient(to top, #060010, transparent);\n pointer-events: none;\n transition: opacity 0.3s ease;\n}\n" }, { "type": "registry:component", "path": "AnimatedList/AnimatedList.tsx", "content": "import React, { useRef, useState, useEffect, useCallback, ReactNode, MouseEventHandler, UIEvent } from 'react';\nimport { motion, useInView } from 'motion/react';\nimport './AnimatedList.css';\n\ninterface AnimatedItemProps {\n children: ReactNode;\n delay?: number;\n index: number;\n onMouseEnter?: MouseEventHandler;\n onClick?: MouseEventHandler;\n}\n\nconst AnimatedItem: React.FC = ({ children, delay = 0, index, onMouseEnter, onClick }) => {\n const ref = useRef(null);\n const inView = useInView(ref, { amount: 0.5, once: false });\n return (\n \n {children}\n \n );\n};\n\ninterface AnimatedListProps {\n items?: string[];\n onItemSelect?: (item: string, index: number) => void;\n showGradients?: boolean;\n enableArrowNavigation?: boolean;\n className?: string;\n itemClassName?: string;\n displayScrollbar?: boolean;\n initialSelectedIndex?: number;\n}\n\nconst AnimatedList: React.FC = ({\n items = [\n 'Item 1',\n 'Item 2',\n 'Item 3',\n 'Item 4',\n 'Item 5',\n 'Item 6',\n 'Item 7',\n 'Item 8',\n 'Item 9',\n 'Item 10',\n 'Item 11',\n 'Item 12',\n 'Item 13',\n 'Item 14',\n 'Item 15'\n ],\n onItemSelect,\n showGradients = true,\n enableArrowNavigation = true,\n className = '',\n itemClassName = '',\n displayScrollbar = true,\n initialSelectedIndex = -1\n}) => {\n const listRef = useRef(null);\n const [selectedIndex, setSelectedIndex] = useState(initialSelectedIndex);\n const [keyboardNav, setKeyboardNav] = useState(false);\n const [topGradientOpacity, setTopGradientOpacity] = useState(0);\n const [bottomGradientOpacity, setBottomGradientOpacity] = useState(1);\n\n const handleItemMouseEnter = useCallback((index: number) => {\n setSelectedIndex(index);\n }, []);\n\n const handleItemClick = useCallback(\n (item: string, index: number) => {\n setSelectedIndex(index);\n if (onItemSelect) {\n onItemSelect(item, index);\n }\n },\n [onItemSelect]\n );\n\n const handleScroll = useCallback((e: UIEvent) => {\n const target = e.target as HTMLDivElement;\n const { scrollTop, scrollHeight, clientHeight } = target;\n setTopGradientOpacity(Math.min(scrollTop / 50, 1));\n const bottomDistance = scrollHeight - (scrollTop + clientHeight);\n setBottomGradientOpacity(scrollHeight <= clientHeight ? 0 : Math.min(bottomDistance / 50, 1));\n }, []);\n\n useEffect(() => {\n if (!enableArrowNavigation) return;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'ArrowDown' || (e.key === 'Tab' && !e.shiftKey)) {\n e.preventDefault();\n setKeyboardNav(true);\n setSelectedIndex(prev => Math.min(prev + 1, items.length - 1));\n } else if (e.key === 'ArrowUp' || (e.key === 'Tab' && e.shiftKey)) {\n e.preventDefault();\n setKeyboardNav(true);\n setSelectedIndex(prev => Math.max(prev - 1, 0));\n } else if (e.key === 'Enter') {\n if (selectedIndex >= 0 && selectedIndex < items.length) {\n e.preventDefault();\n if (onItemSelect) {\n onItemSelect(items[selectedIndex], selectedIndex);\n }\n }\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [items, selectedIndex, onItemSelect, enableArrowNavigation]);\n\n useEffect(() => {\n if (!keyboardNav || selectedIndex < 0 || !listRef.current) return;\n const container = listRef.current;\n const selectedItem = container.querySelector(`[data-index=\"${selectedIndex}\"]`) as HTMLElement | null;\n if (selectedItem) {\n const extraMargin = 50;\n const containerScrollTop = container.scrollTop;\n const containerHeight = container.clientHeight;\n const itemTop = selectedItem.offsetTop;\n const itemBottom = itemTop + selectedItem.offsetHeight;\n if (itemTop < containerScrollTop + extraMargin) {\n container.scrollTo({ top: itemTop - extraMargin, behavior: 'smooth' });\n } else if (itemBottom > containerScrollTop + containerHeight - extraMargin) {\n container.scrollTo({\n top: itemBottom - containerHeight + extraMargin,\n behavior: 'smooth'\n });\n }\n }\n setKeyboardNav(false);\n }, [selectedIndex, keyboardNav]);\n\n return (\n
\n
\n {items.map((item, index) => (\n handleItemMouseEnter(index)}\n onClick={() => handleItemClick(item, index)}\n >\n
\n

{item}

\n
\n \n ))}\n
\n {showGradients && (\n <>\n
\n
\n \n )}\n
\n );\n};\n\nexport default AnimatedList;\n" } ], "registryDependencies": [], "dependencies": [ "motion@^12.23.12" ] } ================================================ FILE: public/r/AnimatedList-TS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "AnimatedList-TS-TW", "title": "AnimatedList", "description": "List items enter with staggered motion variants for polished reveals.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "AnimatedList/AnimatedList.tsx", "content": "import React, { useRef, useState, useEffect, useCallback, ReactNode, MouseEventHandler, UIEvent } from 'react';\nimport { motion, useInView } from 'motion/react';\n\ninterface AnimatedItemProps {\n children: ReactNode;\n delay?: number;\n index: number;\n onMouseEnter?: MouseEventHandler;\n onClick?: MouseEventHandler;\n}\n\nconst AnimatedItem: React.FC = ({ children, delay = 0, index, onMouseEnter, onClick }) => {\n const ref = useRef(null);\n const inView = useInView(ref, { amount: 0.5, once: false });\n return (\n \n {children}\n \n );\n};\n\ninterface AnimatedListProps {\n items?: string[];\n onItemSelect?: (item: string, index: number) => void;\n showGradients?: boolean;\n enableArrowNavigation?: boolean;\n className?: string;\n itemClassName?: string;\n displayScrollbar?: boolean;\n initialSelectedIndex?: number;\n}\n\nconst AnimatedList: React.FC = ({\n items = [\n 'Item 1',\n 'Item 2',\n 'Item 3',\n 'Item 4',\n 'Item 5',\n 'Item 6',\n 'Item 7',\n 'Item 8',\n 'Item 9',\n 'Item 10',\n 'Item 11',\n 'Item 12',\n 'Item 13',\n 'Item 14',\n 'Item 15'\n ],\n onItemSelect,\n showGradients = true,\n enableArrowNavigation = true,\n className = '',\n itemClassName = '',\n displayScrollbar = true,\n initialSelectedIndex = -1\n}) => {\n const listRef = useRef(null);\n const [selectedIndex, setSelectedIndex] = useState(initialSelectedIndex);\n const [keyboardNav, setKeyboardNav] = useState(false);\n const [topGradientOpacity, setTopGradientOpacity] = useState(0);\n const [bottomGradientOpacity, setBottomGradientOpacity] = useState(1);\n\n const handleItemMouseEnter = useCallback((index: number) => {\n setSelectedIndex(index);\n }, []);\n\n const handleItemClick = useCallback(\n (item: string, index: number) => {\n setSelectedIndex(index);\n if (onItemSelect) {\n onItemSelect(item, index);\n }\n },\n [onItemSelect]\n );\n\n const handleScroll = (e: UIEvent) => {\n const { scrollTop, scrollHeight, clientHeight } = e.target as HTMLDivElement;\n setTopGradientOpacity(Math.min(scrollTop / 50, 1));\n const bottomDistance = scrollHeight - (scrollTop + clientHeight);\n setBottomGradientOpacity(scrollHeight <= clientHeight ? 0 : Math.min(bottomDistance / 50, 1));\n };\n\n useEffect(() => {\n if (!enableArrowNavigation) return;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'ArrowDown' || (e.key === 'Tab' && !e.shiftKey)) {\n e.preventDefault();\n setKeyboardNav(true);\n setSelectedIndex(prev => Math.min(prev + 1, items.length - 1));\n } else if (e.key === 'ArrowUp' || (e.key === 'Tab' && e.shiftKey)) {\n e.preventDefault();\n setKeyboardNav(true);\n setSelectedIndex(prev => Math.max(prev - 1, 0));\n } else if (e.key === 'Enter') {\n if (selectedIndex >= 0 && selectedIndex < items.length) {\n e.preventDefault();\n if (onItemSelect) {\n onItemSelect(items[selectedIndex], selectedIndex);\n }\n }\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [items, selectedIndex, onItemSelect, enableArrowNavigation]);\n\n useEffect(() => {\n if (!keyboardNav || selectedIndex < 0 || !listRef.current) return;\n const container = listRef.current;\n const selectedItem = container.querySelector(`[data-index=\"${selectedIndex}\"]`) as HTMLElement | null;\n if (selectedItem) {\n const extraMargin = 50;\n const containerScrollTop = container.scrollTop;\n const containerHeight = container.clientHeight;\n const itemTop = selectedItem.offsetTop;\n const itemBottom = itemTop + selectedItem.offsetHeight;\n if (itemTop < containerScrollTop + extraMargin) {\n container.scrollTo({ top: itemTop - extraMargin, behavior: 'smooth' });\n } else if (itemBottom > containerScrollTop + containerHeight - extraMargin) {\n container.scrollTo({\n top: itemBottom - containerHeight + extraMargin,\n behavior: 'smooth'\n });\n }\n }\n setKeyboardNav(false);\n }, [selectedIndex, keyboardNav]);\n\n return (\n
\n \n {items.map((item, index) => (\n handleItemMouseEnter(index)}\n onClick={() => handleItemClick(item, index)}\n >\n
\n

{item}

\n
\n \n ))}\n
\n {showGradients && (\n <>\n \n \n \n )}\n \n );\n};\n\nexport default AnimatedList;\n" } ], "registryDependencies": [], "dependencies": [ "motion@^12.23.12" ] } ================================================ FILE: public/r/Antigravity-JS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Antigravity-JS-CSS", "title": "Antigravity", "description": "3D antigravity particle field that repels from the cursor with smooth motion.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Antigravity/Antigravity.jsx", "content": "/* eslint-disable react/no-unknown-property */\nimport { Canvas, useFrame, useThree } from '@react-three/fiber';\nimport { useMemo, useRef } from 'react';\nimport * as THREE from 'three';\n\nconst AntigravityInner = ({\n count = 300,\n magnetRadius = 10,\n ringRadius = 10,\n waveSpeed = 0.4,\n waveAmplitude = 1,\n particleSize = 2,\n lerpSpeed = 0.1,\n color = '#FF9FFC',\n autoAnimate = false,\n particleVariance = 1,\n rotationSpeed = 0,\n depthFactor = 1,\n pulseSpeed = 3,\n particleShape = 'capsule',\n fieldStrength = 10\n}) => {\n const meshRef = useRef(null);\n const { viewport } = useThree();\n const dummy = useMemo(() => new THREE.Object3D(), []);\n\n const lastMousePos = useRef({ x: 0, y: 0 });\n const lastMouseMoveTime = useRef(0);\n const virtualMouse = useRef({ x: 0, y: 0 });\n\n const particles = useMemo(() => {\n const temp = [];\n const width = viewport.width || 100;\n const height = viewport.height || 100;\n\n for (let i = 0; i < count; i++) {\n const t = Math.random() * 100;\n const factor = 20 + Math.random() * 100;\n const speed = 0.01 + Math.random() / 200;\n const xFactor = -50 + Math.random() * 100;\n const yFactor = -50 + Math.random() * 100;\n const zFactor = -50 + Math.random() * 100;\n\n const x = (Math.random() - 0.5) * width;\n const y = (Math.random() - 0.5) * height;\n const z = (Math.random() - 0.5) * 20;\n\n const randomRadiusOffset = (Math.random() - 0.5) * 2;\n\n temp.push({\n t,\n factor,\n speed,\n xFactor,\n yFactor,\n zFactor,\n mx: x,\n my: y,\n mz: z,\n cx: x,\n cy: y,\n cz: z,\n vx: 0,\n vy: 0,\n vz: 0,\n randomRadiusOffset\n });\n }\n return temp;\n }, [count, viewport.width, viewport.height]);\n\n useFrame(state => {\n const mesh = meshRef.current;\n if (!mesh) return;\n\n const { viewport: v, pointer: m } = state;\n\n const mouseDist = Math.sqrt(Math.pow(m.x - lastMousePos.current.x, 2) + Math.pow(m.y - lastMousePos.current.y, 2));\n\n if (mouseDist > 0.001) {\n lastMouseMoveTime.current = Date.now();\n lastMousePos.current = { x: m.x, y: m.y };\n }\n\n let destX = (m.x * v.width) / 2;\n let destY = (m.y * v.height) / 2;\n\n if (autoAnimate && Date.now() - lastMouseMoveTime.current > 2000) {\n const time = state.clock.getElapsedTime();\n destX = Math.sin(time * 0.5) * (v.width / 4);\n destY = Math.cos(time * 0.5 * 2) * (v.height / 4);\n }\n\n const smoothFactor = 0.05;\n virtualMouse.current.x += (destX - virtualMouse.current.x) * smoothFactor;\n virtualMouse.current.y += (destY - virtualMouse.current.y) * smoothFactor;\n\n const targetX = virtualMouse.current.x;\n const targetY = virtualMouse.current.y;\n\n const globalRotation = state.clock.getElapsedTime() * rotationSpeed;\n\n particles.forEach((particle, i) => {\n let { t, speed, mx, my, mz, cz, randomRadiusOffset } = particle;\n\n t = particle.t += speed / 2;\n\n const projectionFactor = 1 - cz / 50;\n const projectedTargetX = targetX * projectionFactor;\n const projectedTargetY = targetY * projectionFactor;\n\n const dx = mx - projectedTargetX;\n const dy = my - projectedTargetY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n let targetPos = { x: mx, y: my, z: mz * depthFactor };\n\n if (dist < magnetRadius) {\n const angle = Math.atan2(dy, dx) + globalRotation;\n\n const wave = Math.sin(t * waveSpeed + angle) * (0.5 * waveAmplitude);\n const deviation = randomRadiusOffset * (5 / (fieldStrength + 0.1));\n\n const currentRingRadius = ringRadius + wave + deviation;\n\n targetPos.x = projectedTargetX + currentRingRadius * Math.cos(angle);\n targetPos.y = projectedTargetY + currentRingRadius * Math.sin(angle);\n targetPos.z = mz * depthFactor + Math.sin(t) * (1 * waveAmplitude * depthFactor);\n }\n\n particle.cx += (targetPos.x - particle.cx) * lerpSpeed;\n particle.cy += (targetPos.y - particle.cy) * lerpSpeed;\n particle.cz += (targetPos.z - particle.cz) * lerpSpeed;\n\n dummy.position.set(particle.cx, particle.cy, particle.cz);\n\n dummy.lookAt(projectedTargetX, projectedTargetY, particle.cz);\n dummy.rotateX(Math.PI / 2);\n\n const currentDistToMouse = Math.sqrt(\n Math.pow(particle.cx - projectedTargetX, 2) + Math.pow(particle.cy - projectedTargetY, 2)\n );\n\n const distFromRing = Math.abs(currentDistToMouse - ringRadius);\n let scaleFactor = 1 - distFromRing / 10;\n\n scaleFactor = Math.max(0, Math.min(1, scaleFactor));\n\n const finalScale = scaleFactor * (0.8 + Math.sin(t * pulseSpeed) * 0.2 * particleVariance) * particleSize;\n dummy.scale.set(finalScale, finalScale, finalScale);\n\n dummy.updateMatrix();\n\n mesh.setMatrixAt(i, dummy.matrix);\n });\n\n mesh.instanceMatrix.needsUpdate = true;\n });\n\n return (\n \n {particleShape === 'capsule' && }\n {particleShape === 'sphere' && }\n {particleShape === 'box' && }\n {particleShape === 'tetrahedron' && }\n \n \n );\n};\n\nconst Antigravity = props => {\n return (\n \n \n \n );\n};\n\nexport default Antigravity;\n" } ], "registryDependencies": [], "dependencies": [ "@react-three/fiber@^9.3.0", "three@^0.167.1" ] } ================================================ FILE: public/r/Antigravity-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Antigravity-JS-TW", "title": "Antigravity", "description": "3D antigravity particle field that repels from the cursor with smooth motion.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Antigravity/Antigravity.jsx", "content": "/* eslint-disable react/no-unknown-property */\nimport { Canvas, useFrame, useThree } from '@react-three/fiber';\nimport { useMemo, useRef } from 'react';\nimport * as THREE from 'three';\n\nconst AntigravityInner = ({\n count = 300,\n magnetRadius = 10,\n ringRadius = 10,\n waveSpeed = 0.4,\n waveAmplitude = 1,\n particleSize = 2,\n lerpSpeed = 0.1,\n color = '#FF9FFC',\n autoAnimate = false,\n particleVariance = 1,\n rotationSpeed = 0,\n depthFactor = 1,\n pulseSpeed = 3,\n particleShape = 'capsule',\n fieldStrength = 10\n}) => {\n const meshRef = useRef(null);\n const { viewport } = useThree();\n const dummy = useMemo(() => new THREE.Object3D(), []);\n\n const lastMousePos = useRef({ x: 0, y: 0 });\n const lastMouseMoveTime = useRef(0);\n const virtualMouse = useRef({ x: 0, y: 0 });\n\n const particles = useMemo(() => {\n const temp = [];\n const width = viewport.width || 100;\n const height = viewport.height || 100;\n\n for (let i = 0; i < count; i++) {\n const t = Math.random() * 100;\n const factor = 20 + Math.random() * 100;\n const speed = 0.01 + Math.random() / 200;\n const xFactor = -50 + Math.random() * 100;\n const yFactor = -50 + Math.random() * 100;\n const zFactor = -50 + Math.random() * 100;\n\n const x = (Math.random() - 0.5) * width;\n const y = (Math.random() - 0.5) * height;\n const z = (Math.random() - 0.5) * 20;\n\n const randomRadiusOffset = (Math.random() - 0.5) * 2;\n\n temp.push({\n t,\n factor,\n speed,\n xFactor,\n yFactor,\n zFactor,\n mx: x,\n my: y,\n mz: z,\n cx: x,\n cy: y,\n cz: z,\n vx: 0,\n vy: 0,\n vz: 0,\n randomRadiusOffset\n });\n }\n return temp;\n }, [count, viewport.width, viewport.height]);\n\n useFrame(state => {\n const mesh = meshRef.current;\n if (!mesh) return;\n\n const { viewport: v, pointer: m } = state;\n\n const mouseDist = Math.sqrt(Math.pow(m.x - lastMousePos.current.x, 2) + Math.pow(m.y - lastMousePos.current.y, 2));\n\n if (mouseDist > 0.001) {\n lastMouseMoveTime.current = Date.now();\n lastMousePos.current = { x: m.x, y: m.y };\n }\n\n let destX = (m.x * v.width) / 2;\n let destY = (m.y * v.height) / 2;\n\n if (autoAnimate && Date.now() - lastMouseMoveTime.current > 2000) {\n const time = state.clock.getElapsedTime();\n destX = Math.sin(time * 0.5) * (v.width / 4);\n destY = Math.cos(time * 0.5 * 2) * (v.height / 4);\n }\n\n const smoothFactor = 0.05;\n virtualMouse.current.x += (destX - virtualMouse.current.x) * smoothFactor;\n virtualMouse.current.y += (destY - virtualMouse.current.y) * smoothFactor;\n\n const targetX = virtualMouse.current.x;\n const targetY = virtualMouse.current.y;\n\n const globalRotation = state.clock.getElapsedTime() * rotationSpeed;\n\n particles.forEach((particle, i) => {\n let { t, speed, mx, my, mz, cz, randomRadiusOffset } = particle;\n\n t = particle.t += speed / 2;\n\n const projectionFactor = 1 - cz / 50;\n const projectedTargetX = targetX * projectionFactor;\n const projectedTargetY = targetY * projectionFactor;\n\n const dx = mx - projectedTargetX;\n const dy = my - projectedTargetY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n let targetPos = { x: mx, y: my, z: mz * depthFactor };\n\n if (dist < magnetRadius) {\n const angle = Math.atan2(dy, dx) + globalRotation;\n\n const wave = Math.sin(t * waveSpeed + angle) * (0.5 * waveAmplitude);\n const deviation = randomRadiusOffset * (5 / (fieldStrength + 0.1));\n\n const currentRingRadius = ringRadius + wave + deviation;\n\n targetPos.x = projectedTargetX + currentRingRadius * Math.cos(angle);\n targetPos.y = projectedTargetY + currentRingRadius * Math.sin(angle);\n targetPos.z = mz * depthFactor + Math.sin(t) * (1 * waveAmplitude * depthFactor);\n }\n\n particle.cx += (targetPos.x - particle.cx) * lerpSpeed;\n particle.cy += (targetPos.y - particle.cy) * lerpSpeed;\n particle.cz += (targetPos.z - particle.cz) * lerpSpeed;\n\n dummy.position.set(particle.cx, particle.cy, particle.cz);\n\n dummy.lookAt(projectedTargetX, projectedTargetY, particle.cz);\n dummy.rotateX(Math.PI / 2);\n\n const currentDistToMouse = Math.sqrt(\n Math.pow(particle.cx - projectedTargetX, 2) + Math.pow(particle.cy - projectedTargetY, 2)\n );\n\n const distFromRing = Math.abs(currentDistToMouse - ringRadius);\n let scaleFactor = 1 - distFromRing / 10;\n\n scaleFactor = Math.max(0, Math.min(1, scaleFactor));\n\n const finalScale = scaleFactor * (0.8 + Math.sin(t * pulseSpeed) * 0.2 * particleVariance) * particleSize;\n dummy.scale.set(finalScale, finalScale, finalScale);\n\n dummy.updateMatrix();\n\n mesh.setMatrixAt(i, dummy.matrix);\n });\n\n mesh.instanceMatrix.needsUpdate = true;\n });\n\n return (\n \n {particleShape === 'capsule' && }\n {particleShape === 'sphere' && }\n {particleShape === 'box' && }\n {particleShape === 'tetrahedron' && }\n \n \n );\n};\n\nconst Antigravity = props => {\n return (\n \n \n \n );\n};\n\nexport default Antigravity;\n" } ], "registryDependencies": [], "dependencies": [ "@react-three/fiber@^9.3.0", "three@^0.167.1" ] } ================================================ FILE: public/r/Antigravity-TS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Antigravity-TS-CSS", "title": "Antigravity", "description": "3D antigravity particle field that repels from the cursor with smooth motion.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Antigravity/Antigravity.tsx", "content": "import { Canvas, useFrame, useThree } from '@react-three/fiber';\nimport React, { useMemo, useRef } from 'react';\nimport * as THREE from 'three';\n\ninterface AntigravityProps {\n count?: number;\n magnetRadius?: number;\n ringRadius?: number;\n waveSpeed?: number;\n waveAmplitude?: number;\n particleSize?: number;\n lerpSpeed?: number;\n color?: string;\n autoAnimate?: boolean;\n particleVariance?: number;\n rotationSpeed?: number;\n depthFactor?: number;\n pulseSpeed?: number;\n particleShape?: 'capsule' | 'sphere' | 'box' | 'tetrahedron';\n fieldStrength?: number;\n}\n\nconst AntigravityInner: React.FC = ({\n count = 300,\n magnetRadius = 10,\n ringRadius = 10,\n waveSpeed = 0.4,\n waveAmplitude = 1,\n particleSize = 2,\n lerpSpeed = 0.1,\n color = '#FF9FFC',\n autoAnimate = false,\n particleVariance = 1,\n rotationSpeed = 0,\n depthFactor = 1,\n pulseSpeed = 3,\n particleShape = 'capsule',\n fieldStrength = 10\n}) => {\n const meshRef = useRef(null);\n const { viewport } = useThree();\n const dummy = useMemo(() => new THREE.Object3D(), []);\n\n const lastMousePos = useRef({ x: 0, y: 0 });\n const lastMouseMoveTime = useRef(0);\n const virtualMouse = useRef({ x: 0, y: 0 });\n\n const particles = useMemo(() => {\n const temp = [];\n const width = viewport.width || 100;\n const height = viewport.height || 100;\n\n for (let i = 0; i < count; i++) {\n const t = Math.random() * 100;\n const factor = 20 + Math.random() * 100;\n const speed = 0.01 + Math.random() / 200;\n const xFactor = -50 + Math.random() * 100;\n const yFactor = -50 + Math.random() * 100;\n const zFactor = -50 + Math.random() * 100;\n\n const x = (Math.random() - 0.5) * width;\n const y = (Math.random() - 0.5) * height;\n const z = (Math.random() - 0.5) * 20;\n\n const randomRadiusOffset = (Math.random() - 0.5) * 2;\n\n temp.push({\n t,\n factor,\n speed,\n xFactor,\n yFactor,\n zFactor,\n mx: x,\n my: y,\n mz: z,\n cx: x,\n cy: y,\n cz: z,\n vx: 0,\n vy: 0,\n vz: 0,\n randomRadiusOffset\n });\n }\n return temp;\n }, [count, viewport.width, viewport.height]);\n\n useFrame(state => {\n const mesh = meshRef.current;\n if (!mesh) return;\n\n const { viewport: v, pointer: m } = state;\n\n const mouseDist = Math.sqrt(Math.pow(m.x - lastMousePos.current.x, 2) + Math.pow(m.y - lastMousePos.current.y, 2));\n\n if (mouseDist > 0.001) {\n lastMouseMoveTime.current = Date.now();\n lastMousePos.current = { x: m.x, y: m.y };\n }\n\n let destX = (m.x * v.width) / 2;\n let destY = (m.y * v.height) / 2;\n\n if (autoAnimate && Date.now() - lastMouseMoveTime.current > 2000) {\n const time = state.clock.getElapsedTime();\n destX = Math.sin(time * 0.5) * (v.width / 4);\n destY = Math.cos(time * 0.5 * 2) * (v.height / 4);\n }\n\n const smoothFactor = 0.05;\n virtualMouse.current.x += (destX - virtualMouse.current.x) * smoothFactor;\n virtualMouse.current.y += (destY - virtualMouse.current.y) * smoothFactor;\n\n const targetX = virtualMouse.current.x;\n const targetY = virtualMouse.current.y;\n\n const globalRotation = state.clock.getElapsedTime() * rotationSpeed;\n\n particles.forEach((particle, i) => {\n let { t, speed, mx, my, mz, cz, randomRadiusOffset } = particle;\n\n t = particle.t += speed / 2;\n\n const projectionFactor = 1 - cz / 50;\n const projectedTargetX = targetX * projectionFactor;\n const projectedTargetY = targetY * projectionFactor;\n\n const dx = mx - projectedTargetX;\n const dy = my - projectedTargetY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n let targetPos = { x: mx, y: my, z: mz * depthFactor };\n\n if (dist < magnetRadius) {\n const angle = Math.atan2(dy, dx) + globalRotation;\n\n const wave = Math.sin(t * waveSpeed + angle) * (0.5 * waveAmplitude);\n const deviation = randomRadiusOffset * (5 / (fieldStrength + 0.1));\n\n const currentRingRadius = ringRadius + wave + deviation;\n\n targetPos.x = projectedTargetX + currentRingRadius * Math.cos(angle);\n targetPos.y = projectedTargetY + currentRingRadius * Math.sin(angle);\n targetPos.z = mz * depthFactor + Math.sin(t) * (1 * waveAmplitude * depthFactor);\n }\n\n particle.cx += (targetPos.x - particle.cx) * lerpSpeed;\n particle.cy += (targetPos.y - particle.cy) * lerpSpeed;\n particle.cz += (targetPos.z - particle.cz) * lerpSpeed;\n\n dummy.position.set(particle.cx, particle.cy, particle.cz);\n\n dummy.lookAt(projectedTargetX, projectedTargetY, particle.cz);\n dummy.rotateX(Math.PI / 2);\n\n const currentDistToMouse = Math.sqrt(\n Math.pow(particle.cx - projectedTargetX, 2) + Math.pow(particle.cy - projectedTargetY, 2)\n );\n\n const distFromRing = Math.abs(currentDistToMouse - ringRadius);\n let scaleFactor = 1 - distFromRing / 10;\n\n scaleFactor = Math.max(0, Math.min(1, scaleFactor));\n\n const finalScale = scaleFactor * (0.8 + Math.sin(t * pulseSpeed) * 0.2 * particleVariance) * particleSize;\n dummy.scale.set(finalScale, finalScale, finalScale);\n\n dummy.updateMatrix();\n\n mesh.setMatrixAt(i, dummy.matrix);\n });\n\n mesh.instanceMatrix.needsUpdate = true;\n });\n\n return (\n \n {particleShape === 'capsule' && }\n {particleShape === 'sphere' && }\n {particleShape === 'box' && }\n {particleShape === 'tetrahedron' && }\n \n \n );\n};\n\nconst Antigravity: React.FC = props => {\n return (\n \n \n \n );\n};\n\nexport default Antigravity;\n" } ], "registryDependencies": [], "dependencies": [ "@react-three/fiber@^9.3.0", "three@^0.167.1" ] } ================================================ FILE: public/r/Antigravity-TS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Antigravity-TS-TW", "title": "Antigravity", "description": "3D antigravity particle field that repels from the cursor with smooth motion.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Antigravity/Antigravity.tsx", "content": "import { Canvas, useFrame, useThree } from '@react-three/fiber';\nimport React, { useMemo, useRef } from 'react';\nimport * as THREE from 'three';\n\ninterface AntigravityProps {\n count?: number;\n magnetRadius?: number;\n ringRadius?: number;\n waveSpeed?: number;\n waveAmplitude?: number;\n particleSize?: number;\n lerpSpeed?: number;\n color?: string;\n autoAnimate?: boolean;\n particleVariance?: number;\n rotationSpeed?: number;\n depthFactor?: number;\n pulseSpeed?: number;\n particleShape?: 'capsule' | 'sphere' | 'box' | 'tetrahedron';\n fieldStrength?: number;\n}\n\nconst AntigravityInner: React.FC = ({\n count = 300,\n magnetRadius = 10,\n ringRadius = 10,\n waveSpeed = 0.4,\n waveAmplitude = 1,\n particleSize = 2,\n lerpSpeed = 0.1,\n color = '#FF9FFC',\n autoAnimate = false,\n particleVariance = 1,\n rotationSpeed = 0,\n depthFactor = 1,\n pulseSpeed = 3,\n particleShape = 'capsule',\n fieldStrength = 10\n}) => {\n const meshRef = useRef(null);\n const { viewport } = useThree();\n const dummy = useMemo(() => new THREE.Object3D(), []);\n\n const lastMousePos = useRef({ x: 0, y: 0 });\n const lastMouseMoveTime = useRef(0);\n const virtualMouse = useRef({ x: 0, y: 0 });\n\n const particles = useMemo(() => {\n const temp = [];\n const width = viewport.width || 100;\n const height = viewport.height || 100;\n\n for (let i = 0; i < count; i++) {\n const t = Math.random() * 100;\n const factor = 20 + Math.random() * 100;\n const speed = 0.01 + Math.random() / 200;\n const xFactor = -50 + Math.random() * 100;\n const yFactor = -50 + Math.random() * 100;\n const zFactor = -50 + Math.random() * 100;\n\n const x = (Math.random() - 0.5) * width;\n const y = (Math.random() - 0.5) * height;\n const z = (Math.random() - 0.5) * 20;\n\n const randomRadiusOffset = (Math.random() - 0.5) * 2;\n\n temp.push({\n t,\n factor,\n speed,\n xFactor,\n yFactor,\n zFactor,\n mx: x,\n my: y,\n mz: z,\n cx: x,\n cy: y,\n cz: z,\n vx: 0,\n vy: 0,\n vz: 0,\n randomRadiusOffset\n });\n }\n return temp;\n }, [count, viewport.width, viewport.height]);\n\n useFrame(state => {\n const mesh = meshRef.current;\n if (!mesh) return;\n\n const { viewport: v, pointer: m } = state;\n\n const mouseDist = Math.sqrt(Math.pow(m.x - lastMousePos.current.x, 2) + Math.pow(m.y - lastMousePos.current.y, 2));\n\n if (mouseDist > 0.001) {\n lastMouseMoveTime.current = Date.now();\n lastMousePos.current = { x: m.x, y: m.y };\n }\n\n let destX = (m.x * v.width) / 2;\n let destY = (m.y * v.height) / 2;\n\n if (autoAnimate && Date.now() - lastMouseMoveTime.current > 2000) {\n const time = state.clock.getElapsedTime();\n destX = Math.sin(time * 0.5) * (v.width / 4);\n destY = Math.cos(time * 0.5 * 2) * (v.height / 4);\n }\n\n const smoothFactor = 0.05;\n virtualMouse.current.x += (destX - virtualMouse.current.x) * smoothFactor;\n virtualMouse.current.y += (destY - virtualMouse.current.y) * smoothFactor;\n\n const targetX = virtualMouse.current.x;\n const targetY = virtualMouse.current.y;\n\n const globalRotation = state.clock.getElapsedTime() * rotationSpeed;\n\n particles.forEach((particle, i) => {\n let { t, speed, mx, my, mz, cz, randomRadiusOffset } = particle;\n\n t = particle.t += speed / 2;\n\n const projectionFactor = 1 - cz / 50;\n const projectedTargetX = targetX * projectionFactor;\n const projectedTargetY = targetY * projectionFactor;\n\n const dx = mx - projectedTargetX;\n const dy = my - projectedTargetY;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n let targetPos = { x: mx, y: my, z: mz * depthFactor };\n\n if (dist < magnetRadius) {\n const angle = Math.atan2(dy, dx) + globalRotation;\n\n const wave = Math.sin(t * waveSpeed + angle) * (0.5 * waveAmplitude);\n const deviation = randomRadiusOffset * (5 / (fieldStrength + 0.1));\n\n const currentRingRadius = ringRadius + wave + deviation;\n\n targetPos.x = projectedTargetX + currentRingRadius * Math.cos(angle);\n targetPos.y = projectedTargetY + currentRingRadius * Math.sin(angle);\n targetPos.z = mz * depthFactor + Math.sin(t) * (1 * waveAmplitude * depthFactor);\n }\n\n particle.cx += (targetPos.x - particle.cx) * lerpSpeed;\n particle.cy += (targetPos.y - particle.cy) * lerpSpeed;\n particle.cz += (targetPos.z - particle.cz) * lerpSpeed;\n\n dummy.position.set(particle.cx, particle.cy, particle.cz);\n\n dummy.lookAt(projectedTargetX, projectedTargetY, particle.cz);\n dummy.rotateX(Math.PI / 2);\n\n const currentDistToMouse = Math.sqrt(\n Math.pow(particle.cx - projectedTargetX, 2) + Math.pow(particle.cy - projectedTargetY, 2)\n );\n\n const distFromRing = Math.abs(currentDistToMouse - ringRadius);\n let scaleFactor = 1 - distFromRing / 10;\n\n scaleFactor = Math.max(0, Math.min(1, scaleFactor));\n\n const finalScale = scaleFactor * (0.8 + Math.sin(t * pulseSpeed) * 0.2 * particleVariance) * particleSize;\n dummy.scale.set(finalScale, finalScale, finalScale);\n\n dummy.updateMatrix();\n\n mesh.setMatrixAt(i, dummy.matrix);\n });\n\n mesh.instanceMatrix.needsUpdate = true;\n });\n\n return (\n \n {particleShape === 'capsule' && }\n {particleShape === 'sphere' && }\n {particleShape === 'box' && }\n {particleShape === 'tetrahedron' && }\n \n \n );\n};\n\nconst Antigravity: React.FC = props => {\n return (\n \n \n \n );\n};\n\nexport default Antigravity;\n" } ], "registryDependencies": [], "dependencies": [ "@react-three/fiber@^9.3.0", "three@^0.167.1" ] } ================================================ FILE: public/r/Aurora-JS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Aurora-JS-CSS", "title": "Aurora", "description": "Flowing aurora gradient background.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Aurora/Aurora.css", "content": ".aurora-container {\n width: 100%;\n height: 100%;\n}\n" }, { "type": "registry:component", "path": "Aurora/Aurora.jsx", "content": "import { Renderer, Program, Mesh, Color, Triangle } from 'ogl';\nimport { useEffect, useRef } from 'react';\n\nimport './Aurora.css';\n\nconst VERT = `#version 300 es\nin vec2 position;\nvoid main() {\n gl_Position = vec4(position, 0.0, 1.0);\n}\n`;\n\nconst FRAG = `#version 300 es\nprecision highp float;\n\nuniform float uTime;\nuniform float uAmplitude;\nuniform vec3 uColorStops[3];\nuniform vec2 uResolution;\nuniform float uBlend;\n\nout vec4 fragColor;\n\nvec3 permute(vec3 x) {\n return mod(((x * 34.0) + 1.0) * x, 289.0);\n}\n\nfloat snoise(vec2 v){\n const vec4 C = vec4(\n 0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439\n );\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod(i, 289.0);\n\n vec3 p = permute(\n permute(i.y + vec3(0.0, i1.y, 1.0))\n + i.x + vec3(0.0, i1.x, 1.0)\n );\n\n vec3 m = max(\n 0.5 - vec3(\n dot(x0, x0),\n dot(x12.xy, x12.xy),\n dot(x12.zw, x12.zw)\n ), \n 0.0\n );\n m = m * m;\n m = m * m;\n\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x) - 0.5;\n vec3 ox = floor(x + 0.5);\n vec3 a0 = x - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);\n\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n}\n\nstruct ColorStop {\n vec3 color;\n float position;\n};\n\n#define COLOR_RAMP(colors, factor, finalColor) { \\\n int index = 0; \\\n for (int i = 0; i < 2; i++) { \\\n ColorStop currentColor = colors[i]; \\\n bool isInBetween = currentColor.position <= factor; \\\n index = int(mix(float(index), float(i), float(isInBetween))); \\\n } \\\n ColorStop currentColor = colors[index]; \\\n ColorStop nextColor = colors[index + 1]; \\\n float range = nextColor.position - currentColor.position; \\\n float lerpFactor = (factor - currentColor.position) / range; \\\n finalColor = mix(currentColor.color, nextColor.color, lerpFactor); \\\n}\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / uResolution;\n \n ColorStop colors[3];\n colors[0] = ColorStop(uColorStops[0], 0.0);\n colors[1] = ColorStop(uColorStops[1], 0.5);\n colors[2] = ColorStop(uColorStops[2], 1.0);\n \n vec3 rampColor;\n COLOR_RAMP(colors, uv.x, rampColor);\n \n float height = snoise(vec2(uv.x * 2.0 + uTime * 0.1, uTime * 0.25)) * 0.5 * uAmplitude;\n height = exp(height);\n height = (uv.y * 2.0 - height + 0.2);\n float intensity = 0.6 * height;\n \n float midPoint = 0.20;\n float auroraAlpha = smoothstep(midPoint - uBlend * 0.5, midPoint + uBlend * 0.5, intensity);\n \n vec3 auroraColor = intensity * rampColor;\n \n fragColor = vec4(auroraColor * auroraAlpha, auroraAlpha);\n}\n`;\n\nexport default function Aurora(props) {\n const { colorStops = ['#5227FF', '#7cff67', '#5227FF'], amplitude = 1.0, blend = 0.5 } = props;\n const propsRef = useRef(props);\n propsRef.current = props;\n\n const ctnDom = useRef(null);\n\n useEffect(() => {\n const ctn = ctnDom.current;\n if (!ctn) return;\n\n const renderer = new Renderer({\n alpha: true,\n premultipliedAlpha: true,\n antialias: true\n });\n const gl = renderer.gl;\n gl.clearColor(0, 0, 0, 0);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n gl.canvas.style.backgroundColor = 'transparent';\n\n let program;\n\n function resize() {\n if (!ctn) return;\n const width = ctn.offsetWidth;\n const height = ctn.offsetHeight;\n renderer.setSize(width, height);\n if (program) {\n program.uniforms.uResolution.value = [width, height];\n }\n }\n window.addEventListener('resize', resize);\n\n const geometry = new Triangle(gl);\n if (geometry.attributes.uv) {\n delete geometry.attributes.uv;\n }\n\n const colorStopsArray = colorStops.map(hex => {\n const c = new Color(hex);\n return [c.r, c.g, c.b];\n });\n\n program = new Program(gl, {\n vertex: VERT,\n fragment: FRAG,\n uniforms: {\n uTime: { value: 0 },\n uAmplitude: { value: amplitude },\n uColorStops: { value: colorStopsArray },\n uResolution: { value: [ctn.offsetWidth, ctn.offsetHeight] },\n uBlend: { value: blend }\n }\n });\n\n const mesh = new Mesh(gl, { geometry, program });\n ctn.appendChild(gl.canvas);\n\n let animateId = 0;\n const update = t => {\n animateId = requestAnimationFrame(update);\n const { time = t * 0.01, speed = 1.0 } = propsRef.current;\n program.uniforms.uTime.value = time * speed * 0.1;\n program.uniforms.uAmplitude.value = propsRef.current.amplitude ?? 1.0;\n program.uniforms.uBlend.value = propsRef.current.blend ?? blend;\n const stops = propsRef.current.colorStops ?? colorStops;\n program.uniforms.uColorStops.value = stops.map(hex => {\n const c = new Color(hex);\n return [c.r, c.g, c.b];\n });\n renderer.render({ scene: mesh });\n };\n animateId = requestAnimationFrame(update);\n\n resize();\n\n return () => {\n cancelAnimationFrame(animateId);\n window.removeEventListener('resize', resize);\n if (ctn && gl.canvas.parentNode === ctn) {\n ctn.removeChild(gl.canvas);\n }\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [amplitude]);\n\n return
;\n}\n" } ], "registryDependencies": [], "dependencies": [ "ogl@^1.0.11" ] } ================================================ FILE: public/r/Aurora-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Aurora-JS-TW", "title": "Aurora", "description": "Flowing aurora gradient background.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Aurora/Aurora.jsx", "content": "import { Renderer, Program, Mesh, Color, Triangle } from 'ogl';\nimport { useEffect, useRef } from 'react';\n\nconst VERT = `#version 300 es\nin vec2 position;\nvoid main() {\n gl_Position = vec4(position, 0.0, 1.0);\n}\n`;\n\nconst FRAG = `#version 300 es\nprecision highp float;\n\nuniform float uTime;\nuniform float uAmplitude;\nuniform vec3 uColorStops[3];\nuniform vec2 uResolution;\nuniform float uBlend;\n\nout vec4 fragColor;\n\nvec3 permute(vec3 x) {\n return mod(((x * 34.0) + 1.0) * x, 289.0);\n}\n\nfloat snoise(vec2 v){\n const vec4 C = vec4(\n 0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439\n );\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod(i, 289.0);\n\n vec3 p = permute(\n permute(i.y + vec3(0.0, i1.y, 1.0))\n + i.x + vec3(0.0, i1.x, 1.0)\n );\n\n vec3 m = max(\n 0.5 - vec3(\n dot(x0, x0),\n dot(x12.xy, x12.xy),\n dot(x12.zw, x12.zw)\n ), \n 0.0\n );\n m = m * m;\n m = m * m;\n\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x) - 0.5;\n vec3 ox = floor(x + 0.5);\n vec3 a0 = x - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);\n\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n}\n\nstruct ColorStop {\n vec3 color;\n float position;\n};\n\n#define COLOR_RAMP(colors, factor, finalColor) { \\\n int index = 0; \\\n for (int i = 0; i < 2; i++) { \\\n ColorStop currentColor = colors[i]; \\\n bool isInBetween = currentColor.position <= factor; \\\n index = int(mix(float(index), float(i), float(isInBetween))); \\\n } \\\n ColorStop currentColor = colors[index]; \\\n ColorStop nextColor = colors[index + 1]; \\\n float range = nextColor.position - currentColor.position; \\\n float lerpFactor = (factor - currentColor.position) / range; \\\n finalColor = mix(currentColor.color, nextColor.color, lerpFactor); \\\n}\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / uResolution;\n \n ColorStop colors[3];\n colors[0] = ColorStop(uColorStops[0], 0.0);\n colors[1] = ColorStop(uColorStops[1], 0.5);\n colors[2] = ColorStop(uColorStops[2], 1.0);\n \n vec3 rampColor;\n COLOR_RAMP(colors, uv.x, rampColor);\n \n float height = snoise(vec2(uv.x * 2.0 + uTime * 0.1, uTime * 0.25)) * 0.5 * uAmplitude;\n height = exp(height);\n height = (uv.y * 2.0 - height + 0.2);\n float intensity = 0.6 * height;\n \n float midPoint = 0.20;\n float auroraAlpha = smoothstep(midPoint - uBlend * 0.5, midPoint + uBlend * 0.5, intensity);\n \n vec3 auroraColor = intensity * rampColor;\n \n fragColor = vec4(auroraColor * auroraAlpha, auroraAlpha);\n}\n`;\n\nexport default function Aurora(props) {\n const { colorStops = ['#5227FF', '#7cff67', '#5227FF'], amplitude = 1.0, blend = 0.5 } = props;\n const propsRef = useRef(props);\n propsRef.current = props;\n\n const ctnDom = useRef(null);\n\n useEffect(() => {\n const ctn = ctnDom.current;\n if (!ctn) return;\n\n const renderer = new Renderer({\n alpha: true,\n premultipliedAlpha: true,\n antialias: true\n });\n const gl = renderer.gl;\n gl.clearColor(0, 0, 0, 0);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n gl.canvas.style.backgroundColor = 'transparent';\n\n let program;\n\n function resize() {\n if (!ctn) return;\n const width = ctn.offsetWidth;\n const height = ctn.offsetHeight;\n renderer.setSize(width, height);\n if (program) {\n program.uniforms.uResolution.value = [width, height];\n }\n }\n window.addEventListener('resize', resize);\n\n const geometry = new Triangle(gl);\n if (geometry.attributes.uv) {\n delete geometry.attributes.uv;\n }\n\n const colorStopsArray = colorStops.map(hex => {\n const c = new Color(hex);\n return [c.r, c.g, c.b];\n });\n\n program = new Program(gl, {\n vertex: VERT,\n fragment: FRAG,\n uniforms: {\n uTime: { value: 0 },\n uAmplitude: { value: amplitude },\n uColorStops: { value: colorStopsArray },\n uResolution: { value: [ctn.offsetWidth, ctn.offsetHeight] },\n uBlend: { value: blend }\n }\n });\n\n const mesh = new Mesh(gl, { geometry, program });\n ctn.appendChild(gl.canvas);\n\n let animateId = 0;\n const update = t => {\n animateId = requestAnimationFrame(update);\n const { time = t * 0.01, speed = 1.0 } = propsRef.current;\n program.uniforms.uTime.value = time * speed * 0.1;\n program.uniforms.uAmplitude.value = propsRef.current.amplitude ?? 1.0;\n program.uniforms.uBlend.value = propsRef.current.blend ?? blend;\n const stops = propsRef.current.colorStops ?? colorStops;\n program.uniforms.uColorStops.value = stops.map(hex => {\n const c = new Color(hex);\n return [c.r, c.g, c.b];\n });\n renderer.render({ scene: mesh });\n };\n animateId = requestAnimationFrame(update);\n\n resize();\n\n return () => {\n cancelAnimationFrame(animateId);\n window.removeEventListener('resize', resize);\n if (ctn && gl.canvas.parentNode === ctn) {\n ctn.removeChild(gl.canvas);\n }\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [amplitude]);\n\n return
;\n}\n" } ], "registryDependencies": [], "dependencies": [ "ogl@^1.0.11" ] } ================================================ FILE: public/r/Aurora-TS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Aurora-TS-CSS", "title": "Aurora", "description": "Flowing aurora gradient background.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Aurora/Aurora.css", "content": ".aurora-container {\n width: 100%;\n height: 100%;\n}\n" }, { "type": "registry:component", "path": "Aurora/Aurora.tsx", "content": "import { useEffect, useRef } from 'react';\nimport { Renderer, Program, Mesh, Color, Triangle } from 'ogl';\n\nimport './Aurora.css';\n\nconst VERT = `#version 300 es\nin vec2 position;\nvoid main() {\n gl_Position = vec4(position, 0.0, 1.0);\n}\n`;\n\nconst FRAG = `#version 300 es\nprecision highp float;\n\nuniform float uTime;\nuniform float uAmplitude;\nuniform vec3 uColorStops[3];\nuniform vec2 uResolution;\nuniform float uBlend;\n\nout vec4 fragColor;\n\nvec3 permute(vec3 x) {\n return mod(((x * 34.0) + 1.0) * x, 289.0);\n}\n\nfloat snoise(vec2 v){\n const vec4 C = vec4(\n 0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439\n );\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod(i, 289.0);\n\n vec3 p = permute(\n permute(i.y + vec3(0.0, i1.y, 1.0))\n + i.x + vec3(0.0, i1.x, 1.0)\n );\n\n vec3 m = max(\n 0.5 - vec3(\n dot(x0, x0),\n dot(x12.xy, x12.xy),\n dot(x12.zw, x12.zw)\n ), \n 0.0\n );\n m = m * m;\n m = m * m;\n\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x) - 0.5;\n vec3 ox = floor(x + 0.5);\n vec3 a0 = x - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);\n\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n}\n\nstruct ColorStop {\n vec3 color;\n float position;\n};\n\n#define COLOR_RAMP(colors, factor, finalColor) { \\\n int index = 0; \\\n for (int i = 0; i < 2; i++) { \\\n ColorStop currentColor = colors[i]; \\\n bool isInBetween = currentColor.position <= factor; \\\n index = int(mix(float(index), float(i), float(isInBetween))); \\\n } \\\n ColorStop currentColor = colors[index]; \\\n ColorStop nextColor = colors[index + 1]; \\\n float range = nextColor.position - currentColor.position; \\\n float lerpFactor = (factor - currentColor.position) / range; \\\n finalColor = mix(currentColor.color, nextColor.color, lerpFactor); \\\n}\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / uResolution;\n \n ColorStop colors[3];\n colors[0] = ColorStop(uColorStops[0], 0.0);\n colors[1] = ColorStop(uColorStops[1], 0.5);\n colors[2] = ColorStop(uColorStops[2], 1.0);\n \n vec3 rampColor;\n COLOR_RAMP(colors, uv.x, rampColor);\n \n float height = snoise(vec2(uv.x * 2.0 + uTime * 0.1, uTime * 0.25)) * 0.5 * uAmplitude;\n height = exp(height);\n height = (uv.y * 2.0 - height + 0.2);\n float intensity = 0.6 * height;\n \n float midPoint = 0.20;\n float auroraAlpha = smoothstep(midPoint - uBlend * 0.5, midPoint + uBlend * 0.5, intensity);\n \n vec3 auroraColor = intensity * rampColor;\n \n fragColor = vec4(auroraColor * auroraAlpha, auroraAlpha);\n}\n`;\n\ninterface AuroraProps {\n colorStops?: string[];\n amplitude?: number;\n blend?: number;\n time?: number;\n speed?: number;\n}\n\nexport default function Aurora(props: AuroraProps) {\n const { colorStops = ['#5227FF', '#7cff67', '#5227FF'], amplitude = 1.0, blend = 0.5 } = props;\n const propsRef = useRef(props);\n propsRef.current = props;\n\n const ctnDom = useRef(null);\n\n useEffect(() => {\n const ctn = ctnDom.current;\n if (!ctn) return;\n\n const renderer = new Renderer({\n alpha: true,\n premultipliedAlpha: true,\n antialias: true\n });\n const gl = renderer.gl;\n gl.clearColor(0, 0, 0, 0);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n gl.canvas.style.backgroundColor = 'transparent';\n\n let program: Program | undefined;\n\n function resize() {\n if (!ctn) return;\n const width = ctn.offsetWidth;\n const height = ctn.offsetHeight;\n renderer.setSize(width, height);\n if (program) {\n program.uniforms.uResolution.value = [width, height];\n }\n }\n window.addEventListener('resize', resize);\n\n const geometry = new Triangle(gl);\n if (geometry.attributes.uv) {\n delete geometry.attributes.uv;\n }\n\n const colorStopsArray = colorStops.map(hex => {\n const c = new Color(hex);\n return [c.r, c.g, c.b];\n });\n\n program = new Program(gl, {\n vertex: VERT,\n fragment: FRAG,\n uniforms: {\n uTime: { value: 0 },\n uAmplitude: { value: amplitude },\n uColorStops: { value: colorStopsArray },\n uResolution: { value: [ctn.offsetWidth, ctn.offsetHeight] },\n uBlend: { value: blend }\n }\n });\n\n const mesh = new Mesh(gl, { geometry, program });\n ctn.appendChild(gl.canvas);\n\n let animateId = 0;\n const update = (t: number) => {\n animateId = requestAnimationFrame(update);\n const { time = t * 0.01, speed = 1.0 } = propsRef.current;\n if (program) {\n program.uniforms.uTime.value = time * speed * 0.1;\n program.uniforms.uAmplitude.value = propsRef.current.amplitude ?? 1.0;\n program.uniforms.uBlend.value = propsRef.current.blend ?? blend;\n const stops = propsRef.current.colorStops ?? colorStops;\n program.uniforms.uColorStops.value = stops.map((hex: string) => {\n const c = new Color(hex);\n return [c.r, c.g, c.b];\n });\n renderer.render({ scene: mesh });\n }\n };\n animateId = requestAnimationFrame(update);\n\n resize();\n\n return () => {\n cancelAnimationFrame(animateId);\n window.removeEventListener('resize', resize);\n if (ctn && gl.canvas.parentNode === ctn) {\n ctn.removeChild(gl.canvas);\n }\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n };\n }, [amplitude]);\n\n return
;\n}\n" } ], "registryDependencies": [], "dependencies": [ "ogl@^1.0.11" ] } ================================================ FILE: public/r/Aurora-TS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Aurora-TS-TW", "title": "Aurora", "description": "Flowing aurora gradient background.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Aurora/Aurora.tsx", "content": "import { useEffect, useRef } from 'react';\nimport { Renderer, Program, Mesh, Color, Triangle } from 'ogl';\n\nconst VERT = `#version 300 es\nin vec2 position;\nvoid main() {\n gl_Position = vec4(position, 0.0, 1.0);\n}\n`;\n\nconst FRAG = `#version 300 es\nprecision highp float;\n\nuniform float uTime;\nuniform float uAmplitude;\nuniform vec3 uColorStops[3];\nuniform vec2 uResolution;\nuniform float uBlend;\n\nout vec4 fragColor;\n\nvec3 permute(vec3 x) {\n return mod(((x * 34.0) + 1.0) * x, 289.0);\n}\n\nfloat snoise(vec2 v){\n const vec4 C = vec4(\n 0.211324865405187, 0.366025403784439,\n -0.577350269189626, 0.024390243902439\n );\n vec2 i = floor(v + dot(v, C.yy));\n vec2 x0 = v - i + dot(i, C.xx);\n vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n vec4 x12 = x0.xyxy + C.xxzz;\n x12.xy -= i1;\n i = mod(i, 289.0);\n\n vec3 p = permute(\n permute(i.y + vec3(0.0, i1.y, 1.0))\n + i.x + vec3(0.0, i1.x, 1.0)\n );\n\n vec3 m = max(\n 0.5 - vec3(\n dot(x0, x0),\n dot(x12.xy, x12.xy),\n dot(x12.zw, x12.zw)\n ), \n 0.0\n );\n m = m * m;\n m = m * m;\n\n vec3 x = 2.0 * fract(p * C.www) - 1.0;\n vec3 h = abs(x) - 0.5;\n vec3 ox = floor(x + 0.5);\n vec3 a0 = x - ox;\n m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);\n\n vec3 g;\n g.x = a0.x * x0.x + h.x * x0.y;\n g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n return 130.0 * dot(m, g);\n}\n\nstruct ColorStop {\n vec3 color;\n float position;\n};\n\n#define COLOR_RAMP(colors, factor, finalColor) { \\\n int index = 0; \\\n for (int i = 0; i < 2; i++) { \\\n ColorStop currentColor = colors[i]; \\\n bool isInBetween = currentColor.position <= factor; \\\n index = int(mix(float(index), float(i), float(isInBetween))); \\\n } \\\n ColorStop currentColor = colors[index]; \\\n ColorStop nextColor = colors[index + 1]; \\\n float range = nextColor.position - currentColor.position; \\\n float lerpFactor = (factor - currentColor.position) / range; \\\n finalColor = mix(currentColor.color, nextColor.color, lerpFactor); \\\n}\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / uResolution;\n \n ColorStop colors[3];\n colors[0] = ColorStop(uColorStops[0], 0.0);\n colors[1] = ColorStop(uColorStops[1], 0.5);\n colors[2] = ColorStop(uColorStops[2], 1.0);\n \n vec3 rampColor;\n COLOR_RAMP(colors, uv.x, rampColor);\n \n float height = snoise(vec2(uv.x * 2.0 + uTime * 0.1, uTime * 0.25)) * 0.5 * uAmplitude;\n height = exp(height);\n height = (uv.y * 2.0 - height + 0.2);\n float intensity = 0.6 * height;\n \n float midPoint = 0.20;\n float auroraAlpha = smoothstep(midPoint - uBlend * 0.5, midPoint + uBlend * 0.5, intensity);\n \n vec3 auroraColor = intensity * rampColor;\n \n fragColor = vec4(auroraColor * auroraAlpha, auroraAlpha);\n}\n`;\n\ninterface AuroraProps {\n colorStops?: string[];\n amplitude?: number;\n blend?: number;\n time?: number;\n speed?: number;\n}\n\nexport default function Aurora(props: AuroraProps) {\n const { colorStops = ['#5227FF', '#7cff67', '#5227FF'], amplitude = 1.0, blend = 0.5 } = props;\n const propsRef = useRef(props);\n propsRef.current = props;\n\n const ctnDom = useRef(null);\n\n useEffect(() => {\n const ctn = ctnDom.current;\n if (!ctn) return;\n\n const renderer = new Renderer({\n alpha: true,\n premultipliedAlpha: true,\n antialias: true\n });\n const gl = renderer.gl;\n gl.clearColor(0, 0, 0, 0);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n gl.canvas.style.backgroundColor = 'transparent';\n\n let program: Program | undefined;\n\n function resize() {\n if (!ctn) return;\n const width = ctn.offsetWidth;\n const height = ctn.offsetHeight;\n renderer.setSize(width, height);\n if (program) {\n program.uniforms.uResolution.value = [width, height];\n }\n }\n window.addEventListener('resize', resize);\n\n const geometry = new Triangle(gl);\n if (geometry.attributes.uv) {\n delete geometry.attributes.uv;\n }\n\n const colorStopsArray = colorStops.map(hex => {\n const c = new Color(hex);\n return [c.r, c.g, c.b];\n });\n\n program = new Program(gl, {\n vertex: VERT,\n fragment: FRAG,\n uniforms: {\n uTime: { value: 0 },\n uAmplitude: { value: amplitude },\n uColorStops: { value: colorStopsArray },\n uResolution: { value: [ctn.offsetWidth, ctn.offsetHeight] },\n uBlend: { value: blend }\n }\n });\n\n const mesh = new Mesh(gl, { geometry, program });\n ctn.appendChild(gl.canvas);\n\n let animateId = 0;\n const update = (t: number) => {\n animateId = requestAnimationFrame(update);\n const { time = t * 0.01, speed = 1.0 } = propsRef.current;\n if (program) {\n program.uniforms.uTime.value = time * speed * 0.1;\n program.uniforms.uAmplitude.value = propsRef.current.amplitude ?? 1.0;\n program.uniforms.uBlend.value = propsRef.current.blend ?? blend;\n const stops = propsRef.current.colorStops ?? colorStops;\n program.uniforms.uColorStops.value = stops.map((hex: string) => {\n const c = new Color(hex);\n return [c.r, c.g, c.b];\n });\n renderer.render({ scene: mesh });\n }\n };\n animateId = requestAnimationFrame(update);\n\n resize();\n\n return () => {\n cancelAnimationFrame(animateId);\n window.removeEventListener('resize', resize);\n if (ctn && gl.canvas.parentNode === ctn) {\n ctn.removeChild(gl.canvas);\n }\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n };\n }, [amplitude]);\n\n return
;\n}\n" } ], "registryDependencies": [], "dependencies": [ "ogl@^1.0.11" ] } ================================================ FILE: public/r/Balatro-JS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Balatro-JS-CSS", "title": "Balatro", "description": "The balatro shader, fully customizalbe and interactive.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Balatro/Balatro.css", "content": ".balatro-container {\n width: 100%;\n height: 100%;\n}\n" }, { "type": "registry:component", "path": "Balatro/Balatro.jsx", "content": "import { Renderer, Program, Mesh, Triangle } from 'ogl';\nimport { useEffect, useRef } from 'react';\n\nimport './Balatro.css';\n\nfunction hexToVec4(hex) {\n let hexStr = hex.replace('#', '');\n let r = 0,\n g = 0,\n b = 0,\n a = 1;\n if (hexStr.length === 6) {\n r = parseInt(hexStr.slice(0, 2), 16) / 255;\n g = parseInt(hexStr.slice(2, 4), 16) / 255;\n b = parseInt(hexStr.slice(4, 6), 16) / 255;\n } else if (hexStr.length === 8) {\n r = parseInt(hexStr.slice(0, 2), 16) / 255;\n g = parseInt(hexStr.slice(2, 4), 16) / 255;\n b = parseInt(hexStr.slice(4, 6), 16) / 255;\n a = parseInt(hexStr.slice(6, 8), 16) / 255;\n }\n return [r, g, b, a];\n}\n\nconst vertexShader = `\nattribute vec2 uv;\nattribute vec2 position;\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position, 0, 1);\n}\n`;\n\nconst fragmentShader = `\nprecision highp float;\n\n#define PI 3.14159265359\n\nuniform float iTime;\nuniform vec3 iResolution;\nuniform float uSpinRotation;\nuniform float uSpinSpeed;\nuniform vec2 uOffset;\nuniform vec4 uColor1;\nuniform vec4 uColor2;\nuniform vec4 uColor3;\nuniform float uContrast;\nuniform float uLighting;\nuniform float uSpinAmount;\nuniform float uPixelFilter;\nuniform float uSpinEase;\nuniform bool uIsRotate;\nuniform vec2 uMouse;\n\nvarying vec2 vUv;\n\nvec4 effect(vec2 screenSize, vec2 screen_coords) {\n float pixel_size = length(screenSize.xy) / uPixelFilter;\n vec2 uv = (floor(screen_coords.xy * (1.0 / pixel_size)) * pixel_size - 0.5 * screenSize.xy) / length(screenSize.xy) - uOffset;\n float uv_len = length(uv);\n \n float speed = (uSpinRotation * uSpinEase * 0.2);\n if(uIsRotate){\n speed = iTime * speed;\n }\n speed += 302.2;\n \n float mouseInfluence = (uMouse.x * 2.0 - 1.0);\n speed += mouseInfluence * 0.1;\n \n float new_pixel_angle = atan(uv.y, uv.x) + speed - uSpinEase * 20.0 * (uSpinAmount * uv_len + (1.0 - uSpinAmount));\n vec2 mid = (screenSize.xy / length(screenSize.xy)) / 2.0;\n uv = (vec2(uv_len * cos(new_pixel_angle) + mid.x, uv_len * sin(new_pixel_angle) + mid.y) - mid);\n \n uv *= 30.0;\n float baseSpeed = iTime * uSpinSpeed;\n speed = baseSpeed + mouseInfluence * 2.0;\n \n vec2 uv2 = vec2(uv.x + uv.y);\n \n for(int i = 0; i < 5; i++) {\n uv2 += sin(max(uv.x, uv.y)) + uv;\n uv += 0.5 * vec2(\n cos(5.1123314 + 0.353 * uv2.y + speed * 0.131121),\n sin(uv2.x - 0.113 * speed)\n );\n uv -= cos(uv.x + uv.y) - sin(uv.x * 0.711 - uv.y);\n }\n \n float contrast_mod = (0.25 * uContrast + 0.5 * uSpinAmount + 1.2);\n float paint_res = min(2.0, max(0.0, length(uv) * 0.035 * contrast_mod));\n float c1p = max(0.0, 1.0 - contrast_mod * abs(1.0 - paint_res));\n float c2p = max(0.0, 1.0 - contrast_mod * abs(paint_res));\n float c3p = 1.0 - min(1.0, c1p + c2p);\n float light = (uLighting - 0.2) * max(c1p * 5.0 - 4.0, 0.0) + uLighting * max(c2p * 5.0 - 4.0, 0.0);\n \n return (0.3 / uContrast) * uColor1 + (1.0 - 0.3 / uContrast) * (uColor1 * c1p + uColor2 * c2p + vec4(c3p * uColor3.rgb, c3p * uColor1.a)) + light;\n}\n\nvoid main() {\n vec2 uv = vUv * iResolution.xy;\n gl_FragColor = effect(iResolution.xy, uv);\n}\n`;\n\nexport default function Balatro({\n spinRotation = -2.0,\n spinSpeed = 7.0,\n offset = [0.0, 0.0],\n color1 = '#DE443B',\n color2 = '#006BB4',\n color3 = '#162325',\n contrast = 3.5,\n lighting = 0.4,\n spinAmount = 0.25,\n pixelFilter = 745.0,\n spinEase = 1.0,\n isRotate = false,\n mouseInteraction = true\n}) {\n const containerRef = useRef(null);\n\n useEffect(() => {\n if (!containerRef.current) return;\n const container = containerRef.current;\n const renderer = new Renderer();\n const gl = renderer.gl;\n gl.clearColor(0, 0, 0, 1);\n\n let program;\n\n function resize() {\n renderer.setSize(container.offsetWidth, container.offsetHeight);\n if (program) {\n program.uniforms.iResolution.value = [gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height];\n }\n }\n window.addEventListener('resize', resize);\n resize();\n\n const geometry = new Triangle(gl);\n program = new Program(gl, {\n vertex: vertexShader,\n fragment: fragmentShader,\n uniforms: {\n iTime: { value: 0 },\n iResolution: {\n value: [gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height]\n },\n uSpinRotation: { value: spinRotation },\n uSpinSpeed: { value: spinSpeed },\n uOffset: { value: offset },\n uColor1: { value: hexToVec4(color1) },\n uColor2: { value: hexToVec4(color2) },\n uColor3: { value: hexToVec4(color3) },\n uContrast: { value: contrast },\n uLighting: { value: lighting },\n uSpinAmount: { value: spinAmount },\n uPixelFilter: { value: pixelFilter },\n uSpinEase: { value: spinEase },\n uIsRotate: { value: isRotate },\n uMouse: { value: [0.5, 0.5] }\n }\n });\n\n const mesh = new Mesh(gl, { geometry, program });\n let animationFrameId;\n\n function update(time) {\n animationFrameId = requestAnimationFrame(update);\n program.uniforms.iTime.value = time * 0.001;\n renderer.render({ scene: mesh });\n }\n animationFrameId = requestAnimationFrame(update);\n container.appendChild(gl.canvas);\n\n function handleMouseMove(e) {\n if (!mouseInteraction) return;\n const rect = container.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n const y = 1.0 - (e.clientY - rect.top) / rect.height;\n program.uniforms.uMouse.value = [x, y];\n }\n container.addEventListener('mousemove', handleMouseMove);\n\n return () => {\n cancelAnimationFrame(animationFrameId);\n window.removeEventListener('resize', resize);\n container.removeEventListener('mousemove', handleMouseMove);\n container.removeChild(gl.canvas);\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n };\n }, [\n spinRotation,\n spinSpeed,\n offset,\n color1,\n color2,\n color3,\n contrast,\n lighting,\n spinAmount,\n pixelFilter,\n spinEase,\n isRotate,\n mouseInteraction,\n containerRef\n ]);\n\n return
;\n}\n" } ], "registryDependencies": [], "dependencies": [ "ogl@^1.0.11" ] } ================================================ FILE: public/r/Balatro-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Balatro-JS-TW", "title": "Balatro", "description": "The balatro shader, fully customizalbe and interactive.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Balatro/Balatro.jsx", "content": "import { Renderer, Program, Mesh, Triangle } from 'ogl';\nimport { useEffect, useRef } from 'react';\n\nfunction hexToVec4(hex) {\n let hexStr = hex.replace('#', '');\n let r = 0,\n g = 0,\n b = 0,\n a = 1;\n if (hexStr.length === 6) {\n r = parseInt(hexStr.slice(0, 2), 16) / 255;\n g = parseInt(hexStr.slice(2, 4), 16) / 255;\n b = parseInt(hexStr.slice(4, 6), 16) / 255;\n } else if (hexStr.length === 8) {\n r = parseInt(hexStr.slice(0, 2), 16) / 255;\n g = parseInt(hexStr.slice(2, 4), 16) / 255;\n b = parseInt(hexStr.slice(4, 6), 16) / 255;\n a = parseInt(hexStr.slice(6, 8), 16) / 255;\n }\n return [r, g, b, a];\n}\n\nconst vertexShader = `\nattribute vec2 uv;\nattribute vec2 position;\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position, 0, 1);\n}\n`;\n\nconst fragmentShader = `\nprecision highp float;\n\n#define PI 3.14159265359\n\nuniform float iTime;\nuniform vec3 iResolution;\nuniform float uSpinRotation;\nuniform float uSpinSpeed;\nuniform vec2 uOffset;\nuniform vec4 uColor1;\nuniform vec4 uColor2;\nuniform vec4 uColor3;\nuniform float uContrast;\nuniform float uLighting;\nuniform float uSpinAmount;\nuniform float uPixelFilter;\nuniform float uSpinEase;\nuniform bool uIsRotate;\nuniform vec2 uMouse;\n\nvarying vec2 vUv;\n\nvec4 effect(vec2 screenSize, vec2 screen_coords) {\n float pixel_size = length(screenSize.xy) / uPixelFilter;\n vec2 uv = (floor(screen_coords.xy * (1.0 / pixel_size)) * pixel_size - 0.5 * screenSize.xy) / length(screenSize.xy) - uOffset;\n float uv_len = length(uv);\n \n float speed = (uSpinRotation * uSpinEase * 0.2);\n if(uIsRotate){\n speed = iTime * speed;\n }\n speed += 302.2;\n \n float mouseInfluence = (uMouse.x * 2.0 - 1.0);\n speed += mouseInfluence * 0.1;\n \n float new_pixel_angle = atan(uv.y, uv.x) + speed - uSpinEase * 20.0 * (uSpinAmount * uv_len + (1.0 - uSpinAmount));\n vec2 mid = (screenSize.xy / length(screenSize.xy)) / 2.0;\n uv = (vec2(uv_len * cos(new_pixel_angle) + mid.x, uv_len * sin(new_pixel_angle) + mid.y) - mid);\n \n uv *= 30.0;\n float baseSpeed = iTime * uSpinSpeed;\n speed = baseSpeed + mouseInfluence * 2.0;\n \n vec2 uv2 = vec2(uv.x + uv.y);\n \n for(int i = 0; i < 5; i++) {\n uv2 += sin(max(uv.x, uv.y)) + uv;\n uv += 0.5 * vec2(\n cos(5.1123314 + 0.353 * uv2.y + speed * 0.131121),\n sin(uv2.x - 0.113 * speed)\n );\n uv -= cos(uv.x + uv.y) - sin(uv.x * 0.711 - uv.y);\n }\n \n float contrast_mod = (0.25 * uContrast + 0.5 * uSpinAmount + 1.2);\n float paint_res = min(2.0, max(0.0, length(uv) * 0.035 * contrast_mod));\n float c1p = max(0.0, 1.0 - contrast_mod * abs(1.0 - paint_res));\n float c2p = max(0.0, 1.0 - contrast_mod * abs(paint_res));\n float c3p = 1.0 - min(1.0, c1p + c2p);\n float light = (uLighting - 0.2) * max(c1p * 5.0 - 4.0, 0.0) + uLighting * max(c2p * 5.0 - 4.0, 0.0);\n \n return (0.3 / uContrast) * uColor1 + (1.0 - 0.3 / uContrast) * (uColor1 * c1p + uColor2 * c2p + vec4(c3p * uColor3.rgb, c3p * uColor1.a)) + light;\n}\n\nvoid main() {\n vec2 uv = vUv * iResolution.xy;\n gl_FragColor = effect(iResolution.xy, uv);\n}\n`;\n\nexport default function Balatro({\n spinRotation = -2.0,\n spinSpeed = 7.0,\n offset = [0.0, 0.0],\n color1 = '#DE443B',\n color2 = '#006BB4',\n color3 = '#162325',\n contrast = 3.5,\n lighting = 0.4,\n spinAmount = 0.25,\n pixelFilter = 745.0,\n spinEase = 1.0,\n isRotate = false,\n mouseInteraction = true\n}) {\n const containerRef = useRef(null);\n\n useEffect(() => {\n if (!containerRef.current) return;\n const container = containerRef.current;\n const renderer = new Renderer();\n const gl = renderer.gl;\n gl.clearColor(0, 0, 0, 1);\n\n let program;\n\n function resize() {\n renderer.setSize(container.offsetWidth, container.offsetHeight);\n if (program) {\n program.uniforms.iResolution.value = [gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height];\n }\n }\n window.addEventListener('resize', resize);\n resize();\n\n const geometry = new Triangle(gl);\n program = new Program(gl, {\n vertex: vertexShader,\n fragment: fragmentShader,\n uniforms: {\n iTime: { value: 0 },\n iResolution: {\n value: [gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height]\n },\n uSpinRotation: { value: spinRotation },\n uSpinSpeed: { value: spinSpeed },\n uOffset: { value: offset },\n uColor1: { value: hexToVec4(color1) },\n uColor2: { value: hexToVec4(color2) },\n uColor3: { value: hexToVec4(color3) },\n uContrast: { value: contrast },\n uLighting: { value: lighting },\n uSpinAmount: { value: spinAmount },\n uPixelFilter: { value: pixelFilter },\n uSpinEase: { value: spinEase },\n uIsRotate: { value: isRotate },\n uMouse: { value: [0.5, 0.5] }\n }\n });\n\n const mesh = new Mesh(gl, { geometry, program });\n let animationFrameId;\n\n function update(time) {\n animationFrameId = requestAnimationFrame(update);\n program.uniforms.iTime.value = time * 0.001;\n renderer.render({ scene: mesh });\n }\n animationFrameId = requestAnimationFrame(update);\n container.appendChild(gl.canvas);\n\n function handleMouseMove(e) {\n if (!mouseInteraction) return;\n const rect = container.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n const y = 1.0 - (e.clientY - rect.top) / rect.height;\n program.uniforms.uMouse.value = [x, y];\n }\n container.addEventListener('mousemove', handleMouseMove);\n\n return () => {\n cancelAnimationFrame(animationFrameId);\n window.removeEventListener('resize', resize);\n container.removeEventListener('mousemove', handleMouseMove);\n container.removeChild(gl.canvas);\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n };\n }, [\n spinRotation,\n spinSpeed,\n offset,\n color1,\n color2,\n color3,\n contrast,\n lighting,\n spinAmount,\n pixelFilter,\n spinEase,\n isRotate,\n mouseInteraction,\n containerRef\n ]);\n\n return
;\n}\n" } ], "registryDependencies": [], "dependencies": [ "ogl@^1.0.11" ] } ================================================ FILE: public/r/Balatro-TS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Balatro-TS-CSS", "title": "Balatro", "description": "The balatro shader, fully customizalbe and interactive.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Balatro/Balatro.css", "content": ".balatro-container {\n width: 100%;\n height: 100%;\n}\n" }, { "type": "registry:component", "path": "Balatro/Balatro.tsx", "content": "import { Renderer, Program, Mesh, Triangle } from 'ogl';\nimport { useEffect, useRef } from 'react';\n\nimport './Balatro.css';\n\ninterface BalatroProps {\n spinRotation?: number;\n spinSpeed?: number;\n offset?: [number, number];\n color1?: string;\n color2?: string;\n color3?: string;\n contrast?: number;\n lighting?: number;\n spinAmount?: number;\n pixelFilter?: number;\n spinEase?: number;\n isRotate?: boolean;\n mouseInteraction?: boolean;\n}\n\nfunction hexToVec4(hex: string): [number, number, number, number] {\n let hexStr = hex.replace('#', '');\n let r = 0,\n g = 0,\n b = 0,\n a = 1;\n if (hexStr.length === 6) {\n r = parseInt(hexStr.slice(0, 2), 16) / 255;\n g = parseInt(hexStr.slice(2, 4), 16) / 255;\n b = parseInt(hexStr.slice(4, 6), 16) / 255;\n } else if (hexStr.length === 8) {\n r = parseInt(hexStr.slice(0, 2), 16) / 255;\n g = parseInt(hexStr.slice(2, 4), 16) / 255;\n b = parseInt(hexStr.slice(4, 6), 16) / 255;\n a = parseInt(hexStr.slice(6, 8), 16) / 255;\n }\n return [r, g, b, a];\n}\n\nconst vertexShader = `\nattribute vec2 uv;\nattribute vec2 position;\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position, 0, 1);\n}\n`;\n\nconst fragmentShader = `\nprecision highp float;\n\n#define PI 3.14159265359\n\nuniform float iTime;\nuniform vec3 iResolution;\nuniform float uSpinRotation;\nuniform float uSpinSpeed;\nuniform vec2 uOffset;\nuniform vec4 uColor1;\nuniform vec4 uColor2;\nuniform vec4 uColor3;\nuniform float uContrast;\nuniform float uLighting;\nuniform float uSpinAmount;\nuniform float uPixelFilter;\nuniform float uSpinEase;\nuniform bool uIsRotate;\nuniform vec2 uMouse;\n\nvarying vec2 vUv;\n\nvec4 effect(vec2 screenSize, vec2 screen_coords) {\n float pixel_size = length(screenSize.xy) / uPixelFilter;\n vec2 uv = (floor(screen_coords.xy * (1.0 / pixel_size)) * pixel_size - 0.5 * screenSize.xy) / length(screenSize.xy) - uOffset;\n float uv_len = length(uv);\n \n float speed = (uSpinRotation * uSpinEase * 0.2);\n if(uIsRotate){\n speed = iTime * speed;\n }\n speed += 302.2;\n \n float mouseInfluence = (uMouse.x * 2.0 - 1.0);\n speed += mouseInfluence * 0.1;\n \n float new_pixel_angle = atan(uv.y, uv.x) + speed - uSpinEase * 20.0 * (uSpinAmount * uv_len + (1.0 - uSpinAmount));\n vec2 mid = (screenSize.xy / length(screenSize.xy)) / 2.0;\n uv = (vec2(uv_len * cos(new_pixel_angle) + mid.x, uv_len * sin(new_pixel_angle) + mid.y) - mid);\n \n uv *= 30.0;\n float baseSpeed = iTime * uSpinSpeed;\n speed = baseSpeed + mouseInfluence * 2.0;\n \n vec2 uv2 = vec2(uv.x + uv.y);\n \n for(int i = 0; i < 5; i++) {\n uv2 += sin(max(uv.x, uv.y)) + uv;\n uv += 0.5 * vec2(\n cos(5.1123314 + 0.353 * uv2.y + speed * 0.131121),\n sin(uv2.x - 0.113 * speed)\n );\n uv -= cos(uv.x + uv.y) - sin(uv.x * 0.711 - uv.y);\n }\n \n float contrast_mod = (0.25 * uContrast + 0.5 * uSpinAmount + 1.2);\n float paint_res = min(2.0, max(0.0, length(uv) * 0.035 * contrast_mod));\n float c1p = max(0.0, 1.0 - contrast_mod * abs(1.0 - paint_res));\n float c2p = max(0.0, 1.0 - contrast_mod * abs(paint_res));\n float c3p = 1.0 - min(1.0, c1p + c2p);\n float light = (uLighting - 0.2) * max(c1p * 5.0 - 4.0, 0.0) + uLighting * max(c2p * 5.0 - 4.0, 0.0);\n \n return (0.3 / uContrast) * uColor1 + (1.0 - 0.3 / uContrast) * (uColor1 * c1p + uColor2 * c2p + vec4(c3p * uColor3.rgb, c3p * uColor1.a)) + light;\n}\n\nvoid main() {\n vec2 uv = vUv * iResolution.xy;\n gl_FragColor = effect(iResolution.xy, uv);\n}\n`;\n\nexport default function Balatro({\n spinRotation = -2.0,\n spinSpeed = 7.0,\n offset = [0.0, 0.0],\n color1 = '#DE443B',\n color2 = '#006BB4',\n color3 = '#162325',\n contrast = 3.5,\n lighting = 0.4,\n spinAmount = 0.25,\n pixelFilter = 745.0,\n spinEase = 1.0,\n isRotate = false,\n mouseInteraction = true\n}: BalatroProps) {\n const containerRef = useRef(null);\n\n useEffect(() => {\n if (!containerRef.current) return;\n const container = containerRef.current;\n const renderer = new Renderer();\n const gl = renderer.gl;\n gl.clearColor(0, 0, 0, 1);\n\n let program: Program;\n\n function resize() {\n renderer.setSize(container.offsetWidth, container.offsetHeight);\n if (program) {\n program.uniforms.iResolution.value = [gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height];\n }\n }\n window.addEventListener('resize', resize);\n resize();\n\n const geometry = new Triangle(gl);\n program = new Program(gl, {\n vertex: vertexShader,\n fragment: fragmentShader,\n uniforms: {\n iTime: { value: 0 },\n iResolution: {\n value: [gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height]\n },\n uSpinRotation: { value: spinRotation },\n uSpinSpeed: { value: spinSpeed },\n uOffset: { value: offset },\n uColor1: { value: hexToVec4(color1) },\n uColor2: { value: hexToVec4(color2) },\n uColor3: { value: hexToVec4(color3) },\n uContrast: { value: contrast },\n uLighting: { value: lighting },\n uSpinAmount: { value: spinAmount },\n uPixelFilter: { value: pixelFilter },\n uSpinEase: { value: spinEase },\n uIsRotate: { value: isRotate },\n uMouse: { value: [0.5, 0.5] }\n }\n });\n\n const mesh = new Mesh(gl, { geometry, program });\n let animationFrameId: number;\n\n function update(time: number) {\n animationFrameId = requestAnimationFrame(update);\n program.uniforms.iTime.value = time * 0.001;\n renderer.render({ scene: mesh });\n }\n animationFrameId = requestAnimationFrame(update);\n container.appendChild(gl.canvas);\n\n function handleMouseMove(e: MouseEvent) {\n if (!mouseInteraction) return;\n const rect = container.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n const y = 1.0 - (e.clientY - rect.top) / rect.height;\n program.uniforms.uMouse.value = [x, y];\n }\n container.addEventListener('mousemove', handleMouseMove);\n\n return () => {\n cancelAnimationFrame(animationFrameId);\n window.removeEventListener('resize', resize);\n container.removeEventListener('mousemove', handleMouseMove);\n container.removeChild(gl.canvas);\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n };\n }, [\n spinRotation,\n spinSpeed,\n offset,\n color1,\n color2,\n color3,\n contrast,\n lighting,\n spinAmount,\n pixelFilter,\n spinEase,\n isRotate,\n mouseInteraction\n ]);\n\n return
;\n}\n" } ], "registryDependencies": [], "dependencies": [ "ogl@^1.0.11" ] } ================================================ FILE: public/r/Balatro-TS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Balatro-TS-TW", "title": "Balatro", "description": "The balatro shader, fully customizalbe and interactive.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Balatro/Balatro.tsx", "content": "import { Renderer, Program, Mesh, Triangle } from 'ogl';\nimport { useEffect, useRef } from 'react';\n\ninterface BalatroProps {\n spinRotation?: number;\n spinSpeed?: number;\n offset?: [number, number];\n color1?: string;\n color2?: string;\n color3?: string;\n contrast?: number;\n lighting?: number;\n spinAmount?: number;\n pixelFilter?: number;\n spinEase?: number;\n isRotate?: boolean;\n mouseInteraction?: boolean;\n}\n\nfunction hexToVec4(hex: string): [number, number, number, number] {\n let hexStr = hex.replace('#', '');\n let r = 0,\n g = 0,\n b = 0,\n a = 1;\n if (hexStr.length === 6) {\n r = parseInt(hexStr.slice(0, 2), 16) / 255;\n g = parseInt(hexStr.slice(2, 4), 16) / 255;\n b = parseInt(hexStr.slice(4, 6), 16) / 255;\n } else if (hexStr.length === 8) {\n r = parseInt(hexStr.slice(0, 2), 16) / 255;\n g = parseInt(hexStr.slice(2, 4), 16) / 255;\n b = parseInt(hexStr.slice(4, 6), 16) / 255;\n a = parseInt(hexStr.slice(6, 8), 16) / 255;\n }\n return [r, g, b, a];\n}\n\nconst vertexShader = `\nattribute vec2 uv;\nattribute vec2 position;\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position, 0, 1);\n}\n`;\n\nconst fragmentShader = `\nprecision highp float;\n\n#define PI 3.14159265359\n\nuniform float iTime;\nuniform vec3 iResolution;\nuniform float uSpinRotation;\nuniform float uSpinSpeed;\nuniform vec2 uOffset;\nuniform vec4 uColor1;\nuniform vec4 uColor2;\nuniform vec4 uColor3;\nuniform float uContrast;\nuniform float uLighting;\nuniform float uSpinAmount;\nuniform float uPixelFilter;\nuniform float uSpinEase;\nuniform bool uIsRotate;\nuniform vec2 uMouse;\n\nvarying vec2 vUv;\n\nvec4 effect(vec2 screenSize, vec2 screen_coords) {\n float pixel_size = length(screenSize.xy) / uPixelFilter;\n vec2 uv = (floor(screen_coords.xy * (1.0 / pixel_size)) * pixel_size - 0.5 * screenSize.xy) / length(screenSize.xy) - uOffset;\n float uv_len = length(uv);\n \n float speed = (uSpinRotation * uSpinEase * 0.2);\n if(uIsRotate){\n speed = iTime * speed;\n }\n speed += 302.2;\n \n float mouseInfluence = (uMouse.x * 2.0 - 1.0);\n speed += mouseInfluence * 0.1;\n \n float new_pixel_angle = atan(uv.y, uv.x) + speed - uSpinEase * 20.0 * (uSpinAmount * uv_len + (1.0 - uSpinAmount));\n vec2 mid = (screenSize.xy / length(screenSize.xy)) / 2.0;\n uv = (vec2(uv_len * cos(new_pixel_angle) + mid.x, uv_len * sin(new_pixel_angle) + mid.y) - mid);\n \n uv *= 30.0;\n float baseSpeed = iTime * uSpinSpeed;\n speed = baseSpeed + mouseInfluence * 2.0;\n \n vec2 uv2 = vec2(uv.x + uv.y);\n \n for(int i = 0; i < 5; i++) {\n uv2 += sin(max(uv.x, uv.y)) + uv;\n uv += 0.5 * vec2(\n cos(5.1123314 + 0.353 * uv2.y + speed * 0.131121),\n sin(uv2.x - 0.113 * speed)\n );\n uv -= cos(uv.x + uv.y) - sin(uv.x * 0.711 - uv.y);\n }\n \n float contrast_mod = (0.25 * uContrast + 0.5 * uSpinAmount + 1.2);\n float paint_res = min(2.0, max(0.0, length(uv) * 0.035 * contrast_mod));\n float c1p = max(0.0, 1.0 - contrast_mod * abs(1.0 - paint_res));\n float c2p = max(0.0, 1.0 - contrast_mod * abs(paint_res));\n float c3p = 1.0 - min(1.0, c1p + c2p);\n float light = (uLighting - 0.2) * max(c1p * 5.0 - 4.0, 0.0) + uLighting * max(c2p * 5.0 - 4.0, 0.0);\n \n return (0.3 / uContrast) * uColor1 + (1.0 - 0.3 / uContrast) * (uColor1 * c1p + uColor2 * c2p + vec4(c3p * uColor3.rgb, c3p * uColor1.a)) + light;\n}\n\nvoid main() {\n vec2 uv = vUv * iResolution.xy;\n gl_FragColor = effect(iResolution.xy, uv);\n}\n`;\n\nexport default function Balatro({\n spinRotation = -2.0,\n spinSpeed = 7.0,\n offset = [0.0, 0.0],\n color1 = '#DE443B',\n color2 = '#006BB4',\n color3 = '#162325',\n contrast = 3.5,\n lighting = 0.4,\n spinAmount = 0.25,\n pixelFilter = 745.0,\n spinEase = 1.0,\n isRotate = false,\n mouseInteraction = true\n}: BalatroProps) {\n const containerRef = useRef(null);\n\n useEffect(() => {\n if (!containerRef.current) return;\n const container = containerRef.current;\n const renderer = new Renderer();\n const gl = renderer.gl;\n gl.clearColor(0, 0, 0, 1);\n\n let program: Program;\n\n function resize() {\n renderer.setSize(container.offsetWidth, container.offsetHeight);\n if (program) {\n program.uniforms.iResolution.value = [gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height];\n }\n }\n window.addEventListener('resize', resize);\n resize();\n\n const geometry = new Triangle(gl);\n program = new Program(gl, {\n vertex: vertexShader,\n fragment: fragmentShader,\n uniforms: {\n iTime: { value: 0 },\n iResolution: {\n value: [gl.canvas.width, gl.canvas.height, gl.canvas.width / gl.canvas.height]\n },\n uSpinRotation: { value: spinRotation },\n uSpinSpeed: { value: spinSpeed },\n uOffset: { value: offset },\n uColor1: { value: hexToVec4(color1) },\n uColor2: { value: hexToVec4(color2) },\n uColor3: { value: hexToVec4(color3) },\n uContrast: { value: contrast },\n uLighting: { value: lighting },\n uSpinAmount: { value: spinAmount },\n uPixelFilter: { value: pixelFilter },\n uSpinEase: { value: spinEase },\n uIsRotate: { value: isRotate },\n uMouse: { value: [0.5, 0.5] }\n }\n });\n\n const mesh = new Mesh(gl, { geometry, program });\n let animationFrameId: number;\n\n function update(time: number) {\n animationFrameId = requestAnimationFrame(update);\n program.uniforms.iTime.value = time * 0.001;\n renderer.render({ scene: mesh });\n }\n animationFrameId = requestAnimationFrame(update);\n container.appendChild(gl.canvas);\n\n function handleMouseMove(e: MouseEvent) {\n if (!mouseInteraction) return;\n const rect = container.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n const y = 1.0 - (e.clientY - rect.top) / rect.height;\n program.uniforms.uMouse.value = [x, y];\n }\n container.addEventListener('mousemove', handleMouseMove);\n\n return () => {\n cancelAnimationFrame(animationFrameId);\n window.removeEventListener('resize', resize);\n container.removeEventListener('mousemove', handleMouseMove);\n container.removeChild(gl.canvas);\n gl.getExtension('WEBGL_lose_context')?.loseContext();\n };\n }, [\n spinRotation,\n spinSpeed,\n offset,\n color1,\n color2,\n color3,\n contrast,\n lighting,\n spinAmount,\n pixelFilter,\n spinEase,\n isRotate,\n mouseInteraction\n ]);\n\n return
;\n}\n" } ], "registryDependencies": [], "dependencies": [ "ogl@^1.0.11" ] } ================================================ FILE: public/r/Ballpit-JS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Ballpit-JS-CSS", "title": "Ballpit", "description": "Physics ball pit simulation with bouncing colorful spheres.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Ballpit/Ballpit.jsx", "content": "import { useEffect, useRef } from 'react';\nimport {\n Vector3 as a,\n MeshPhysicalMaterial as c,\n InstancedMesh as d,\n Clock as e,\n AmbientLight as f,\n SphereGeometry as g,\n ShaderChunk as h,\n Scene as i,\n Color as l,\n Object3D as m,\n SRGBColorSpace as n,\n MathUtils as o,\n PMREMGenerator as p,\n Vector2 as r,\n WebGLRenderer as s,\n PerspectiveCamera as t,\n PointLight as u,\n ACESFilmicToneMapping as v,\n Plane as w,\n Raycaster as y\n} from 'three';\nimport { RoomEnvironment as z } from 'three/examples/jsm/environments/RoomEnvironment.js';\n\nclass x {\n #e;\n canvas;\n camera;\n cameraMinAspect;\n cameraMaxAspect;\n cameraFov;\n maxPixelRatio;\n minPixelRatio;\n scene;\n renderer;\n #t;\n size = { width: 0, height: 0, wWidth: 0, wHeight: 0, ratio: 0, pixelRatio: 0 };\n render = this.#i;\n onBeforeRender = () => {};\n onAfterRender = () => {};\n onAfterResize = () => {};\n #s = false;\n #n = false;\n isDisposed = false;\n #o;\n #r;\n #a;\n #c = new e();\n #h = { elapsed: 0, delta: 0 };\n #l;\n constructor(e) {\n this.#e = { ...e };\n this.#m();\n this.#d();\n this.#p();\n this.resize();\n this.#g();\n }\n #m() {\n this.camera = new t();\n this.cameraFov = this.camera.fov;\n }\n #d() {\n this.scene = new i();\n }\n #p() {\n if (this.#e.canvas) {\n this.canvas = this.#e.canvas;\n } else if (this.#e.id) {\n this.canvas = document.getElementById(this.#e.id);\n } else {\n console.error('Three: Missing canvas or id parameter');\n }\n this.canvas.style.display = 'block';\n const e = {\n canvas: this.canvas,\n powerPreference: 'high-performance',\n ...(this.#e.rendererOptions ?? {})\n };\n this.renderer = new s(e);\n this.renderer.outputColorSpace = n;\n }\n #g() {\n if (!(this.#e.size instanceof Object)) {\n window.addEventListener('resize', this.#f.bind(this));\n if (this.#e.size === 'parent' && this.canvas.parentNode) {\n this.#r = new ResizeObserver(this.#f.bind(this));\n this.#r.observe(this.canvas.parentNode);\n }\n }\n this.#o = new IntersectionObserver(this.#u.bind(this), {\n root: null,\n rootMargin: '0px',\n threshold: 0\n });\n this.#o.observe(this.canvas);\n document.addEventListener('visibilitychange', this.#v.bind(this));\n }\n #y() {\n window.removeEventListener('resize', this.#f.bind(this));\n this.#r?.disconnect();\n this.#o?.disconnect();\n document.removeEventListener('visibilitychange', this.#v.bind(this));\n }\n #u(e) {\n this.#s = e[0].isIntersecting;\n this.#s ? this.#w() : this.#z();\n }\n #v() {\n if (this.#s) {\n document.hidden ? this.#z() : this.#w();\n }\n }\n #f() {\n if (this.#a) clearTimeout(this.#a);\n this.#a = setTimeout(this.resize.bind(this), 100);\n }\n resize() {\n let e, t;\n if (this.#e.size instanceof Object) {\n e = this.#e.size.width;\n t = this.#e.size.height;\n } else if (this.#e.size === 'parent' && this.canvas.parentNode) {\n e = this.canvas.parentNode.offsetWidth;\n t = this.canvas.parentNode.offsetHeight;\n } else {\n e = window.innerWidth;\n t = window.innerHeight;\n }\n this.size.width = e;\n this.size.height = t;\n this.size.ratio = e / t;\n this.#x();\n this.#b();\n this.onAfterResize(this.size);\n }\n #x() {\n this.camera.aspect = this.size.width / this.size.height;\n if (this.camera.isPerspectiveCamera && this.cameraFov) {\n if (this.cameraMinAspect && this.camera.aspect < this.cameraMinAspect) {\n this.#A(this.cameraMinAspect);\n } else if (this.cameraMaxAspect && this.camera.aspect > this.cameraMaxAspect) {\n this.#A(this.cameraMaxAspect);\n } else {\n this.camera.fov = this.cameraFov;\n }\n }\n this.camera.updateProjectionMatrix();\n this.updateWorldSize();\n }\n #A(e) {\n const t = Math.tan(o.degToRad(this.cameraFov / 2)) / (this.camera.aspect / e);\n this.camera.fov = 2 * o.radToDeg(Math.atan(t));\n }\n updateWorldSize() {\n if (this.camera.isPerspectiveCamera) {\n const e = (this.camera.fov * Math.PI) / 180;\n this.size.wHeight = 2 * Math.tan(e / 2) * this.camera.position.length();\n this.size.wWidth = this.size.wHeight * this.camera.aspect;\n } else if (this.camera.isOrthographicCamera) {\n this.size.wHeight = this.camera.top - this.camera.bottom;\n this.size.wWidth = this.camera.right - this.camera.left;\n }\n }\n #b() {\n this.renderer.setSize(this.size.width, this.size.height);\n this.#t?.setSize(this.size.width, this.size.height);\n let e = window.devicePixelRatio;\n if (this.maxPixelRatio && e > this.maxPixelRatio) {\n e = this.maxPixelRatio;\n } else if (this.minPixelRatio && e < this.minPixelRatio) {\n e = this.minPixelRatio;\n }\n this.renderer.setPixelRatio(e);\n this.size.pixelRatio = e;\n }\n get postprocessing() {\n return this.#t;\n }\n set postprocessing(e) {\n this.#t = e;\n this.render = e.render.bind(e);\n }\n #w() {\n if (this.#n) return;\n const animate = () => {\n this.#l = requestAnimationFrame(animate);\n this.#h.delta = this.#c.getDelta();\n this.#h.elapsed += this.#h.delta;\n this.onBeforeRender(this.#h);\n this.render();\n this.onAfterRender(this.#h);\n };\n this.#n = true;\n this.#c.start();\n animate();\n }\n #z() {\n if (this.#n) {\n cancelAnimationFrame(this.#l);\n this.#n = false;\n this.#c.stop();\n }\n }\n #i() {\n this.renderer.render(this.scene, this.camera);\n }\n clear() {\n this.scene.traverse(e => {\n if (e.isMesh && typeof e.material === 'object' && e.material !== null) {\n Object.keys(e.material).forEach(t => {\n const i = e.material[t];\n if (i !== null && typeof i === 'object' && typeof i.dispose === 'function') {\n i.dispose();\n }\n });\n e.material.dispose();\n e.geometry.dispose();\n }\n });\n this.scene.clear();\n }\n dispose() {\n this.#y();\n this.#z();\n this.clear();\n this.#t?.dispose();\n this.renderer.dispose();\n this.renderer.forceContextLoss();\n this.isDisposed = true;\n }\n}\n\nconst b = new Map(),\n A = new r();\nlet R = false;\nfunction S(e) {\n const t = {\n position: new r(),\n nPosition: new r(),\n hover: false,\n touching: false,\n onEnter() {},\n onMove() {},\n onClick() {},\n onLeave() {},\n ...e\n };\n (function (e, t) {\n if (!b.has(e)) {\n b.set(e, t);\n if (!R) {\n document.body.addEventListener('pointermove', M);\n document.body.addEventListener('pointerleave', L);\n document.body.addEventListener('click', C);\n\n document.body.addEventListener('touchstart', TouchStart, { passive: false });\n document.body.addEventListener('touchmove', TouchMove, { passive: false });\n document.body.addEventListener('touchend', TouchEnd, { passive: false });\n document.body.addEventListener('touchcancel', TouchEnd, { passive: false });\n\n R = true;\n }\n }\n })(e.domElement, t);\n t.dispose = () => {\n const t = e.domElement;\n b.delete(t);\n if (b.size === 0) {\n document.body.removeEventListener('pointermove', M);\n document.body.removeEventListener('pointerleave', L);\n document.body.removeEventListener('click', C);\n\n document.body.removeEventListener('touchstart', TouchStart);\n document.body.removeEventListener('touchmove', TouchMove);\n document.body.removeEventListener('touchend', TouchEnd);\n document.body.removeEventListener('touchcancel', TouchEnd);\n\n R = false;\n }\n };\n return t;\n}\n\nfunction M(e) {\n A.x = e.clientX;\n A.y = e.clientY;\n processInteraction();\n}\n\nfunction processInteraction() {\n for (const [elem, t] of b) {\n const i = elem.getBoundingClientRect();\n if (D(i)) {\n P(t, i);\n if (!t.hover) {\n t.hover = true;\n t.onEnter(t);\n }\n t.onMove(t);\n } else if (t.hover && !t.touching) {\n t.hover = false;\n t.onLeave(t);\n }\n }\n}\n\nfunction C(e) {\n A.x = e.clientX;\n A.y = e.clientY;\n for (const [elem, t] of b) {\n const i = elem.getBoundingClientRect();\n P(t, i);\n if (D(i)) t.onClick(t);\n }\n}\n\nfunction L() {\n for (const t of b.values()) {\n if (t.hover) {\n t.hover = false;\n t.onLeave(t);\n }\n }\n}\n\nfunction TouchStart(e) {\n if (e.touches.length > 0) {\n e.preventDefault();\n A.x = e.touches[0].clientX;\n A.y = e.touches[0].clientY;\n\n for (const [elem, t] of b) {\n const rect = elem.getBoundingClientRect();\n if (D(rect)) {\n t.touching = true;\n P(t, rect);\n if (!t.hover) {\n t.hover = true;\n t.onEnter(t);\n }\n t.onMove(t);\n }\n }\n }\n}\n\nfunction TouchMove(e) {\n if (e.touches.length > 0) {\n e.preventDefault();\n A.x = e.touches[0].clientX;\n A.y = e.touches[0].clientY;\n\n for (const [elem, t] of b) {\n const rect = elem.getBoundingClientRect();\n P(t, rect);\n\n if (D(rect)) {\n if (!t.hover) {\n t.hover = true;\n t.touching = true;\n t.onEnter(t);\n }\n t.onMove(t);\n } else if (t.hover && t.touching) {\n t.onMove(t);\n }\n }\n }\n}\n\nfunction TouchEnd() {\n for (const [, t] of b) {\n if (t.touching) {\n t.touching = false;\n if (t.hover) {\n t.hover = false;\n t.onLeave(t);\n }\n }\n }\n}\n\nfunction P(e, t) {\n const { position: i, nPosition: s } = e;\n i.x = A.x - t.left;\n i.y = A.y - t.top;\n s.x = (i.x / t.width) * 2 - 1;\n s.y = (-i.y / t.height) * 2 + 1;\n}\nfunction D(e) {\n const { x: t, y: i } = A;\n const { left: s, top: n, width: o, height: r } = e;\n return t >= s && t <= s + o && i >= n && i <= n + r;\n}\n\nconst { randFloat: k, randFloatSpread: E } = o;\nconst F = new a();\nconst I = new a();\nconst O = new a();\nconst V = new a();\nconst B = new a();\nconst N = new a();\nconst _ = new a();\nconst j = new a();\nconst H = new a();\nconst T = new a();\n\nclass W {\n constructor(e) {\n this.config = e;\n this.positionData = new Float32Array(3 * e.count).fill(0);\n this.velocityData = new Float32Array(3 * e.count).fill(0);\n this.sizeData = new Float32Array(e.count).fill(1);\n this.center = new a();\n this.#R();\n this.setSizes();\n }\n #R() {\n const { config: e, positionData: t } = this;\n this.center.toArray(t, 0);\n for (let i = 1; i < e.count; i++) {\n const s = 3 * i;\n t[s] = E(2 * e.maxX);\n t[s + 1] = E(2 * e.maxY);\n t[s + 2] = E(2 * e.maxZ);\n }\n }\n setSizes() {\n const { config: e, sizeData: t } = this;\n t[0] = e.size0;\n for (let i = 1; i < e.count; i++) {\n t[i] = k(e.minSize, e.maxSize);\n }\n }\n update(e) {\n const { config: t, center: i, positionData: s, sizeData: n, velocityData: o } = this;\n let r = 0;\n if (t.controlSphere0) {\n r = 1;\n F.fromArray(s, 0);\n F.lerp(i, 0.1).toArray(s, 0);\n V.set(0, 0, 0).toArray(o, 0);\n }\n for (let idx = r; idx < t.count; idx++) {\n const base = 3 * idx;\n I.fromArray(s, base);\n B.fromArray(o, base);\n B.y -= e.delta * t.gravity * n[idx];\n B.multiplyScalar(t.friction);\n B.clampLength(0, t.maxVelocity);\n I.add(B);\n I.toArray(s, base);\n B.toArray(o, base);\n }\n for (let idx = r; idx < t.count; idx++) {\n const base = 3 * idx;\n I.fromArray(s, base);\n B.fromArray(o, base);\n const radius = n[idx];\n for (let jdx = idx + 1; jdx < t.count; jdx++) {\n const otherBase = 3 * jdx;\n O.fromArray(s, otherBase);\n N.fromArray(o, otherBase);\n const otherRadius = n[jdx];\n _.copy(O).sub(I);\n const dist = _.length();\n const sumRadius = radius + otherRadius;\n if (dist < sumRadius) {\n const overlap = sumRadius - dist;\n j.copy(_)\n .normalize()\n .multiplyScalar(0.5 * overlap);\n H.copy(j).multiplyScalar(Math.max(B.length(), 1));\n T.copy(j).multiplyScalar(Math.max(N.length(), 1));\n I.sub(j);\n B.sub(H);\n I.toArray(s, base);\n B.toArray(o, base);\n O.add(j);\n N.add(T);\n O.toArray(s, otherBase);\n N.toArray(o, otherBase);\n }\n }\n if (t.controlSphere0) {\n _.copy(F).sub(I);\n const dist = _.length();\n const sumRadius0 = radius + n[0];\n if (dist < sumRadius0) {\n const diff = sumRadius0 - dist;\n j.copy(_.normalize()).multiplyScalar(diff);\n H.copy(j).multiplyScalar(Math.max(B.length(), 2));\n I.sub(j);\n B.sub(H);\n }\n }\n if (Math.abs(I.x) + radius > t.maxX) {\n I.x = Math.sign(I.x) * (t.maxX - radius);\n B.x = -B.x * t.wallBounce;\n }\n if (t.gravity === 0) {\n if (Math.abs(I.y) + radius > t.maxY) {\n I.y = Math.sign(I.y) * (t.maxY - radius);\n B.y = -B.y * t.wallBounce;\n }\n } else if (I.y - radius < -t.maxY) {\n I.y = -t.maxY + radius;\n B.y = -B.y * t.wallBounce;\n }\n const maxBoundary = Math.max(t.maxZ, t.maxSize);\n if (Math.abs(I.z) + radius > maxBoundary) {\n I.z = Math.sign(I.z) * (t.maxZ - radius);\n B.z = -B.z * t.wallBounce;\n }\n I.toArray(s, base);\n B.toArray(o, base);\n }\n }\n}\n\nclass Y extends c {\n constructor(e) {\n super(e);\n this.uniforms = {\n thicknessDistortion: { value: 0.1 },\n thicknessAmbient: { value: 0 },\n thicknessAttenuation: { value: 0.1 },\n thicknessPower: { value: 2 },\n thicknessScale: { value: 10 }\n };\n this.defines.USE_UV = '';\n this.onBeforeCompile = e => {\n Object.assign(e.uniforms, this.uniforms);\n e.fragmentShader =\n '\\n uniform float thicknessPower;\\n uniform float thicknessScale;\\n uniform float thicknessDistortion;\\n uniform float thicknessAmbient;\\n uniform float thicknessAttenuation;\\n ' +\n e.fragmentShader;\n e.fragmentShader = e.fragmentShader.replace(\n 'void main() {',\n '\\n void RE_Direct_Scattering(const in IncidentLight directLight, const in vec2 uv, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, inout ReflectedLight reflectedLight) {\\n vec3 scatteringHalf = normalize(directLight.direction + (geometryNormal * thicknessDistortion));\\n float scatteringDot = pow(saturate(dot(geometryViewDir, -scatteringHalf)), thicknessPower) * thicknessScale;\\n #ifdef USE_COLOR\\n vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * vColor;\\n #else\\n vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * diffuse;\\n #endif\\n reflectedLight.directDiffuse += scatteringIllu * thicknessAttenuation * directLight.color;\\n }\\n\\n void main() {\\n '\n );\n const t = h.lights_fragment_begin.replaceAll(\n 'RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );',\n '\\n RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\\n RE_Direct_Scattering(directLight, vUv, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, reflectedLight);\\n '\n );\n e.fragmentShader = e.fragmentShader.replace('#include ', t);\n if (this.onBeforeCompile2) this.onBeforeCompile2(e);\n };\n }\n}\n\nconst X = {\n count: 200,\n colors: [0, 0, 0],\n ambientColor: 16777215,\n ambientIntensity: 1,\n lightIntensity: 200,\n materialParams: {\n metalness: 0.5,\n roughness: 0.5,\n clearcoat: 1,\n clearcoatRoughness: 0.15\n },\n minSize: 0.5,\n maxSize: 1,\n size0: 1,\n gravity: 0.5,\n friction: 0.9975,\n wallBounce: 0.95,\n maxVelocity: 0.15,\n maxX: 5,\n maxY: 5,\n maxZ: 2,\n controlSphere0: false,\n followCursor: true\n};\n\nconst U = new m();\n\nclass Z extends d {\n constructor(e, t = {}) {\n const i = { ...X, ...t };\n const s = new z();\n const n = new p(e, 0.04).fromScene(s).texture;\n const o = new g();\n const r = new Y({ envMap: n, ...i.materialParams });\n r.envMapRotation.x = -Math.PI / 2;\n super(o, r, i.count);\n this.config = i;\n this.physics = new W(i);\n this.#S();\n this.setColors(i.colors);\n }\n #S() {\n this.ambientLight = new f(this.config.ambientColor, this.config.ambientIntensity);\n this.add(this.ambientLight);\n this.light = new u(this.config.colors[0], this.config.lightIntensity);\n this.add(this.light);\n }\n setColors(e) {\n if (Array.isArray(e) && e.length > 1) {\n const t = (function (e) {\n let t, i;\n function setColors(e) {\n t = e;\n i = [];\n t.forEach(col => {\n i.push(new l(col));\n });\n }\n setColors(e);\n return {\n setColors,\n getColorAt: function (ratio, out = new l()) {\n const scaled = Math.max(0, Math.min(1, ratio)) * (t.length - 1);\n const idx = Math.floor(scaled);\n const start = i[idx];\n if (idx >= t.length - 1) return start.clone();\n const alpha = scaled - idx;\n const end = i[idx + 1];\n out.r = start.r + alpha * (end.r - start.r);\n out.g = start.g + alpha * (end.g - start.g);\n out.b = start.b + alpha * (end.b - start.b);\n return out;\n }\n };\n })(e);\n for (let idx = 0; idx < this.count; idx++) {\n this.setColorAt(idx, t.getColorAt(idx / this.count));\n if (idx === 0) {\n this.light.color.copy(t.getColorAt(idx / this.count));\n }\n }\n this.instanceColor.needsUpdate = true;\n }\n }\n update(e) {\n this.physics.update(e);\n for (let idx = 0; idx < this.count; idx++) {\n U.position.fromArray(this.physics.positionData, 3 * idx);\n if (idx === 0 && this.config.followCursor === false) {\n U.scale.setScalar(0);\n } else {\n U.scale.setScalar(this.physics.sizeData[idx]);\n }\n U.updateMatrix();\n this.setMatrixAt(idx, U.matrix);\n if (idx === 0) this.light.position.copy(U.position);\n }\n this.instanceMatrix.needsUpdate = true;\n }\n}\n\nfunction createBallpit(e, t = {}) {\n const i = new x({\n canvas: e,\n size: 'parent',\n rendererOptions: { antialias: true, alpha: true }\n });\n let s;\n i.renderer.toneMapping = v;\n i.camera.position.set(0, 0, 20);\n i.camera.lookAt(0, 0, 0);\n i.cameraMaxAspect = 1.5;\n i.resize();\n initialize(t);\n const n = new y();\n const o = new w(new a(0, 0, 1), 0);\n const r = new a();\n let c = false;\n\n e.style.touchAction = 'none';\n e.style.userSelect = 'none';\n e.style.webkitUserSelect = 'none';\n\n const h = S({\n domElement: e,\n onMove() {\n n.setFromCamera(h.nPosition, i.camera);\n i.camera.getWorldDirection(o.normal);\n n.ray.intersectPlane(o, r);\n s.physics.center.copy(r);\n s.config.controlSphere0 = true;\n },\n onLeave() {\n s.config.controlSphere0 = false;\n }\n });\n function initialize(e) {\n if (s) {\n i.clear();\n i.scene.remove(s);\n }\n s = new Z(i.renderer, e);\n i.scene.add(s);\n }\n i.onBeforeRender = e => {\n if (!c) s.update(e);\n };\n i.onAfterResize = e => {\n s.config.maxX = e.wWidth / 2;\n s.config.maxY = e.wHeight / 2;\n };\n return {\n three: i,\n get spheres() {\n return s;\n },\n setCount(e) {\n initialize({ ...s.config, count: e });\n },\n togglePause() {\n c = !c;\n },\n dispose() {\n h.dispose();\n i.dispose();\n }\n };\n}\n\nconst Ballpit = ({ className = '', followCursor = true, ...props }) => {\n const canvasRef = useRef(null);\n const spheresInstanceRef = useRef(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n spheresInstanceRef.current = createBallpit(canvas, { followCursor, ...props });\n\n return () => {\n if (spheresInstanceRef.current) {\n spheresInstanceRef.current.dispose();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return ;\n};\n\nexport default Ballpit;\n" } ], "registryDependencies": [], "dependencies": [ "three@^0.167.1" ] } ================================================ FILE: public/r/Ballpit-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Ballpit-JS-TW", "title": "Ballpit", "description": "Physics ball pit simulation with bouncing colorful spheres.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Ballpit/Ballpit.jsx", "content": "import { useEffect, useRef } from 'react';\nimport {\n Vector3 as a,\n MeshPhysicalMaterial as c,\n InstancedMesh as d,\n Clock as e,\n AmbientLight as f,\n SphereGeometry as g,\n ShaderChunk as h,\n Scene as i,\n Color as l,\n Object3D as m,\n SRGBColorSpace as n,\n MathUtils as o,\n PMREMGenerator as p,\n Vector2 as r,\n WebGLRenderer as s,\n PerspectiveCamera as t,\n PointLight as u,\n ACESFilmicToneMapping as v,\n Plane as w,\n Raycaster as y\n} from 'three';\nimport { RoomEnvironment as z } from 'three/examples/jsm/environments/RoomEnvironment.js';\n\nclass x {\n #e;\n canvas;\n camera;\n cameraMinAspect;\n cameraMaxAspect;\n cameraFov;\n maxPixelRatio;\n minPixelRatio;\n scene;\n renderer;\n #t;\n size = { width: 0, height: 0, wWidth: 0, wHeight: 0, ratio: 0, pixelRatio: 0 };\n render = this.#i;\n onBeforeRender = () => {};\n onAfterRender = () => {};\n onAfterResize = () => {};\n #s = false;\n #n = false;\n isDisposed = false;\n #o;\n #r;\n #a;\n #c = new e();\n #h = { elapsed: 0, delta: 0 };\n #l;\n constructor(e) {\n this.#e = { ...e };\n this.#m();\n this.#d();\n this.#p();\n this.resize();\n this.#g();\n }\n #m() {\n this.camera = new t();\n this.cameraFov = this.camera.fov;\n }\n #d() {\n this.scene = new i();\n }\n #p() {\n if (this.#e.canvas) {\n this.canvas = this.#e.canvas;\n } else if (this.#e.id) {\n this.canvas = document.getElementById(this.#e.id);\n } else {\n console.error('Three: Missing canvas or id parameter');\n }\n this.canvas.style.display = 'block';\n const e = {\n canvas: this.canvas,\n powerPreference: 'high-performance',\n ...(this.#e.rendererOptions ?? {})\n };\n this.renderer = new s(e);\n this.renderer.outputColorSpace = n;\n }\n #g() {\n if (!(this.#e.size instanceof Object)) {\n window.addEventListener('resize', this.#f.bind(this));\n if (this.#e.size === 'parent' && this.canvas.parentNode) {\n this.#r = new ResizeObserver(this.#f.bind(this));\n this.#r.observe(this.canvas.parentNode);\n }\n }\n this.#o = new IntersectionObserver(this.#u.bind(this), {\n root: null,\n rootMargin: '0px',\n threshold: 0\n });\n this.#o.observe(this.canvas);\n document.addEventListener('visibilitychange', this.#v.bind(this));\n }\n #y() {\n window.removeEventListener('resize', this.#f.bind(this));\n this.#r?.disconnect();\n this.#o?.disconnect();\n document.removeEventListener('visibilitychange', this.#v.bind(this));\n }\n #u(e) {\n this.#s = e[0].isIntersecting;\n this.#s ? this.#w() : this.#z();\n }\n #v() {\n if (this.#s) {\n document.hidden ? this.#z() : this.#w();\n }\n }\n #f() {\n if (this.#a) clearTimeout(this.#a);\n this.#a = setTimeout(this.resize.bind(this), 100);\n }\n resize() {\n let e, t;\n if (this.#e.size instanceof Object) {\n e = this.#e.size.width;\n t = this.#e.size.height;\n } else if (this.#e.size === 'parent' && this.canvas.parentNode) {\n e = this.canvas.parentNode.offsetWidth;\n t = this.canvas.parentNode.offsetHeight;\n } else {\n e = window.innerWidth;\n t = window.innerHeight;\n }\n this.size.width = e;\n this.size.height = t;\n this.size.ratio = e / t;\n this.#x();\n this.#b();\n this.onAfterResize(this.size);\n }\n #x() {\n this.camera.aspect = this.size.width / this.size.height;\n if (this.camera.isPerspectiveCamera && this.cameraFov) {\n if (this.cameraMinAspect && this.camera.aspect < this.cameraMinAspect) {\n this.#A(this.cameraMinAspect);\n } else if (this.cameraMaxAspect && this.camera.aspect > this.cameraMaxAspect) {\n this.#A(this.cameraMaxAspect);\n } else {\n this.camera.fov = this.cameraFov;\n }\n }\n this.camera.updateProjectionMatrix();\n this.updateWorldSize();\n }\n #A(e) {\n const t = Math.tan(o.degToRad(this.cameraFov / 2)) / (this.camera.aspect / e);\n this.camera.fov = 2 * o.radToDeg(Math.atan(t));\n }\n updateWorldSize() {\n if (this.camera.isPerspectiveCamera) {\n const e = (this.camera.fov * Math.PI) / 180;\n this.size.wHeight = 2 * Math.tan(e / 2) * this.camera.position.length();\n this.size.wWidth = this.size.wHeight * this.camera.aspect;\n } else if (this.camera.isOrthographicCamera) {\n this.size.wHeight = this.camera.top - this.camera.bottom;\n this.size.wWidth = this.camera.right - this.camera.left;\n }\n }\n #b() {\n this.renderer.setSize(this.size.width, this.size.height);\n this.#t?.setSize(this.size.width, this.size.height);\n let e = window.devicePixelRatio;\n if (this.maxPixelRatio && e > this.maxPixelRatio) {\n e = this.maxPixelRatio;\n } else if (this.minPixelRatio && e < this.minPixelRatio) {\n e = this.minPixelRatio;\n }\n this.renderer.setPixelRatio(e);\n this.size.pixelRatio = e;\n }\n get postprocessing() {\n return this.#t;\n }\n set postprocessing(e) {\n this.#t = e;\n this.render = e.render.bind(e);\n }\n #w() {\n if (this.#n) return;\n const animate = () => {\n this.#l = requestAnimationFrame(animate);\n this.#h.delta = this.#c.getDelta();\n this.#h.elapsed += this.#h.delta;\n this.onBeforeRender(this.#h);\n this.render();\n this.onAfterRender(this.#h);\n };\n this.#n = true;\n this.#c.start();\n animate();\n }\n #z() {\n if (this.#n) {\n cancelAnimationFrame(this.#l);\n this.#n = false;\n this.#c.stop();\n }\n }\n #i() {\n this.renderer.render(this.scene, this.camera);\n }\n clear() {\n this.scene.traverse(e => {\n if (e.isMesh && typeof e.material === 'object' && e.material !== null) {\n Object.keys(e.material).forEach(t => {\n const i = e.material[t];\n if (i !== null && typeof i === 'object' && typeof i.dispose === 'function') {\n i.dispose();\n }\n });\n e.material.dispose();\n e.geometry.dispose();\n }\n });\n this.scene.clear();\n }\n dispose() {\n this.#y();\n this.#z();\n this.clear();\n this.#t?.dispose();\n this.renderer.dispose();\n this.renderer.forceContextLoss();\n this.isDisposed = true;\n }\n}\n\nconst b = new Map(),\n A = new r();\nlet R = false;\nfunction S(e) {\n const t = {\n position: new r(),\n nPosition: new r(),\n hover: false,\n touching: false,\n onEnter() {},\n onMove() {},\n onClick() {},\n onLeave() {},\n ...e\n };\n (function (e, t) {\n if (!b.has(e)) {\n b.set(e, t);\n if (!R) {\n document.body.addEventListener('pointermove', M);\n document.body.addEventListener('pointerleave', L);\n document.body.addEventListener('click', C);\n\n document.body.addEventListener('touchstart', TouchStart, { passive: false });\n document.body.addEventListener('touchmove', TouchMove, { passive: false });\n document.body.addEventListener('touchend', TouchEnd, { passive: false });\n document.body.addEventListener('touchcancel', TouchEnd, { passive: false });\n\n R = true;\n }\n }\n })(e.domElement, t);\n t.dispose = () => {\n const t = e.domElement;\n b.delete(t);\n if (b.size === 0) {\n document.body.removeEventListener('pointermove', M);\n document.body.removeEventListener('pointerleave', L);\n document.body.removeEventListener('click', C);\n\n document.body.removeEventListener('touchstart', TouchStart);\n document.body.removeEventListener('touchmove', TouchMove);\n document.body.removeEventListener('touchend', TouchEnd);\n document.body.removeEventListener('touchcancel', TouchEnd);\n\n R = false;\n }\n };\n return t;\n}\n\nfunction M(e) {\n A.x = e.clientX;\n A.y = e.clientY;\n processInteraction();\n}\n\nfunction processInteraction() {\n for (const [elem, t] of b) {\n const i = elem.getBoundingClientRect();\n if (D(i)) {\n P(t, i);\n if (!t.hover) {\n t.hover = true;\n t.onEnter(t);\n }\n t.onMove(t);\n } else if (t.hover && !t.touching) {\n t.hover = false;\n t.onLeave(t);\n }\n }\n}\n\nfunction C(e) {\n A.x = e.clientX;\n A.y = e.clientY;\n for (const [elem, t] of b) {\n const i = elem.getBoundingClientRect();\n P(t, i);\n if (D(i)) t.onClick(t);\n }\n}\n\nfunction L() {\n for (const t of b.values()) {\n if (t.hover) {\n t.hover = false;\n t.onLeave(t);\n }\n }\n}\n\nfunction TouchStart(e) {\n if (e.touches.length > 0) {\n e.preventDefault();\n A.x = e.touches[0].clientX;\n A.y = e.touches[0].clientY;\n\n for (const [elem, t] of b) {\n const rect = elem.getBoundingClientRect();\n if (D(rect)) {\n t.touching = true;\n P(t, rect);\n if (!t.hover) {\n t.hover = true;\n t.onEnter(t);\n }\n t.onMove(t);\n }\n }\n }\n}\n\nfunction TouchMove(e) {\n if (e.touches.length > 0) {\n e.preventDefault();\n A.x = e.touches[0].clientX;\n A.y = e.touches[0].clientY;\n\n for (const [elem, t] of b) {\n const rect = elem.getBoundingClientRect();\n P(t, rect);\n\n if (D(rect)) {\n if (!t.hover) {\n t.hover = true;\n t.touching = true;\n t.onEnter(t);\n }\n t.onMove(t);\n } else if (t.hover && t.touching) {\n t.onMove(t);\n }\n }\n }\n}\n\nfunction TouchEnd() {\n for (const [, t] of b) {\n if (t.touching) {\n t.touching = false;\n if (t.hover) {\n t.hover = false;\n t.onLeave(t);\n }\n }\n }\n}\n\nfunction P(e, t) {\n const { position: i, nPosition: s } = e;\n i.x = A.x - t.left;\n i.y = A.y - t.top;\n s.x = (i.x / t.width) * 2 - 1;\n s.y = (-i.y / t.height) * 2 + 1;\n}\nfunction D(e) {\n const { x: t, y: i } = A;\n const { left: s, top: n, width: o, height: r } = e;\n return t >= s && t <= s + o && i >= n && i <= n + r;\n}\n\nconst { randFloat: k, randFloatSpread: E } = o;\nconst F = new a();\nconst I = new a();\nconst O = new a();\nconst V = new a();\nconst B = new a();\nconst N = new a();\nconst _ = new a();\nconst j = new a();\nconst H = new a();\nconst T = new a();\n\nclass W {\n constructor(e) {\n this.config = e;\n this.positionData = new Float32Array(3 * e.count).fill(0);\n this.velocityData = new Float32Array(3 * e.count).fill(0);\n this.sizeData = new Float32Array(e.count).fill(1);\n this.center = new a();\n this.#R();\n this.setSizes();\n }\n #R() {\n const { config: e, positionData: t } = this;\n this.center.toArray(t, 0);\n for (let i = 1; i < e.count; i++) {\n const s = 3 * i;\n t[s] = E(2 * e.maxX);\n t[s + 1] = E(2 * e.maxY);\n t[s + 2] = E(2 * e.maxZ);\n }\n }\n setSizes() {\n const { config: e, sizeData: t } = this;\n t[0] = e.size0;\n for (let i = 1; i < e.count; i++) {\n t[i] = k(e.minSize, e.maxSize);\n }\n }\n update(e) {\n const { config: t, center: i, positionData: s, sizeData: n, velocityData: o } = this;\n let r = 0;\n if (t.controlSphere0) {\n r = 1;\n F.fromArray(s, 0);\n F.lerp(i, 0.1).toArray(s, 0);\n V.set(0, 0, 0).toArray(o, 0);\n }\n for (let idx = r; idx < t.count; idx++) {\n const base = 3 * idx;\n I.fromArray(s, base);\n B.fromArray(o, base);\n B.y -= e.delta * t.gravity * n[idx];\n B.multiplyScalar(t.friction);\n B.clampLength(0, t.maxVelocity);\n I.add(B);\n I.toArray(s, base);\n B.toArray(o, base);\n }\n for (let idx = r; idx < t.count; idx++) {\n const base = 3 * idx;\n I.fromArray(s, base);\n B.fromArray(o, base);\n const radius = n[idx];\n for (let jdx = idx + 1; jdx < t.count; jdx++) {\n const otherBase = 3 * jdx;\n O.fromArray(s, otherBase);\n N.fromArray(o, otherBase);\n const otherRadius = n[jdx];\n _.copy(O).sub(I);\n const dist = _.length();\n const sumRadius = radius + otherRadius;\n if (dist < sumRadius) {\n const overlap = sumRadius - dist;\n j.copy(_)\n .normalize()\n .multiplyScalar(0.5 * overlap);\n H.copy(j).multiplyScalar(Math.max(B.length(), 1));\n T.copy(j).multiplyScalar(Math.max(N.length(), 1));\n I.sub(j);\n B.sub(H);\n I.toArray(s, base);\n B.toArray(o, base);\n O.add(j);\n N.add(T);\n O.toArray(s, otherBase);\n N.toArray(o, otherBase);\n }\n }\n if (t.controlSphere0) {\n _.copy(F).sub(I);\n const dist = _.length();\n const sumRadius0 = radius + n[0];\n if (dist < sumRadius0) {\n const diff = sumRadius0 - dist;\n j.copy(_.normalize()).multiplyScalar(diff);\n H.copy(j).multiplyScalar(Math.max(B.length(), 2));\n I.sub(j);\n B.sub(H);\n }\n }\n if (Math.abs(I.x) + radius > t.maxX) {\n I.x = Math.sign(I.x) * (t.maxX - radius);\n B.x = -B.x * t.wallBounce;\n }\n if (t.gravity === 0) {\n if (Math.abs(I.y) + radius > t.maxY) {\n I.y = Math.sign(I.y) * (t.maxY - radius);\n B.y = -B.y * t.wallBounce;\n }\n } else if (I.y - radius < -t.maxY) {\n I.y = -t.maxY + radius;\n B.y = -B.y * t.wallBounce;\n }\n const maxBoundary = Math.max(t.maxZ, t.maxSize);\n if (Math.abs(I.z) + radius > maxBoundary) {\n I.z = Math.sign(I.z) * (t.maxZ - radius);\n B.z = -B.z * t.wallBounce;\n }\n I.toArray(s, base);\n B.toArray(o, base);\n }\n }\n}\n\nclass Y extends c {\n constructor(e) {\n super(e);\n this.uniforms = {\n thicknessDistortion: { value: 0.1 },\n thicknessAmbient: { value: 0 },\n thicknessAttenuation: { value: 0.1 },\n thicknessPower: { value: 2 },\n thicknessScale: { value: 10 }\n };\n this.defines.USE_UV = '';\n this.onBeforeCompile = e => {\n Object.assign(e.uniforms, this.uniforms);\n e.fragmentShader =\n '\\n uniform float thicknessPower;\\n uniform float thicknessScale;\\n uniform float thicknessDistortion;\\n uniform float thicknessAmbient;\\n uniform float thicknessAttenuation;\\n ' +\n e.fragmentShader;\n e.fragmentShader = e.fragmentShader.replace(\n 'void main() {',\n '\\n void RE_Direct_Scattering(const in IncidentLight directLight, const in vec2 uv, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, inout ReflectedLight reflectedLight) {\\n vec3 scatteringHalf = normalize(directLight.direction + (geometryNormal * thicknessDistortion));\\n float scatteringDot = pow(saturate(dot(geometryViewDir, -scatteringHalf)), thicknessPower) * thicknessScale;\\n #ifdef USE_COLOR\\n vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * vColor;\\n #else\\n vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * diffuse;\\n #endif\\n reflectedLight.directDiffuse += scatteringIllu * thicknessAttenuation * directLight.color;\\n }\\n\\n void main() {\\n '\n );\n const t = h.lights_fragment_begin.replaceAll(\n 'RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );',\n '\\n RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\\n RE_Direct_Scattering(directLight, vUv, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, reflectedLight);\\n '\n );\n e.fragmentShader = e.fragmentShader.replace('#include ', t);\n if (this.onBeforeCompile2) this.onBeforeCompile2(e);\n };\n }\n}\n\nconst X = {\n count: 200,\n colors: [0, 0, 0],\n ambientColor: 16777215,\n ambientIntensity: 1,\n lightIntensity: 200,\n materialParams: {\n metalness: 0.5,\n roughness: 0.5,\n clearcoat: 1,\n clearcoatRoughness: 0.15\n },\n minSize: 0.5,\n maxSize: 1,\n size0: 1,\n gravity: 0.5,\n friction: 0.9975,\n wallBounce: 0.95,\n maxVelocity: 0.15,\n maxX: 5,\n maxY: 5,\n maxZ: 2,\n controlSphere0: false,\n followCursor: true\n};\n\nconst U = new m();\n\nclass Z extends d {\n constructor(e, t = {}) {\n const i = { ...X, ...t };\n const s = new z();\n const n = new p(e, 0.04).fromScene(s).texture;\n const o = new g();\n const r = new Y({ envMap: n, ...i.materialParams });\n r.envMapRotation.x = -Math.PI / 2;\n super(o, r, i.count);\n this.config = i;\n this.physics = new W(i);\n this.#S();\n this.setColors(i.colors);\n }\n #S() {\n this.ambientLight = new f(this.config.ambientColor, this.config.ambientIntensity);\n this.add(this.ambientLight);\n this.light = new u(this.config.colors[0], this.config.lightIntensity);\n this.add(this.light);\n }\n setColors(e) {\n if (Array.isArray(e) && e.length > 1) {\n const t = (function (e) {\n let t, i;\n function setColors(e) {\n t = e;\n i = [];\n t.forEach(col => {\n i.push(new l(col));\n });\n }\n setColors(e);\n return {\n setColors,\n getColorAt: function (ratio, out = new l()) {\n const scaled = Math.max(0, Math.min(1, ratio)) * (t.length - 1);\n const idx = Math.floor(scaled);\n const start = i[idx];\n if (idx >= t.length - 1) return start.clone();\n const alpha = scaled - idx;\n const end = i[idx + 1];\n out.r = start.r + alpha * (end.r - start.r);\n out.g = start.g + alpha * (end.g - start.g);\n out.b = start.b + alpha * (end.b - start.b);\n return out;\n }\n };\n })(e);\n for (let idx = 0; idx < this.count; idx++) {\n this.setColorAt(idx, t.getColorAt(idx / this.count));\n if (idx === 0) {\n this.light.color.copy(t.getColorAt(idx / this.count));\n }\n }\n this.instanceColor.needsUpdate = true;\n }\n }\n update(e) {\n this.physics.update(e);\n for (let idx = 0; idx < this.count; idx++) {\n U.position.fromArray(this.physics.positionData, 3 * idx);\n if (idx === 0 && this.config.followCursor === false) {\n U.scale.setScalar(0);\n } else {\n U.scale.setScalar(this.physics.sizeData[idx]);\n }\n U.updateMatrix();\n this.setMatrixAt(idx, U.matrix);\n if (idx === 0) this.light.position.copy(U.position);\n }\n this.instanceMatrix.needsUpdate = true;\n }\n}\n\nfunction createBallpit(e, t = {}) {\n const i = new x({\n canvas: e,\n size: 'parent',\n rendererOptions: { antialias: true, alpha: true }\n });\n let s;\n i.renderer.toneMapping = v;\n i.camera.position.set(0, 0, 20);\n i.camera.lookAt(0, 0, 0);\n i.cameraMaxAspect = 1.5;\n i.resize();\n initialize(t);\n const n = new y();\n const o = new w(new a(0, 0, 1), 0);\n const r = new a();\n let c = false;\n\n e.style.touchAction = 'none';\n e.style.userSelect = 'none';\n e.style.webkitUserSelect = 'none';\n\n const h = S({\n domElement: e,\n onMove() {\n n.setFromCamera(h.nPosition, i.camera);\n i.camera.getWorldDirection(o.normal);\n n.ray.intersectPlane(o, r);\n s.physics.center.copy(r);\n s.config.controlSphere0 = true;\n },\n onLeave() {\n s.config.controlSphere0 = false;\n }\n });\n function initialize(e) {\n if (s) {\n i.clear();\n i.scene.remove(s);\n }\n s = new Z(i.renderer, e);\n i.scene.add(s);\n }\n i.onBeforeRender = e => {\n if (!c) s.update(e);\n };\n i.onAfterResize = e => {\n s.config.maxX = e.wWidth / 2;\n s.config.maxY = e.wHeight / 2;\n };\n return {\n three: i,\n get spheres() {\n return s;\n },\n setCount(e) {\n initialize({ ...s.config, count: e });\n },\n togglePause() {\n c = !c;\n },\n dispose() {\n h.dispose();\n i.dispose();\n }\n };\n}\n\nconst Ballpit = ({ className = '', followCursor = true, ...props }) => {\n const canvasRef = useRef(null);\n const spheresInstanceRef = useRef(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n spheresInstanceRef.current = createBallpit(canvas, { followCursor, ...props });\n\n return () => {\n if (spheresInstanceRef.current) {\n spheresInstanceRef.current.dispose();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return ;\n};\n\nexport default Ballpit;\n" } ], "registryDependencies": [], "dependencies": [ "three@^0.167.1" ] } ================================================ FILE: public/r/Ballpit-TS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Ballpit-TS-CSS", "title": "Ballpit", "description": "Physics ball pit simulation with bouncing colorful spheres.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Ballpit/Ballpit.tsx", "content": "import { gsap } from 'gsap';\nimport { Observer } from 'gsap/Observer';\nimport React, { useEffect, useRef } from 'react';\nimport {\n ACESFilmicToneMapping,\n AmbientLight,\n Clock,\n Color,\n InstancedMesh,\n MathUtils,\n MeshPhysicalMaterial,\n Object3D,\n PerspectiveCamera,\n Plane,\n PMREMGenerator,\n PointLight,\n Raycaster,\n Scene,\n ShaderChunk,\n SphereGeometry,\n SRGBColorSpace,\n Vector2,\n Vector3,\n WebGLRenderer,\n WebGLRendererParameters\n} from 'three';\nimport { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';\n\ngsap.registerPlugin(Observer);\n\ninterface XConfig {\n canvas?: HTMLCanvasElement;\n id?: string;\n rendererOptions?: Partial;\n size?: 'parent' | { width: number; height: number };\n}\n\ninterface SizeData {\n width: number;\n height: number;\n wWidth: number;\n wHeight: number;\n ratio: number;\n pixelRatio: number;\n}\n\nclass X {\n #config: XConfig;\n #postprocessing: any;\n #resizeObserver?: ResizeObserver;\n #intersectionObserver?: IntersectionObserver;\n #resizeTimer?: number;\n #animationFrameId: number = 0;\n #clock: Clock = new Clock();\n #animationState = { elapsed: 0, delta: 0 };\n #isAnimating: boolean = false;\n #isVisible: boolean = false;\n\n canvas!: HTMLCanvasElement;\n camera!: PerspectiveCamera;\n cameraMinAspect?: number;\n cameraMaxAspect?: number;\n cameraFov!: number;\n maxPixelRatio?: number;\n minPixelRatio?: number;\n scene!: Scene;\n renderer!: WebGLRenderer;\n size: SizeData = {\n width: 0,\n height: 0,\n wWidth: 0,\n wHeight: 0,\n ratio: 0,\n pixelRatio: 0\n };\n\n render: () => void = this.#render.bind(this);\n onBeforeRender: (state: { elapsed: number; delta: number }) => void = () => {};\n onAfterRender: (state: { elapsed: number; delta: number }) => void = () => {};\n onAfterResize: (size: SizeData) => void = () => {};\n isDisposed: boolean = false;\n\n constructor(config: XConfig) {\n this.#config = { ...config };\n this.#initCamera();\n this.#initScene();\n this.#initRenderer();\n this.resize();\n this.#initObservers();\n }\n\n #initCamera() {\n this.camera = new PerspectiveCamera();\n this.cameraFov = this.camera.fov;\n }\n\n #initScene() {\n this.scene = new Scene();\n }\n\n #initRenderer() {\n if (this.#config.canvas) {\n this.canvas = this.#config.canvas;\n } else if (this.#config.id) {\n const elem = document.getElementById(this.#config.id);\n if (elem instanceof HTMLCanvasElement) {\n this.canvas = elem;\n } else {\n console.error('Three: Missing canvas or id parameter');\n }\n } else {\n console.error('Three: Missing canvas or id parameter');\n }\n this.canvas!.style.display = 'block';\n const rendererOptions: WebGLRendererParameters = {\n canvas: this.canvas,\n powerPreference: 'high-performance',\n ...(this.#config.rendererOptions ?? {})\n };\n this.renderer = new WebGLRenderer(rendererOptions);\n this.renderer.outputColorSpace = SRGBColorSpace;\n }\n\n #initObservers() {\n if (!(this.#config.size instanceof Object)) {\n window.addEventListener('resize', this.#onResize.bind(this));\n if (this.#config.size === 'parent' && this.canvas.parentNode) {\n this.#resizeObserver = new ResizeObserver(this.#onResize.bind(this));\n this.#resizeObserver.observe(this.canvas.parentNode as Element);\n }\n }\n this.#intersectionObserver = new IntersectionObserver(this.#onIntersection.bind(this), {\n root: null,\n rootMargin: '0px',\n threshold: 0\n });\n this.#intersectionObserver.observe(this.canvas);\n document.addEventListener('visibilitychange', this.#onVisibilityChange.bind(this));\n }\n\n #onResize() {\n if (this.#resizeTimer) clearTimeout(this.#resizeTimer);\n this.#resizeTimer = window.setTimeout(this.resize.bind(this), 100);\n }\n\n resize() {\n let w: number, h: number;\n if (this.#config.size instanceof Object) {\n w = this.#config.size.width;\n h = this.#config.size.height;\n } else if (this.#config.size === 'parent' && this.canvas.parentNode) {\n w = (this.canvas.parentNode as HTMLElement).offsetWidth;\n h = (this.canvas.parentNode as HTMLElement).offsetHeight;\n } else {\n w = window.innerWidth;\n h = window.innerHeight;\n }\n this.size.width = w;\n this.size.height = h;\n this.size.ratio = w / h;\n this.#updateCamera();\n this.#updateRenderer();\n this.onAfterResize(this.size);\n }\n\n #updateCamera() {\n this.camera.aspect = this.size.width / this.size.height;\n if (this.camera.isPerspectiveCamera && this.cameraFov) {\n if (this.cameraMinAspect && this.camera.aspect < this.cameraMinAspect) {\n this.#adjustFov(this.cameraMinAspect);\n } else if (this.cameraMaxAspect && this.camera.aspect > this.cameraMaxAspect) {\n this.#adjustFov(this.cameraMaxAspect);\n } else {\n this.camera.fov = this.cameraFov;\n }\n }\n this.camera.updateProjectionMatrix();\n this.updateWorldSize();\n }\n\n #adjustFov(aspect: number) {\n const tanFov = Math.tan(MathUtils.degToRad(this.cameraFov / 2));\n const newTan = tanFov / (this.camera.aspect / aspect);\n this.camera.fov = 2 * MathUtils.radToDeg(Math.atan(newTan));\n }\n\n updateWorldSize() {\n if (this.camera.isPerspectiveCamera) {\n const fovRad = (this.camera.fov * Math.PI) / 180;\n this.size.wHeight = 2 * Math.tan(fovRad / 2) * this.camera.position.length();\n this.size.wWidth = this.size.wHeight * this.camera.aspect;\n } else if ((this.camera as any).isOrthographicCamera) {\n const cam = this.camera as any;\n this.size.wHeight = cam.top - cam.bottom;\n this.size.wWidth = cam.right - cam.left;\n }\n }\n\n #updateRenderer() {\n this.renderer.setSize(this.size.width, this.size.height);\n this.#postprocessing?.setSize(this.size.width, this.size.height);\n let pr = window.devicePixelRatio;\n if (this.maxPixelRatio && pr > this.maxPixelRatio) {\n pr = this.maxPixelRatio;\n } else if (this.minPixelRatio && pr < this.minPixelRatio) {\n pr = this.minPixelRatio;\n }\n this.renderer.setPixelRatio(pr);\n this.size.pixelRatio = pr;\n }\n\n get postprocessing() {\n return this.#postprocessing;\n }\n set postprocessing(value: any) {\n this.#postprocessing = value;\n this.render = value.render.bind(value);\n }\n\n #onIntersection(entries: IntersectionObserverEntry[]) {\n this.#isAnimating = entries[0].isIntersecting;\n this.#isAnimating ? this.#startAnimation() : this.#stopAnimation();\n }\n\n #onVisibilityChange() {\n if (this.#isAnimating) {\n document.hidden ? this.#stopAnimation() : this.#startAnimation();\n }\n }\n\n #startAnimation() {\n if (this.#isVisible) return;\n const animateFrame = () => {\n this.#animationFrameId = requestAnimationFrame(animateFrame);\n this.#animationState.delta = this.#clock.getDelta();\n this.#animationState.elapsed += this.#animationState.delta;\n this.onBeforeRender(this.#animationState);\n this.render();\n this.onAfterRender(this.#animationState);\n };\n this.#isVisible = true;\n this.#clock.start();\n animateFrame();\n }\n\n #stopAnimation() {\n if (this.#isVisible) {\n cancelAnimationFrame(this.#animationFrameId);\n this.#isVisible = false;\n this.#clock.stop();\n }\n }\n\n #render() {\n this.renderer.render(this.scene, this.camera);\n }\n\n clear() {\n this.scene.traverse(obj => {\n if ((obj as any).isMesh && typeof (obj as any).material === 'object' && (obj as any).material !== null) {\n Object.keys((obj as any).material).forEach(key => {\n const matProp = (obj as any).material[key];\n if (matProp && typeof matProp === 'object' && typeof matProp.dispose === 'function') {\n matProp.dispose();\n }\n });\n (obj as any).material.dispose();\n (obj as any).geometry.dispose();\n }\n });\n this.scene.clear();\n }\n\n dispose() {\n this.#onResizeCleanup();\n this.#stopAnimation();\n this.clear();\n this.#postprocessing?.dispose();\n this.renderer.dispose();\n this.renderer.forceContextLoss();\n this.isDisposed = true;\n }\n\n #onResizeCleanup() {\n window.removeEventListener('resize', this.#onResize.bind(this));\n this.#resizeObserver?.disconnect();\n this.#intersectionObserver?.disconnect();\n document.removeEventListener('visibilitychange', this.#onVisibilityChange.bind(this));\n }\n}\n\ninterface WConfig {\n count: number;\n maxX: number;\n maxY: number;\n maxZ: number;\n maxSize: number;\n minSize: number;\n size0: number;\n gravity: number;\n friction: number;\n wallBounce: number;\n maxVelocity: number;\n controlSphere0?: boolean;\n followCursor?: boolean;\n}\n\nclass W {\n config: WConfig;\n positionData: Float32Array;\n velocityData: Float32Array;\n sizeData: Float32Array;\n center: Vector3 = new Vector3();\n\n constructor(config: WConfig) {\n this.config = config;\n this.positionData = new Float32Array(3 * config.count).fill(0);\n this.velocityData = new Float32Array(3 * config.count).fill(0);\n this.sizeData = new Float32Array(config.count).fill(1);\n this.center = new Vector3();\n this.#initializePositions();\n this.setSizes();\n }\n\n #initializePositions() {\n const { config, positionData } = this;\n this.center.toArray(positionData, 0);\n for (let i = 1; i < config.count; i++) {\n const idx = 3 * i;\n positionData[idx] = MathUtils.randFloatSpread(2 * config.maxX);\n positionData[idx + 1] = MathUtils.randFloatSpread(2 * config.maxY);\n positionData[idx + 2] = MathUtils.randFloatSpread(2 * config.maxZ);\n }\n }\n\n setSizes() {\n const { config, sizeData } = this;\n sizeData[0] = config.size0;\n for (let i = 1; i < config.count; i++) {\n sizeData[i] = MathUtils.randFloat(config.minSize, config.maxSize);\n }\n }\n\n update(deltaInfo: { delta: number }) {\n const { config, center, positionData, sizeData, velocityData } = this;\n let startIdx = 0;\n if (config.controlSphere0) {\n startIdx = 1;\n const firstVec = new Vector3().fromArray(positionData, 0);\n firstVec.lerp(center, 0.1).toArray(positionData, 0);\n new Vector3(0, 0, 0).toArray(velocityData, 0);\n }\n for (let idx = startIdx; idx < config.count; idx++) {\n const base = 3 * idx;\n const pos = new Vector3().fromArray(positionData, base);\n const vel = new Vector3().fromArray(velocityData, base);\n vel.y -= deltaInfo.delta * config.gravity * sizeData[idx];\n vel.multiplyScalar(config.friction);\n vel.clampLength(0, config.maxVelocity);\n pos.add(vel);\n pos.toArray(positionData, base);\n vel.toArray(velocityData, base);\n }\n for (let idx = startIdx; idx < config.count; idx++) {\n const base = 3 * idx;\n const pos = new Vector3().fromArray(positionData, base);\n const vel = new Vector3().fromArray(velocityData, base);\n const radius = sizeData[idx];\n for (let jdx = idx + 1; jdx < config.count; jdx++) {\n const otherBase = 3 * jdx;\n const otherPos = new Vector3().fromArray(positionData, otherBase);\n const otherVel = new Vector3().fromArray(velocityData, otherBase);\n const diff = new Vector3().copy(otherPos).sub(pos);\n const dist = diff.length();\n const sumRadius = radius + sizeData[jdx];\n if (dist < sumRadius) {\n const overlap = sumRadius - dist;\n const correction = diff.normalize().multiplyScalar(0.5 * overlap);\n const velCorrection = correction.clone().multiplyScalar(Math.max(vel.length(), 1));\n pos.sub(correction);\n vel.sub(velCorrection);\n pos.toArray(positionData, base);\n vel.toArray(velocityData, base);\n otherPos.add(correction);\n otherVel.add(correction.clone().multiplyScalar(Math.max(otherVel.length(), 1)));\n otherPos.toArray(positionData, otherBase);\n otherVel.toArray(velocityData, otherBase);\n }\n }\n if (config.controlSphere0) {\n const diff = new Vector3().copy(new Vector3().fromArray(positionData, 0)).sub(pos);\n const d = diff.length();\n const sumRadius0 = radius + sizeData[0];\n if (d < sumRadius0) {\n const correction = diff.normalize().multiplyScalar(sumRadius0 - d);\n const velCorrection = correction.clone().multiplyScalar(Math.max(vel.length(), 2));\n pos.sub(correction);\n vel.sub(velCorrection);\n }\n }\n if (Math.abs(pos.x) + radius > config.maxX) {\n pos.x = Math.sign(pos.x) * (config.maxX - radius);\n vel.x = -vel.x * config.wallBounce;\n }\n if (config.gravity === 0) {\n if (Math.abs(pos.y) + radius > config.maxY) {\n pos.y = Math.sign(pos.y) * (config.maxY - radius);\n vel.y = -vel.y * config.wallBounce;\n }\n } else if (pos.y - radius < -config.maxY) {\n pos.y = -config.maxY + radius;\n vel.y = -vel.y * config.wallBounce;\n }\n const maxBoundary = Math.max(config.maxZ, config.maxSize);\n if (Math.abs(pos.z) + radius > maxBoundary) {\n pos.z = Math.sign(pos.z) * (config.maxZ - radius);\n vel.z = -vel.z * config.wallBounce;\n }\n pos.toArray(positionData, base);\n vel.toArray(velocityData, base);\n }\n }\n}\n\nclass Y extends MeshPhysicalMaterial {\n uniforms: { [key: string]: { value: any } } = {\n thicknessDistortion: { value: 0.1 },\n thicknessAmbient: { value: 0 },\n thicknessAttenuation: { value: 0.1 },\n thicknessPower: { value: 2 },\n thicknessScale: { value: 10 }\n };\n defines: { USE_UV: string };\n\n constructor(params: any) {\n super(params);\n this.defines = { USE_UV: '' };\n this.onBeforeCompile = shader => {\n Object.assign(shader.uniforms, this.uniforms);\n shader.fragmentShader =\n `\n uniform float thicknessPower;\n uniform float thicknessScale;\n uniform float thicknessDistortion;\n uniform float thicknessAmbient;\n uniform float thicknessAttenuation;\n ` + shader.fragmentShader;\n shader.fragmentShader = shader.fragmentShader.replace(\n 'void main() {',\n `\n void RE_Direct_Scattering(const in IncidentLight directLight, const in vec2 uv, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, inout ReflectedLight reflectedLight) {\n vec3 scatteringHalf = normalize(directLight.direction + (geometryNormal * thicknessDistortion));\n float scatteringDot = pow(saturate(dot(geometryViewDir, -scatteringHalf)), thicknessPower) * thicknessScale;\n #ifdef USE_COLOR\n vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * vColor;\n #else\n vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * diffuse;\n #endif\n reflectedLight.directDiffuse += scatteringIllu * thicknessAttenuation * directLight.color;\n }\n\n void main() {\n `\n );\n const lightsChunk = ShaderChunk.lights_fragment_begin.replaceAll(\n 'RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );',\n `\n RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n RE_Direct_Scattering(directLight, vUv, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, reflectedLight);\n `\n );\n shader.fragmentShader = shader.fragmentShader.replace('#include ', lightsChunk);\n if (this.onBeforeCompile2) this.onBeforeCompile2(shader);\n };\n }\n onBeforeCompile2?: (shader: any) => void;\n}\n\nconst XConfig = {\n count: 200,\n colors: [0, 0, 0],\n ambientColor: 0xffffff,\n ambientIntensity: 1,\n lightIntensity: 200,\n materialParams: {\n metalness: 0.5,\n roughness: 0.5,\n clearcoat: 1,\n clearcoatRoughness: 0.15\n },\n minSize: 0.5,\n maxSize: 1,\n size0: 1,\n gravity: 0.5,\n friction: 0.9975,\n wallBounce: 0.95,\n maxVelocity: 0.15,\n maxX: 5,\n maxY: 5,\n maxZ: 2,\n controlSphere0: false,\n followCursor: true\n};\n\nconst U = new Object3D();\n\nlet globalPointerActive = false;\nconst pointerPosition = new Vector2();\n\ninterface PointerData {\n position: Vector2;\n nPosition: Vector2;\n hover: boolean;\n touching: boolean;\n onEnter: (data: PointerData) => void;\n onMove: (data: PointerData) => void;\n onClick: (data: PointerData) => void;\n onLeave: (data: PointerData) => void;\n dispose?: () => void;\n}\n\nconst pointerMap = new Map();\n\nfunction createPointerData(options: Partial & { domElement: HTMLElement }): PointerData {\n const defaultData: PointerData = {\n position: new Vector2(),\n nPosition: new Vector2(),\n hover: false,\n touching: false,\n onEnter: () => {},\n onMove: () => {},\n onClick: () => {},\n onLeave: () => {},\n ...options\n };\n if (!pointerMap.has(options.domElement)) {\n pointerMap.set(options.domElement, defaultData);\n if (!globalPointerActive) {\n document.body.addEventListener('pointermove', onPointerMove as EventListener);\n document.body.addEventListener('pointerleave', onPointerLeave as EventListener);\n document.body.addEventListener('click', onPointerClick as EventListener);\n\n document.body.addEventListener('touchstart', onTouchStart as EventListener, { passive: false });\n document.body.addEventListener('touchmove', onTouchMove as EventListener, { passive: false });\n document.body.addEventListener('touchend', onTouchEnd as EventListener, { passive: false });\n document.body.addEventListener('touchcancel', onTouchEnd as EventListener, { passive: false });\n globalPointerActive = true;\n }\n }\n defaultData.dispose = () => {\n pointerMap.delete(options.domElement);\n if (pointerMap.size === 0) {\n document.body.removeEventListener('pointermove', onPointerMove as EventListener);\n document.body.removeEventListener('pointerleave', onPointerLeave as EventListener);\n document.body.removeEventListener('click', onPointerClick as EventListener);\n\n document.body.removeEventListener('touchstart', onTouchStart as EventListener);\n document.body.removeEventListener('touchmove', onTouchMove as EventListener);\n document.body.removeEventListener('touchend', onTouchEnd as EventListener);\n document.body.removeEventListener('touchcancel', onTouchEnd as EventListener);\n globalPointerActive = false;\n }\n };\n return defaultData;\n}\n\nfunction onPointerMove(e: PointerEvent) {\n pointerPosition.set(e.clientX, e.clientY);\n processPointerInteraction();\n}\n\nfunction processPointerInteraction() {\n for (const [elem, data] of pointerMap) {\n const rect = elem.getBoundingClientRect();\n if (isInside(rect)) {\n updatePointerData(data, rect);\n if (!data.hover) {\n data.hover = true;\n data.onEnter(data);\n }\n data.onMove(data);\n } else if (data.hover && !data.touching) {\n data.hover = false;\n data.onLeave(data);\n }\n }\n}\n\nfunction onTouchStart(e: TouchEvent) {\n if (e.touches.length > 0) {\n e.preventDefault();\n pointerPosition.set(e.touches[0].clientX, e.touches[0].clientY);\n for (const [elem, data] of pointerMap) {\n const rect = elem.getBoundingClientRect();\n if (isInside(rect)) {\n data.touching = true;\n updatePointerData(data, rect);\n if (!data.hover) {\n data.hover = true;\n data.onEnter(data);\n }\n data.onMove(data);\n }\n }\n }\n}\n\nfunction onTouchMove(e: TouchEvent) {\n if (e.touches.length > 0) {\n e.preventDefault();\n pointerPosition.set(e.touches[0].clientX, e.touches[0].clientY);\n for (const [elem, data] of pointerMap) {\n const rect = elem.getBoundingClientRect();\n updatePointerData(data, rect);\n if (isInside(rect)) {\n if (!data.hover) {\n data.hover = true;\n data.touching = true;\n data.onEnter(data);\n }\n data.onMove(data);\n } else if (data.hover && data.touching) {\n data.onMove(data);\n }\n }\n }\n}\n\nfunction onTouchEnd() {\n for (const [, data] of pointerMap) {\n if (data.touching) {\n data.touching = false;\n if (data.hover) {\n data.hover = false;\n data.onLeave(data);\n }\n }\n }\n}\n\nfunction onPointerClick(e: PointerEvent) {\n pointerPosition.set(e.clientX, e.clientY);\n for (const [elem, data] of pointerMap) {\n const rect = elem.getBoundingClientRect();\n updatePointerData(data, rect);\n if (isInside(rect)) data.onClick(data);\n }\n}\n\nfunction onPointerLeave() {\n for (const data of pointerMap.values()) {\n if (data.hover) {\n data.hover = false;\n data.onLeave(data);\n }\n }\n}\n\nfunction updatePointerData(data: PointerData, rect: DOMRect) {\n data.position.set(pointerPosition.x - rect.left, pointerPosition.y - rect.top);\n data.nPosition.set((data.position.x / rect.width) * 2 - 1, (-data.position.y / rect.height) * 2 + 1);\n}\n\nfunction isInside(rect: DOMRect) {\n return (\n pointerPosition.x >= rect.left &&\n pointerPosition.x <= rect.left + rect.width &&\n pointerPosition.y >= rect.top &&\n pointerPosition.y <= rect.top + rect.height\n );\n}\n\nconst { randFloat, randFloatSpread } = MathUtils;\nconst F = new Vector3();\nconst I = new Vector3();\nconst O = new Vector3();\nconst V = new Vector3();\nconst B = new Vector3();\nconst N = new Vector3();\nconst _ = new Vector3();\nconst j = new Vector3();\nconst H = new Vector3();\nconst T = new Vector3();\n\nclass Z extends InstancedMesh {\n config: typeof XConfig;\n physics: W;\n ambientLight: AmbientLight | undefined;\n light: PointLight | undefined;\n\n constructor(renderer: WebGLRenderer, params: Partial = {}) {\n const config = { ...XConfig, ...params };\n const roomEnv = new RoomEnvironment();\n const pmrem = new PMREMGenerator(renderer);\n const envTexture = pmrem.fromScene(roomEnv).texture;\n const geometry = new SphereGeometry();\n const material = new Y({ envMap: envTexture, ...config.materialParams });\n material.envMapRotation.x = -Math.PI / 2;\n super(geometry, material, config.count);\n this.config = config;\n this.physics = new W(config);\n this.#setupLights();\n this.setColors(config.colors);\n }\n\n #setupLights() {\n this.ambientLight = new AmbientLight(this.config.ambientColor, this.config.ambientIntensity);\n this.add(this.ambientLight);\n this.light = new PointLight(this.config.colors[0], this.config.lightIntensity);\n this.add(this.light);\n }\n\n setColors(colors: number[]) {\n if (Array.isArray(colors) && colors.length > 1) {\n const colorUtils = (function (colorsArr: number[]) {\n let baseColors: number[] = colorsArr;\n let colorObjects: Color[] = [];\n baseColors.forEach(col => {\n colorObjects.push(new Color(col));\n });\n return {\n setColors: (cols: number[]) => {\n baseColors = cols;\n colorObjects = [];\n baseColors.forEach(col => {\n colorObjects.push(new Color(col));\n });\n },\n getColorAt: (ratio: number, out: Color = new Color()) => {\n const clamped = Math.max(0, Math.min(1, ratio));\n const scaled = clamped * (baseColors.length - 1);\n const idx = Math.floor(scaled);\n const start = colorObjects[idx];\n if (idx >= baseColors.length - 1) return start.clone();\n const alpha = scaled - idx;\n const end = colorObjects[idx + 1];\n out.r = start.r + alpha * (end.r - start.r);\n out.g = start.g + alpha * (end.g - start.g);\n out.b = start.b + alpha * (end.b - start.b);\n return out;\n }\n };\n })(colors);\n for (let idx = 0; idx < this.count; idx++) {\n this.setColorAt(idx, colorUtils.getColorAt(idx / this.count));\n if (idx === 0) {\n this.light!.color.copy(colorUtils.getColorAt(idx / this.count));\n }\n }\n\n if (!this.instanceColor) return;\n this.instanceColor.needsUpdate = true;\n }\n }\n\n update(deltaInfo: { delta: number }) {\n this.physics.update(deltaInfo);\n for (let idx = 0; idx < this.count; idx++) {\n U.position.fromArray(this.physics.positionData, 3 * idx);\n if (idx === 0 && this.config.followCursor === false) {\n U.scale.setScalar(0);\n } else {\n U.scale.setScalar(this.physics.sizeData[idx]);\n }\n U.updateMatrix();\n this.setMatrixAt(idx, U.matrix);\n if (idx === 0) this.light!.position.copy(U.position);\n }\n this.instanceMatrix.needsUpdate = true;\n }\n}\n\ninterface CreateBallpitReturn {\n three: X;\n spheres: Z;\n setCount: (count: number) => void;\n togglePause: () => void;\n dispose: () => void;\n}\n\nfunction createBallpit(canvas: HTMLCanvasElement, config: any = {}): CreateBallpitReturn {\n const threeInstance = new X({\n canvas,\n size: 'parent',\n rendererOptions: { antialias: true, alpha: true }\n });\n let spheres: Z;\n threeInstance.renderer.toneMapping = ACESFilmicToneMapping;\n threeInstance.camera.position.set(0, 0, 20);\n threeInstance.camera.lookAt(0, 0, 0);\n threeInstance.cameraMaxAspect = 1.5;\n threeInstance.resize();\n initialize(config);\n const raycaster = new Raycaster();\n const plane = new Plane(new Vector3(0, 0, 1), 0);\n const intersectionPoint = new Vector3();\n let isPaused = false;\n\n canvas.style.touchAction = 'none';\n canvas.style.userSelect = 'none';\n (canvas.style as any).webkitUserSelect = 'none';\n\n const pointerData = createPointerData({\n domElement: canvas,\n onMove() {\n raycaster.setFromCamera(pointerData.nPosition, threeInstance.camera);\n threeInstance.camera.getWorldDirection(plane.normal);\n raycaster.ray.intersectPlane(plane, intersectionPoint);\n spheres.physics.center.copy(intersectionPoint);\n spheres.config.controlSphere0 = true;\n },\n onLeave() {\n spheres.config.controlSphere0 = false;\n }\n });\n function initialize(cfg: any) {\n if (spheres) {\n threeInstance.clear();\n threeInstance.scene.remove(spheres);\n }\n spheres = new Z(threeInstance.renderer, cfg);\n threeInstance.scene.add(spheres);\n }\n threeInstance.onBeforeRender = deltaInfo => {\n if (!isPaused) spheres.update(deltaInfo);\n };\n threeInstance.onAfterResize = size => {\n spheres.config.maxX = size.wWidth / 2;\n spheres.config.maxY = size.wHeight / 2;\n };\n return {\n three: threeInstance,\n get spheres() {\n return spheres;\n },\n setCount(count: number) {\n initialize({ ...spheres.config, count });\n },\n togglePause() {\n isPaused = !isPaused;\n },\n dispose() {\n pointerData.dispose?.();\n threeInstance.dispose();\n }\n };\n}\n\ninterface BallpitProps {\n className?: string;\n followCursor?: boolean;\n [key: string]: any;\n}\n\nconst Ballpit: React.FC = ({ className = '', followCursor = true, ...props }) => {\n const canvasRef = useRef(null);\n const spheresInstanceRef = useRef(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n spheresInstanceRef.current = createBallpit(canvas, {\n followCursor,\n ...props\n });\n\n return () => {\n if (spheresInstanceRef.current) {\n spheresInstanceRef.current.dispose();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return ;\n};\n\nexport default Ballpit;\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0", "three@^0.167.1" ] } ================================================ FILE: public/r/Ballpit-TS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Ballpit-TS-TW", "title": "Ballpit", "description": "Physics ball pit simulation with bouncing colorful spheres.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Ballpit/Ballpit.tsx", "content": "import { gsap } from 'gsap';\nimport { Observer } from 'gsap/Observer';\nimport React, { useEffect, useRef } from 'react';\nimport {\n ACESFilmicToneMapping,\n AmbientLight,\n Clock,\n Color,\n InstancedMesh,\n MathUtils,\n MeshPhysicalMaterial,\n Object3D,\n PerspectiveCamera,\n Plane,\n PMREMGenerator,\n PointLight,\n Raycaster,\n Scene,\n ShaderChunk,\n SphereGeometry,\n SRGBColorSpace,\n Vector2,\n Vector3,\n WebGLRenderer,\n WebGLRendererParameters\n} from 'three';\nimport { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';\n\ngsap.registerPlugin(Observer);\n\ninterface XConfig {\n canvas?: HTMLCanvasElement;\n id?: string;\n rendererOptions?: Partial;\n size?: 'parent' | { width: number; height: number };\n}\n\ninterface SizeData {\n width: number;\n height: number;\n wWidth: number;\n wHeight: number;\n ratio: number;\n pixelRatio: number;\n}\n\nclass X {\n #config: XConfig;\n #postprocessing: any;\n #resizeObserver?: ResizeObserver;\n #intersectionObserver?: IntersectionObserver;\n #resizeTimer?: number;\n #animationFrameId: number = 0;\n #clock: Clock = new Clock();\n #animationState = { elapsed: 0, delta: 0 };\n #isAnimating: boolean = false;\n #isVisible: boolean = false;\n\n canvas!: HTMLCanvasElement;\n camera!: PerspectiveCamera;\n cameraMinAspect?: number;\n cameraMaxAspect?: number;\n cameraFov!: number;\n maxPixelRatio?: number;\n minPixelRatio?: number;\n scene!: Scene;\n renderer!: WebGLRenderer;\n size: SizeData = {\n width: 0,\n height: 0,\n wWidth: 0,\n wHeight: 0,\n ratio: 0,\n pixelRatio: 0\n };\n\n render: () => void = this.#render.bind(this);\n onBeforeRender: (state: { elapsed: number; delta: number }) => void = () => {};\n onAfterRender: (state: { elapsed: number; delta: number }) => void = () => {};\n onAfterResize: (size: SizeData) => void = () => {};\n isDisposed: boolean = false;\n\n constructor(config: XConfig) {\n this.#config = { ...config };\n this.#initCamera();\n this.#initScene();\n this.#initRenderer();\n this.resize();\n this.#initObservers();\n }\n\n #initCamera() {\n this.camera = new PerspectiveCamera();\n this.cameraFov = this.camera.fov;\n }\n\n #initScene() {\n this.scene = new Scene();\n }\n\n #initRenderer() {\n if (this.#config.canvas) {\n this.canvas = this.#config.canvas;\n } else if (this.#config.id) {\n const elem = document.getElementById(this.#config.id);\n if (elem instanceof HTMLCanvasElement) {\n this.canvas = elem;\n } else {\n console.error('Three: Missing canvas or id parameter');\n }\n } else {\n console.error('Three: Missing canvas or id parameter');\n }\n this.canvas!.style.display = 'block';\n const rendererOptions: WebGLRendererParameters = {\n canvas: this.canvas,\n powerPreference: 'high-performance',\n ...(this.#config.rendererOptions ?? {})\n };\n this.renderer = new WebGLRenderer(rendererOptions);\n this.renderer.outputColorSpace = SRGBColorSpace;\n }\n\n #initObservers() {\n if (!(this.#config.size instanceof Object)) {\n window.addEventListener('resize', this.#onResize.bind(this));\n if (this.#config.size === 'parent' && this.canvas.parentNode) {\n this.#resizeObserver = new ResizeObserver(this.#onResize.bind(this));\n this.#resizeObserver.observe(this.canvas.parentNode as Element);\n }\n }\n this.#intersectionObserver = new IntersectionObserver(this.#onIntersection.bind(this), {\n root: null,\n rootMargin: '0px',\n threshold: 0\n });\n this.#intersectionObserver.observe(this.canvas);\n document.addEventListener('visibilitychange', this.#onVisibilityChange.bind(this));\n }\n\n #onResize() {\n if (this.#resizeTimer) clearTimeout(this.#resizeTimer);\n this.#resizeTimer = window.setTimeout(this.resize.bind(this), 100);\n }\n\n resize() {\n let w: number, h: number;\n if (this.#config.size instanceof Object) {\n w = this.#config.size.width;\n h = this.#config.size.height;\n } else if (this.#config.size === 'parent' && this.canvas.parentNode) {\n w = (this.canvas.parentNode as HTMLElement).offsetWidth;\n h = (this.canvas.parentNode as HTMLElement).offsetHeight;\n } else {\n w = window.innerWidth;\n h = window.innerHeight;\n }\n this.size.width = w;\n this.size.height = h;\n this.size.ratio = w / h;\n this.#updateCamera();\n this.#updateRenderer();\n this.onAfterResize(this.size);\n }\n\n #updateCamera() {\n this.camera.aspect = this.size.width / this.size.height;\n if (this.camera.isPerspectiveCamera && this.cameraFov) {\n if (this.cameraMinAspect && this.camera.aspect < this.cameraMinAspect) {\n this.#adjustFov(this.cameraMinAspect);\n } else if (this.cameraMaxAspect && this.camera.aspect > this.cameraMaxAspect) {\n this.#adjustFov(this.cameraMaxAspect);\n } else {\n this.camera.fov = this.cameraFov;\n }\n }\n this.camera.updateProjectionMatrix();\n this.updateWorldSize();\n }\n\n #adjustFov(aspect: number) {\n const tanFov = Math.tan(MathUtils.degToRad(this.cameraFov / 2));\n const newTan = tanFov / (this.camera.aspect / aspect);\n this.camera.fov = 2 * MathUtils.radToDeg(Math.atan(newTan));\n }\n\n updateWorldSize() {\n if (this.camera.isPerspectiveCamera) {\n const fovRad = (this.camera.fov * Math.PI) / 180;\n this.size.wHeight = 2 * Math.tan(fovRad / 2) * this.camera.position.length();\n this.size.wWidth = this.size.wHeight * this.camera.aspect;\n } else if ((this.camera as any).isOrthographicCamera) {\n const cam = this.camera as any;\n this.size.wHeight = cam.top - cam.bottom;\n this.size.wWidth = cam.right - cam.left;\n }\n }\n\n #updateRenderer() {\n this.renderer.setSize(this.size.width, this.size.height);\n this.#postprocessing?.setSize(this.size.width, this.size.height);\n let pr = window.devicePixelRatio;\n if (this.maxPixelRatio && pr > this.maxPixelRatio) {\n pr = this.maxPixelRatio;\n } else if (this.minPixelRatio && pr < this.minPixelRatio) {\n pr = this.minPixelRatio;\n }\n this.renderer.setPixelRatio(pr);\n this.size.pixelRatio = pr;\n }\n\n get postprocessing() {\n return this.#postprocessing;\n }\n set postprocessing(value: any) {\n this.#postprocessing = value;\n this.render = value.render.bind(value);\n }\n\n #onIntersection(entries: IntersectionObserverEntry[]) {\n this.#isAnimating = entries[0].isIntersecting;\n this.#isAnimating ? this.#startAnimation() : this.#stopAnimation();\n }\n\n #onVisibilityChange() {\n if (this.#isAnimating) {\n document.hidden ? this.#stopAnimation() : this.#startAnimation();\n }\n }\n\n #startAnimation() {\n if (this.#isVisible) return;\n const animateFrame = () => {\n this.#animationFrameId = requestAnimationFrame(animateFrame);\n this.#animationState.delta = this.#clock.getDelta();\n this.#animationState.elapsed += this.#animationState.delta;\n this.onBeforeRender(this.#animationState);\n this.render();\n this.onAfterRender(this.#animationState);\n };\n this.#isVisible = true;\n this.#clock.start();\n animateFrame();\n }\n\n #stopAnimation() {\n if (this.#isVisible) {\n cancelAnimationFrame(this.#animationFrameId);\n this.#isVisible = false;\n this.#clock.stop();\n }\n }\n\n #render() {\n this.renderer.render(this.scene, this.camera);\n }\n\n clear() {\n this.scene.traverse(obj => {\n if ((obj as any).isMesh && typeof (obj as any).material === 'object' && (obj as any).material !== null) {\n Object.keys((obj as any).material).forEach(key => {\n const matProp = (obj as any).material[key];\n if (matProp && typeof matProp === 'object' && typeof matProp.dispose === 'function') {\n matProp.dispose();\n }\n });\n (obj as any).material.dispose();\n (obj as any).geometry.dispose();\n }\n });\n this.scene.clear();\n }\n\n dispose() {\n this.#onResizeCleanup();\n this.#stopAnimation();\n this.clear();\n this.#postprocessing?.dispose();\n this.renderer.dispose();\n this.renderer.forceContextLoss();\n this.isDisposed = true;\n }\n\n #onResizeCleanup() {\n window.removeEventListener('resize', this.#onResize.bind(this));\n this.#resizeObserver?.disconnect();\n this.#intersectionObserver?.disconnect();\n document.removeEventListener('visibilitychange', this.#onVisibilityChange.bind(this));\n }\n}\n\ninterface WConfig {\n count: number;\n maxX: number;\n maxY: number;\n maxZ: number;\n maxSize: number;\n minSize: number;\n size0: number;\n gravity: number;\n friction: number;\n wallBounce: number;\n maxVelocity: number;\n controlSphere0?: boolean;\n followCursor?: boolean;\n}\n\nclass W {\n config: WConfig;\n positionData: Float32Array;\n velocityData: Float32Array;\n sizeData: Float32Array;\n center: Vector3 = new Vector3();\n\n constructor(config: WConfig) {\n this.config = config;\n this.positionData = new Float32Array(3 * config.count).fill(0);\n this.velocityData = new Float32Array(3 * config.count).fill(0);\n this.sizeData = new Float32Array(config.count).fill(1);\n this.center = new Vector3();\n this.#initializePositions();\n this.setSizes();\n }\n\n #initializePositions() {\n const { config, positionData } = this;\n this.center.toArray(positionData, 0);\n for (let i = 1; i < config.count; i++) {\n const idx = 3 * i;\n positionData[idx] = MathUtils.randFloatSpread(2 * config.maxX);\n positionData[idx + 1] = MathUtils.randFloatSpread(2 * config.maxY);\n positionData[idx + 2] = MathUtils.randFloatSpread(2 * config.maxZ);\n }\n }\n\n setSizes() {\n const { config, sizeData } = this;\n sizeData[0] = config.size0;\n for (let i = 1; i < config.count; i++) {\n sizeData[i] = MathUtils.randFloat(config.minSize, config.maxSize);\n }\n }\n\n update(deltaInfo: { delta: number }) {\n const { config, center, positionData, sizeData, velocityData } = this;\n let startIdx = 0;\n if (config.controlSphere0) {\n startIdx = 1;\n const firstVec = new Vector3().fromArray(positionData, 0);\n firstVec.lerp(center, 0.1).toArray(positionData, 0);\n new Vector3(0, 0, 0).toArray(velocityData, 0);\n }\n for (let idx = startIdx; idx < config.count; idx++) {\n const base = 3 * idx;\n const pos = new Vector3().fromArray(positionData, base);\n const vel = new Vector3().fromArray(velocityData, base);\n vel.y -= deltaInfo.delta * config.gravity * sizeData[idx];\n vel.multiplyScalar(config.friction);\n vel.clampLength(0, config.maxVelocity);\n pos.add(vel);\n pos.toArray(positionData, base);\n vel.toArray(velocityData, base);\n }\n for (let idx = startIdx; idx < config.count; idx++) {\n const base = 3 * idx;\n const pos = new Vector3().fromArray(positionData, base);\n const vel = new Vector3().fromArray(velocityData, base);\n const radius = sizeData[idx];\n for (let jdx = idx + 1; jdx < config.count; jdx++) {\n const otherBase = 3 * jdx;\n const otherPos = new Vector3().fromArray(positionData, otherBase);\n const otherVel = new Vector3().fromArray(velocityData, otherBase);\n const diff = new Vector3().copy(otherPos).sub(pos);\n const dist = diff.length();\n const sumRadius = radius + sizeData[jdx];\n if (dist < sumRadius) {\n const overlap = sumRadius - dist;\n const correction = diff.normalize().multiplyScalar(0.5 * overlap);\n const velCorrection = correction.clone().multiplyScalar(Math.max(vel.length(), 1));\n pos.sub(correction);\n vel.sub(velCorrection);\n pos.toArray(positionData, base);\n vel.toArray(velocityData, base);\n otherPos.add(correction);\n otherVel.add(correction.clone().multiplyScalar(Math.max(otherVel.length(), 1)));\n otherPos.toArray(positionData, otherBase);\n otherVel.toArray(velocityData, otherBase);\n }\n }\n if (config.controlSphere0) {\n const diff = new Vector3().copy(new Vector3().fromArray(positionData, 0)).sub(pos);\n const d = diff.length();\n const sumRadius0 = radius + sizeData[0];\n if (d < sumRadius0) {\n const correction = diff.normalize().multiplyScalar(sumRadius0 - d);\n const velCorrection = correction.clone().multiplyScalar(Math.max(vel.length(), 2));\n pos.sub(correction);\n vel.sub(velCorrection);\n }\n }\n if (Math.abs(pos.x) + radius > config.maxX) {\n pos.x = Math.sign(pos.x) * (config.maxX - radius);\n vel.x = -vel.x * config.wallBounce;\n }\n if (config.gravity === 0) {\n if (Math.abs(pos.y) + radius > config.maxY) {\n pos.y = Math.sign(pos.y) * (config.maxY - radius);\n vel.y = -vel.y * config.wallBounce;\n }\n } else if (pos.y - radius < -config.maxY) {\n pos.y = -config.maxY + radius;\n vel.y = -vel.y * config.wallBounce;\n }\n const maxBoundary = Math.max(config.maxZ, config.maxSize);\n if (Math.abs(pos.z) + radius > maxBoundary) {\n pos.z = Math.sign(pos.z) * (config.maxZ - radius);\n vel.z = -vel.z * config.wallBounce;\n }\n pos.toArray(positionData, base);\n vel.toArray(velocityData, base);\n }\n }\n}\n\nclass Y extends MeshPhysicalMaterial {\n uniforms: { [key: string]: { value: any } } = {\n thicknessDistortion: { value: 0.1 },\n thicknessAmbient: { value: 0 },\n thicknessAttenuation: { value: 0.1 },\n thicknessPower: { value: 2 },\n thicknessScale: { value: 10 }\n };\n defines: { USE_UV: string; };\n\n constructor(params: any) {\n super(params);\n this.defines = { USE_UV: '' };\n this.onBeforeCompile = shader => {\n Object.assign(shader.uniforms, this.uniforms);\n shader.fragmentShader =\n `\n uniform float thicknessPower;\n uniform float thicknessScale;\n uniform float thicknessDistortion;\n uniform float thicknessAmbient;\n uniform float thicknessAttenuation;\n ` + shader.fragmentShader;\n shader.fragmentShader = shader.fragmentShader.replace(\n 'void main() {',\n `\n void RE_Direct_Scattering(const in IncidentLight directLight, const in vec2 uv, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, inout ReflectedLight reflectedLight) {\n vec3 scatteringHalf = normalize(directLight.direction + (geometryNormal * thicknessDistortion));\n float scatteringDot = pow(saturate(dot(geometryViewDir, -scatteringHalf)), thicknessPower) * thicknessScale;\n #ifdef USE_COLOR\n vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * vColor;\n #else\n vec3 scatteringIllu = (scatteringDot + thicknessAmbient) * diffuse;\n #endif\n reflectedLight.directDiffuse += scatteringIllu * thicknessAttenuation * directLight.color;\n }\n\n void main() {\n `\n );\n const lightsChunk = ShaderChunk.lights_fragment_begin.replaceAll(\n 'RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );',\n `\n RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );\n RE_Direct_Scattering(directLight, vUv, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, reflectedLight);\n `\n );\n shader.fragmentShader = shader.fragmentShader.replace('#include ', lightsChunk);\n if (this.onBeforeCompile2) this.onBeforeCompile2(shader);\n };\n }\n onBeforeCompile2?: (shader: any) => void;\n}\n\nconst XConfig = {\n count: 200,\n colors: [0, 0, 0],\n ambientColor: 0xffffff,\n ambientIntensity: 1,\n lightIntensity: 200,\n materialParams: {\n metalness: 0.5,\n roughness: 0.5,\n clearcoat: 1,\n clearcoatRoughness: 0.15\n },\n minSize: 0.5,\n maxSize: 1,\n size0: 1,\n gravity: 0.5,\n friction: 0.9975,\n wallBounce: 0.95,\n maxVelocity: 0.15,\n maxX: 5,\n maxY: 5,\n maxZ: 2,\n controlSphere0: false,\n followCursor: true\n};\n\nconst U = new Object3D();\n\nlet globalPointerActive = false;\nconst pointerPosition = new Vector2();\n\ninterface PointerData {\n position: Vector2;\n nPosition: Vector2;\n hover: boolean;\n touching: boolean;\n onEnter: (data: PointerData) => void;\n onMove: (data: PointerData) => void;\n onClick: (data: PointerData) => void;\n onLeave: (data: PointerData) => void;\n dispose?: () => void;\n}\n\nconst pointerMap = new Map();\n\nfunction createPointerData(options: Partial & { domElement: HTMLElement }): PointerData {\n const defaultData: PointerData = {\n position: new Vector2(),\n nPosition: new Vector2(),\n hover: false,\n touching: false,\n onEnter: () => {},\n onMove: () => {},\n onClick: () => {},\n onLeave: () => {},\n ...options\n };\n if (!pointerMap.has(options.domElement)) {\n pointerMap.set(options.domElement, defaultData);\n if (!globalPointerActive) {\n document.body.addEventListener('pointermove', onPointerMove as EventListener);\n document.body.addEventListener('pointerleave', onPointerLeave as EventListener);\n document.body.addEventListener('click', onPointerClick as EventListener);\n\n document.body.addEventListener('touchstart', onTouchStart as EventListener, {\n passive: false\n });\n document.body.addEventListener('touchmove', onTouchMove as EventListener, {\n passive: false\n });\n document.body.addEventListener('touchend', onTouchEnd as EventListener, {\n passive: false\n });\n document.body.addEventListener('touchcancel', onTouchEnd as EventListener, {\n passive: false\n });\n globalPointerActive = true;\n }\n }\n defaultData.dispose = () => {\n pointerMap.delete(options.domElement);\n if (pointerMap.size === 0) {\n document.body.removeEventListener('pointermove', onPointerMove as EventListener);\n document.body.removeEventListener('pointerleave', onPointerLeave as EventListener);\n document.body.removeEventListener('click', onPointerClick as EventListener);\n\n document.body.removeEventListener('touchstart', onTouchStart as EventListener);\n document.body.removeEventListener('touchmove', onTouchMove as EventListener);\n document.body.removeEventListener('touchend', onTouchEnd as EventListener);\n document.body.removeEventListener('touchcancel', onTouchEnd as EventListener);\n globalPointerActive = false;\n }\n };\n return defaultData;\n}\n\nfunction onPointerMove(e: PointerEvent) {\n pointerPosition.set(e.clientX, e.clientY);\n processPointerInteraction();\n}\n\nfunction processPointerInteraction() {\n for (const [elem, data] of pointerMap) {\n const rect = elem.getBoundingClientRect();\n if (isInside(rect)) {\n updatePointerData(data, rect);\n if (!data.hover) {\n data.hover = true;\n data.onEnter(data);\n }\n data.onMove(data);\n } else if (data.hover && !data.touching) {\n data.hover = false;\n data.onLeave(data);\n }\n }\n}\n\nfunction onTouchStart(e: TouchEvent) {\n if (e.touches.length > 0) {\n e.preventDefault();\n pointerPosition.set(e.touches[0].clientX, e.touches[0].clientY);\n for (const [elem, data] of pointerMap) {\n const rect = elem.getBoundingClientRect();\n if (isInside(rect)) {\n data.touching = true;\n updatePointerData(data, rect);\n if (!data.hover) {\n data.hover = true;\n data.onEnter(data);\n }\n data.onMove(data);\n }\n }\n }\n}\n\nfunction onTouchMove(e: TouchEvent) {\n if (e.touches.length > 0) {\n e.preventDefault();\n pointerPosition.set(e.touches[0].clientX, e.touches[0].clientY);\n for (const [elem, data] of pointerMap) {\n const rect = elem.getBoundingClientRect();\n updatePointerData(data, rect);\n if (isInside(rect)) {\n if (!data.hover) {\n data.hover = true;\n data.touching = true;\n data.onEnter(data);\n }\n data.onMove(data);\n } else if (data.hover && data.touching) {\n data.onMove(data);\n }\n }\n }\n}\n\nfunction onTouchEnd() {\n for (const [, data] of pointerMap) {\n if (data.touching) {\n data.touching = false;\n if (data.hover) {\n data.hover = false;\n data.onLeave(data);\n }\n }\n }\n}\n\nfunction onPointerClick(e: PointerEvent) {\n pointerPosition.set(e.clientX, e.clientY);\n for (const [elem, data] of pointerMap) {\n const rect = elem.getBoundingClientRect();\n updatePointerData(data, rect);\n if (isInside(rect)) data.onClick(data);\n }\n}\n\nfunction onPointerLeave() {\n for (const data of pointerMap.values()) {\n if (data.hover) {\n data.hover = false;\n data.onLeave(data);\n }\n }\n}\n\nfunction updatePointerData(data: PointerData, rect: DOMRect) {\n data.position.set(pointerPosition.x - rect.left, pointerPosition.y - rect.top);\n data.nPosition.set((data.position.x / rect.width) * 2 - 1, (-data.position.y / rect.height) * 2 + 1);\n}\n\nfunction isInside(rect: DOMRect) {\n return (\n pointerPosition.x >= rect.left &&\n pointerPosition.x <= rect.left + rect.width &&\n pointerPosition.y >= rect.top &&\n pointerPosition.y <= rect.top + rect.height\n );\n}\n\nclass Z extends InstancedMesh {\n config: typeof XConfig;\n physics: W;\n ambientLight: AmbientLight | undefined;\n light: PointLight | undefined;\n\n constructor(renderer: WebGLRenderer, params: Partial = {}) {\n const config = { ...XConfig, ...params };\n const roomEnv = new RoomEnvironment();\n const pmrem = new PMREMGenerator(renderer);\n const envTexture = pmrem.fromScene(roomEnv).texture;\n const geometry = new SphereGeometry();\n const material = new Y({ envMap: envTexture, ...config.materialParams });\n material.envMapRotation.x = -Math.PI / 2;\n super(geometry, material, config.count);\n this.config = config;\n this.physics = new W(config);\n this.#setupLights();\n this.setColors(config.colors);\n }\n\n #setupLights() {\n this.ambientLight = new AmbientLight(this.config.ambientColor, this.config.ambientIntensity);\n this.add(this.ambientLight);\n this.light = new PointLight(this.config.colors[0], this.config.lightIntensity);\n this.add(this.light);\n }\n\n setColors(colors: number[]) {\n if (Array.isArray(colors) && colors.length > 1) {\n const colorUtils = (function (colorsArr: number[]) {\n let baseColors: number[] = colorsArr;\n let colorObjects: Color[] = [];\n baseColors.forEach(col => {\n colorObjects.push(new Color(col));\n });\n return {\n setColors: (cols: number[]) => {\n baseColors = cols;\n colorObjects = [];\n baseColors.forEach(col => {\n colorObjects.push(new Color(col));\n });\n },\n getColorAt: (ratio: number, out: Color = new Color()) => {\n const clamped = Math.max(0, Math.min(1, ratio));\n const scaled = clamped * (baseColors.length - 1);\n const idx = Math.floor(scaled);\n const start = colorObjects[idx];\n if (idx >= baseColors.length - 1) return start.clone();\n const alpha = scaled - idx;\n const end = colorObjects[idx + 1];\n out.r = start.r + alpha * (end.r - start.r);\n out.g = start.g + alpha * (end.g - start.g);\n out.b = start.b + alpha * (end.b - start.b);\n return out;\n }\n };\n })(colors);\n for (let idx = 0; idx < this.count; idx++) {\n this.setColorAt(idx, colorUtils.getColorAt(idx / this.count));\n if (idx === 0) {\n this.light!.color.copy(colorUtils.getColorAt(idx / this.count));\n }\n }\n\n if (!this.instanceColor) return;\n this.instanceColor.needsUpdate = true;\n }\n }\n\n update(deltaInfo: { delta: number }) {\n this.physics.update(deltaInfo);\n for (let idx = 0; idx < this.count; idx++) {\n U.position.fromArray(this.physics.positionData, 3 * idx);\n if (idx === 0 && this.config.followCursor === false) {\n U.scale.setScalar(0);\n } else {\n U.scale.setScalar(this.physics.sizeData[idx]);\n }\n U.updateMatrix();\n this.setMatrixAt(idx, U.matrix);\n if (idx === 0) this.light!.position.copy(U.position);\n }\n this.instanceMatrix.needsUpdate = true;\n }\n}\n\ninterface CreateBallpitReturn {\n three: X;\n spheres: Z;\n setCount: (count: number) => void;\n togglePause: () => void;\n dispose: () => void;\n}\n\nfunction createBallpit(canvas: HTMLCanvasElement, config: any = {}): CreateBallpitReturn {\n const threeInstance = new X({\n canvas,\n size: 'parent',\n rendererOptions: { antialias: true, alpha: true }\n });\n let spheres: Z;\n threeInstance.renderer.toneMapping = ACESFilmicToneMapping;\n threeInstance.camera.position.set(0, 0, 20);\n threeInstance.camera.lookAt(0, 0, 0);\n threeInstance.cameraMaxAspect = 1.5;\n threeInstance.resize();\n initialize(config);\n const raycaster = new Raycaster();\n const plane = new Plane(new Vector3(0, 0, 1), 0);\n const intersectionPoint = new Vector3();\n let isPaused = false;\n\n canvas.style.touchAction = 'none';\n canvas.style.userSelect = 'none';\n (canvas.style as any).webkitUserSelect = 'none';\n\n const pointerData = createPointerData({\n domElement: canvas,\n onMove() {\n raycaster.setFromCamera(pointerData.nPosition, threeInstance.camera);\n threeInstance.camera.getWorldDirection(plane.normal);\n raycaster.ray.intersectPlane(plane, intersectionPoint);\n spheres.physics.center.copy(intersectionPoint);\n spheres.config.controlSphere0 = true;\n },\n onLeave() {\n spheres.config.controlSphere0 = false;\n }\n });\n function initialize(cfg: any) {\n if (spheres) {\n threeInstance.clear();\n threeInstance.scene.remove(spheres);\n }\n spheres = new Z(threeInstance.renderer, cfg);\n threeInstance.scene.add(spheres);\n }\n threeInstance.onBeforeRender = deltaInfo => {\n if (!isPaused) spheres.update(deltaInfo);\n };\n threeInstance.onAfterResize = size => {\n spheres.config.maxX = size.wWidth / 2;\n spheres.config.maxY = size.wHeight / 2;\n };\n return {\n three: threeInstance,\n get spheres() {\n return spheres;\n },\n setCount(count: number) {\n initialize({ ...spheres.config, count });\n },\n togglePause() {\n isPaused = !isPaused;\n },\n dispose() {\n pointerData.dispose?.();\n threeInstance.dispose();\n }\n };\n}\n\ninterface BallpitProps {\n className?: string;\n followCursor?: boolean;\n [key: string]: any;\n}\n\nconst Ballpit: React.FC = ({ className = '', followCursor = true, ...props }) => {\n const canvasRef = useRef(null);\n const spheresInstanceRef = useRef(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n spheresInstanceRef.current = createBallpit(canvas, {\n followCursor,\n ...props\n });\n\n return () => {\n if (spheresInstanceRef.current) {\n spheresInstanceRef.current.dispose();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return ;\n};\n\nexport default Ballpit;\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0", "three@^0.167.1" ] } ================================================ FILE: public/r/Beams-JS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Beams-JS-CSS", "title": "Beams", "description": "Crossing animated ribbons with customizable properties.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Beams/Beams.css", "content": ".beams-container {\n position: relative;\n width: 100%;\n height: 100%;\n}\n" }, { "type": "registry:component", "path": "Beams/Beams.jsx", "content": "/* eslint-disable react/no-unknown-property */\nimport { forwardRef, useImperativeHandle, useEffect, useRef, useMemo } from 'react';\n\nimport * as THREE from 'three';\n\nimport { Canvas, useFrame } from '@react-three/fiber';\nimport { PerspectiveCamera } from '@react-three/drei';\nimport { degToRad } from 'three/src/math/MathUtils.js';\n\nimport './Beams.css';\n\nfunction extendMaterial(BaseMaterial, cfg) {\n const physical = THREE.ShaderLib.physical;\n const { vertexShader: baseVert, fragmentShader: baseFrag, uniforms: baseUniforms } = physical;\n const baseDefines = physical.defines ?? {};\n\n const uniforms = THREE.UniformsUtils.clone(baseUniforms);\n\n const defaults = new BaseMaterial(cfg.material || {});\n\n if (defaults.color) uniforms.diffuse.value = defaults.color;\n if ('roughness' in defaults) uniforms.roughness.value = defaults.roughness;\n if ('metalness' in defaults) uniforms.metalness.value = defaults.metalness;\n if ('envMap' in defaults) uniforms.envMap.value = defaults.envMap;\n if ('envMapIntensity' in defaults) uniforms.envMapIntensity.value = defaults.envMapIntensity;\n\n Object.entries(cfg.uniforms ?? {}).forEach(([key, u]) => {\n uniforms[key] = u !== null && typeof u === 'object' && 'value' in u ? u : { value: u };\n });\n\n let vert = `${cfg.header}\\n${cfg.vertexHeader ?? ''}\\n${baseVert}`;\n let frag = `${cfg.header}\\n${cfg.fragmentHeader ?? ''}\\n${baseFrag}`;\n\n for (const [inc, code] of Object.entries(cfg.vertex ?? {})) {\n vert = vert.replace(inc, `${inc}\\n${code}`);\n }\n for (const [inc, code] of Object.entries(cfg.fragment ?? {})) {\n frag = frag.replace(inc, `${inc}\\n${code}`);\n }\n\n const mat = new THREE.ShaderMaterial({\n defines: { ...baseDefines },\n uniforms,\n vertexShader: vert,\n fragmentShader: frag,\n lights: true,\n fog: !!cfg.material?.fog\n });\n\n return mat;\n}\n\nconst CanvasWrapper = ({ children }) => (\n \n {children}\n \n);\n\nconst hexToNormalizedRGB = hex => {\n const clean = hex.replace('#', '');\n const r = parseInt(clean.substring(0, 2), 16);\n const g = parseInt(clean.substring(2, 4), 16);\n const b = parseInt(clean.substring(4, 6), 16);\n return [r / 255, g / 255, b / 255];\n};\n\nconst noise = `\nfloat random (in vec2 st) {\n return fract(sin(dot(st.xy,\n vec2(12.9898,78.233)))*\n 43758.5453123);\n}\nfloat noise (in vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = random(i);\n float b = random(i + vec2(1.0, 0.0));\n float c = random(i + vec2(0.0, 1.0));\n float d = random(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n return mix(a, b, u.x) +\n (c - a)* u.y * (1.0 - u.x) +\n (d - b) * u.x * u.y;\n}\nvec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\nvec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\nvec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}\nfloat cnoise(vec3 P){\n vec3 Pi0 = floor(P);\n vec3 Pi1 = Pi0 + vec3(1.0);\n Pi0 = mod(Pi0, 289.0);\n Pi1 = mod(Pi1, 289.0);\n vec3 Pf0 = fract(P);\n vec3 Pf1 = Pf0 - vec3(1.0);\n vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);\n vec4 iy = vec4(Pi0.yy, Pi1.yy);\n vec4 iz0 = Pi0.zzzz;\n vec4 iz1 = Pi1.zzzz;\n vec4 ixy = permute(permute(ix) + iy);\n vec4 ixy0 = permute(ixy + iz0);\n vec4 ixy1 = permute(ixy + iz1);\n vec4 gx0 = ixy0 / 7.0;\n vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;\n gx0 = fract(gx0);\n vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);\n vec4 sz0 = step(gz0, vec4(0.0));\n gx0 -= sz0 * (step(0.0, gx0) - 0.5);\n gy0 -= sz0 * (step(0.0, gy0) - 0.5);\n vec4 gx1 = ixy1 / 7.0;\n vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;\n gx1 = fract(gx1);\n vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);\n vec4 sz1 = step(gz1, vec4(0.0));\n gx1 -= sz1 * (step(0.0, gx1) - 0.5);\n gy1 -= sz1 * (step(0.0, gy1) - 0.5);\n vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);\n vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);\n vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);\n vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);\n vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);\n vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);\n vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);\n vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);\n vec4 norm0 = taylorInvSqrt(vec4(dot(g000,g000),dot(g010,g010),dot(g100,g100),dot(g110,g110)));\n g000 *= norm0.x; g010 *= norm0.y; g100 *= norm0.z; g110 *= norm0.w;\n vec4 norm1 = taylorInvSqrt(vec4(dot(g001,g001),dot(g011,g011),dot(g101,g101),dot(g111,g111)));\n g001 *= norm1.x; g011 *= norm1.y; g101 *= norm1.z; g111 *= norm1.w;\n float n000 = dot(g000, Pf0);\n float n100 = dot(g100, vec3(Pf1.x,Pf0.yz));\n float n010 = dot(g010, vec3(Pf0.x,Pf1.y,Pf0.z));\n float n110 = dot(g110, vec3(Pf1.xy,Pf0.z));\n float n001 = dot(g001, vec3(Pf0.xy,Pf1.z));\n float n101 = dot(g101, vec3(Pf1.x,Pf0.y,Pf1.z));\n float n011 = dot(g011, vec3(Pf0.x,Pf1.yz));\n float n111 = dot(g111, Pf1);\n vec3 fade_xyz = fade(Pf0);\n vec4 n_z = mix(vec4(n000,n100,n010,n110),vec4(n001,n101,n011,n111),fade_xyz.z);\n vec2 n_yz = mix(n_z.xy,n_z.zw,fade_xyz.y);\n float n_xyz = mix(n_yz.x,n_yz.y,fade_xyz.x);\n return 2.2 * n_xyz;\n}\n`;\n\nconst Beams = ({\n beamWidth = 2,\n beamHeight = 15,\n beamNumber = 12,\n lightColor = '#ffffff',\n speed = 2,\n noiseIntensity = 1.75,\n scale = 0.2,\n rotation = 0\n}) => {\n const meshRef = useRef(null);\n const beamMaterial = useMemo(\n () =>\n extendMaterial(THREE.MeshStandardMaterial, {\n header: `\n varying vec3 vEye;\n varying float vNoise;\n varying vec2 vUv;\n varying vec3 vPosition;\n uniform float time;\n uniform float uSpeed;\n uniform float uNoiseIntensity;\n uniform float uScale;\n ${noise}`,\n vertexHeader: `\n float getPos(vec3 pos) {\n vec3 noisePos =\n vec3(pos.x * 0., pos.y - uv.y, pos.z + time * uSpeed * 3.) * uScale;\n return cnoise(noisePos);\n }\n vec3 getCurrentPos(vec3 pos) {\n vec3 newpos = pos;\n newpos.z += getPos(pos);\n return newpos;\n }\n vec3 getNormal(vec3 pos) {\n vec3 curpos = getCurrentPos(pos);\n vec3 nextposX = getCurrentPos(pos + vec3(0.01, 0.0, 0.0));\n vec3 nextposZ = getCurrentPos(pos + vec3(0.0, -0.01, 0.0));\n vec3 tangentX = normalize(nextposX - curpos);\n vec3 tangentZ = normalize(nextposZ - curpos);\n return normalize(cross(tangentZ, tangentX));\n }`,\n fragmentHeader: '',\n vertex: {\n '#include ': `transformed.z += getPos(transformed.xyz);`,\n '#include ': `objectNormal = getNormal(position.xyz);`\n },\n fragment: {\n '#include ': `\n float randomNoise = noise(gl_FragCoord.xy);\n gl_FragColor.rgb -= randomNoise / 15. * uNoiseIntensity;`\n },\n material: { fog: true },\n uniforms: {\n diffuse: new THREE.Color(...hexToNormalizedRGB('#000000')),\n time: { shared: true, mixed: true, linked: true, value: 0 },\n roughness: 0.3,\n metalness: 0.3,\n uSpeed: { shared: true, mixed: true, linked: true, value: speed },\n envMapIntensity: 10,\n uNoiseIntensity: noiseIntensity,\n uScale: scale\n }\n }),\n [speed, noiseIntensity, scale]\n );\n\n return (\n \n \n \n \n \n \n \n \n \n );\n};\n\nfunction createStackedPlanesBufferGeometry(n, width, height, spacing, heightSegments) {\n const geometry = new THREE.BufferGeometry();\n const numVertices = n * (heightSegments + 1) * 2;\n const numFaces = n * heightSegments * 2;\n const positions = new Float32Array(numVertices * 3);\n const indices = new Uint32Array(numFaces * 3);\n const uvs = new Float32Array(numVertices * 2);\n\n let vertexOffset = 0;\n let indexOffset = 0;\n let uvOffset = 0;\n const totalWidth = n * width + (n - 1) * spacing;\n const xOffsetBase = -totalWidth / 2;\n\n for (let i = 0; i < n; i++) {\n const xOffset = xOffsetBase + i * (width + spacing);\n const uvXOffset = Math.random() * 300;\n const uvYOffset = Math.random() * 300;\n\n for (let j = 0; j <= heightSegments; j++) {\n const y = height * (j / heightSegments - 0.5);\n const v0 = [xOffset, y, 0];\n const v1 = [xOffset + width, y, 0];\n positions.set([...v0, ...v1], vertexOffset * 3);\n\n const uvY = j / heightSegments;\n uvs.set([uvXOffset, uvY + uvYOffset, uvXOffset + 1, uvY + uvYOffset], uvOffset);\n\n if (j < heightSegments) {\n const a = vertexOffset,\n b = vertexOffset + 1,\n c = vertexOffset + 2,\n d = vertexOffset + 3;\n indices.set([a, b, c, c, b, d], indexOffset);\n indexOffset += 6;\n }\n vertexOffset += 2;\n uvOffset += 4;\n }\n }\n\n geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));\n geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));\n geometry.setIndex(new THREE.BufferAttribute(indices, 1));\n geometry.computeVertexNormals();\n return geometry;\n}\n\nconst MergedPlanes = forwardRef(({ material, width, count, height }, ref) => {\n const mesh = useRef(null);\n useImperativeHandle(ref, () => mesh.current);\n const geometry = useMemo(\n () => createStackedPlanesBufferGeometry(count, width, height, 0, 100),\n [count, width, height]\n );\n useFrame((_, delta) => {\n mesh.current.material.uniforms.time.value += 0.1 * delta;\n });\n return ;\n});\nMergedPlanes.displayName = 'MergedPlanes';\n\nconst PlaneNoise = forwardRef((props, ref) => (\n \n));\nPlaneNoise.displayName = 'PlaneNoise';\n\nconst DirLight = ({ position, color }) => {\n const dir = useRef(null);\n useEffect(() => {\n if (!dir.current) return;\n const cam = dir.current.shadow.camera;\n if (!cam) return;\n cam.top = 24;\n cam.bottom = -24;\n cam.left = -24;\n cam.right = 24;\n cam.far = 64;\n dir.current.shadow.bias = -0.004;\n }, []);\n return ;\n};\n\nexport default Beams;\n" } ], "registryDependencies": [], "dependencies": [ "three@^0.167.1", "@react-three/fiber@^9.3.0", "@react-three/drei@^10.7.4" ] } ================================================ FILE: public/r/Beams-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Beams-JS-TW", "title": "Beams", "description": "Crossing animated ribbons with customizable properties.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Beams/Beams.jsx", "content": "/* eslint-disable react/no-unknown-property */\nimport { forwardRef, useImperativeHandle, useEffect, useRef, useMemo } from 'react';\n\nimport * as THREE from 'three';\n\nimport { Canvas, useFrame } from '@react-three/fiber';\nimport { PerspectiveCamera } from '@react-three/drei';\nimport { degToRad } from 'three/src/math/MathUtils.js';\n\nfunction extendMaterial(BaseMaterial, cfg) {\n const physical = THREE.ShaderLib.physical;\n const { vertexShader: baseVert, fragmentShader: baseFrag, uniforms: baseUniforms } = physical;\n const baseDefines = physical.defines ?? {};\n\n const uniforms = THREE.UniformsUtils.clone(baseUniforms);\n\n const defaults = new BaseMaterial(cfg.material || {});\n\n if (defaults.color) uniforms.diffuse.value = defaults.color;\n if ('roughness' in defaults) uniforms.roughness.value = defaults.roughness;\n if ('metalness' in defaults) uniforms.metalness.value = defaults.metalness;\n if ('envMap' in defaults) uniforms.envMap.value = defaults.envMap;\n if ('envMapIntensity' in defaults) uniforms.envMapIntensity.value = defaults.envMapIntensity;\n\n Object.entries(cfg.uniforms ?? {}).forEach(([key, u]) => {\n uniforms[key] = u !== null && typeof u === 'object' && 'value' in u ? u : { value: u };\n });\n\n let vert = `${cfg.header}\\n${cfg.vertexHeader ?? ''}\\n${baseVert}`;\n let frag = `${cfg.header}\\n${cfg.fragmentHeader ?? ''}\\n${baseFrag}`;\n\n for (const [inc, code] of Object.entries(cfg.vertex ?? {})) {\n vert = vert.replace(inc, `${inc}\\n${code}`);\n }\n for (const [inc, code] of Object.entries(cfg.fragment ?? {})) {\n frag = frag.replace(inc, `${inc}\\n${code}`);\n }\n\n const mat = new THREE.ShaderMaterial({\n defines: { ...baseDefines },\n uniforms,\n vertexShader: vert,\n fragmentShader: frag,\n lights: true,\n fog: !!cfg.material?.fog\n });\n\n return mat;\n}\n\nconst CanvasWrapper = ({ children }) => (\n \n {children}\n \n);\n\nconst hexToNormalizedRGB = hex => {\n const clean = hex.replace('#', '');\n const r = parseInt(clean.substring(0, 2), 16);\n const g = parseInt(clean.substring(2, 4), 16);\n const b = parseInt(clean.substring(4, 6), 16);\n return [r / 255, g / 255, b / 255];\n};\n\nconst noise = `\nfloat random (in vec2 st) {\n return fract(sin(dot(st.xy,\n vec2(12.9898,78.233)))*\n 43758.5453123);\n}\nfloat noise (in vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = random(i);\n float b = random(i + vec2(1.0, 0.0));\n float c = random(i + vec2(0.0, 1.0));\n float d = random(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n return mix(a, b, u.x) +\n (c - a)* u.y * (1.0 - u.x) +\n (d - b) * u.x * u.y;\n}\nvec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\nvec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\nvec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}\nfloat cnoise(vec3 P){\n vec3 Pi0 = floor(P);\n vec3 Pi1 = Pi0 + vec3(1.0);\n Pi0 = mod(Pi0, 289.0);\n Pi1 = mod(Pi1, 289.0);\n vec3 Pf0 = fract(P);\n vec3 Pf1 = Pf0 - vec3(1.0);\n vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);\n vec4 iy = vec4(Pi0.yy, Pi1.yy);\n vec4 iz0 = Pi0.zzzz;\n vec4 iz1 = Pi1.zzzz;\n vec4 ixy = permute(permute(ix) + iy);\n vec4 ixy0 = permute(ixy + iz0);\n vec4 ixy1 = permute(ixy + iz1);\n vec4 gx0 = ixy0 / 7.0;\n vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;\n gx0 = fract(gx0);\n vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);\n vec4 sz0 = step(gz0, vec4(0.0));\n gx0 -= sz0 * (step(0.0, gx0) - 0.5);\n gy0 -= sz0 * (step(0.0, gy0) - 0.5);\n vec4 gx1 = ixy1 / 7.0;\n vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;\n gx1 = fract(gx1);\n vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);\n vec4 sz1 = step(gz1, vec4(0.0));\n gx1 -= sz1 * (step(0.0, gx1) - 0.5);\n gy1 -= sz1 * (step(0.0, gy1) - 0.5);\n vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);\n vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);\n vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);\n vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);\n vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);\n vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);\n vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);\n vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);\n vec4 norm0 = taylorInvSqrt(vec4(dot(g000,g000),dot(g010,g010),dot(g100,g100),dot(g110,g110)));\n g000 *= norm0.x; g010 *= norm0.y; g100 *= norm0.z; g110 *= norm0.w;\n vec4 norm1 = taylorInvSqrt(vec4(dot(g001,g001),dot(g011,g011),dot(g101,g101),dot(g111,g111)));\n g001 *= norm1.x; g011 *= norm1.y; g101 *= norm1.z; g111 *= norm1.w;\n float n000 = dot(g000, Pf0);\n float n100 = dot(g100, vec3(Pf1.x,Pf0.yz));\n float n010 = dot(g010, vec3(Pf0.x,Pf1.y,Pf0.z));\n float n110 = dot(g110, vec3(Pf1.xy,Pf0.z));\n float n001 = dot(g001, vec3(Pf0.xy,Pf1.z));\n float n101 = dot(g101, vec3(Pf1.x,Pf0.y,Pf1.z));\n float n011 = dot(g011, vec3(Pf0.x,Pf1.yz));\n float n111 = dot(g111, Pf1);\n vec3 fade_xyz = fade(Pf0);\n vec4 n_z = mix(vec4(n000,n100,n010,n110),vec4(n001,n101,n011,n111),fade_xyz.z);\n vec2 n_yz = mix(n_z.xy,n_z.zw,fade_xyz.y);\n float n_xyz = mix(n_yz.x,n_yz.y,fade_xyz.x);\n return 2.2 * n_xyz;\n}\n`;\n\nconst Beams = ({\n beamWidth = 2,\n beamHeight = 15,\n beamNumber = 12,\n lightColor = '#ffffff',\n speed = 2,\n noiseIntensity = 1.75,\n scale = 0.2,\n rotation = 0\n}) => {\n const meshRef = useRef(null);\n const beamMaterial = useMemo(\n () =>\n extendMaterial(THREE.MeshStandardMaterial, {\n header: `\n varying vec3 vEye;\n varying float vNoise;\n varying vec2 vUv;\n varying vec3 vPosition;\n uniform float time;\n uniform float uSpeed;\n uniform float uNoiseIntensity;\n uniform float uScale;\n ${noise}`,\n vertexHeader: `\n float getPos(vec3 pos) {\n vec3 noisePos =\n vec3(pos.x * 0., pos.y - uv.y, pos.z + time * uSpeed * 3.) * uScale;\n return cnoise(noisePos);\n }\n vec3 getCurrentPos(vec3 pos) {\n vec3 newpos = pos;\n newpos.z += getPos(pos);\n return newpos;\n }\n vec3 getNormal(vec3 pos) {\n vec3 curpos = getCurrentPos(pos);\n vec3 nextposX = getCurrentPos(pos + vec3(0.01, 0.0, 0.0));\n vec3 nextposZ = getCurrentPos(pos + vec3(0.0, -0.01, 0.0));\n vec3 tangentX = normalize(nextposX - curpos);\n vec3 tangentZ = normalize(nextposZ - curpos);\n return normalize(cross(tangentZ, tangentX));\n }`,\n fragmentHeader: '',\n vertex: {\n '#include ': `transformed.z += getPos(transformed.xyz);`,\n '#include ': `objectNormal = getNormal(position.xyz);`\n },\n fragment: {\n '#include ': `\n float randomNoise = noise(gl_FragCoord.xy);\n gl_FragColor.rgb -= randomNoise / 15. * uNoiseIntensity;`\n },\n material: { fog: true },\n uniforms: {\n diffuse: new THREE.Color(...hexToNormalizedRGB('#000000')),\n time: { shared: true, mixed: true, linked: true, value: 0 },\n roughness: 0.3,\n metalness: 0.3,\n uSpeed: { shared: true, mixed: true, linked: true, value: speed },\n envMapIntensity: 10,\n uNoiseIntensity: noiseIntensity,\n uScale: scale\n }\n }),\n [speed, noiseIntensity, scale]\n );\n\n return (\n \n \n \n \n \n \n \n \n \n );\n};\n\nfunction createStackedPlanesBufferGeometry(n, width, height, spacing, heightSegments) {\n const geometry = new THREE.BufferGeometry();\n const numVertices = n * (heightSegments + 1) * 2;\n const numFaces = n * heightSegments * 2;\n const positions = new Float32Array(numVertices * 3);\n const indices = new Uint32Array(numFaces * 3);\n const uvs = new Float32Array(numVertices * 2);\n\n let vertexOffset = 0;\n let indexOffset = 0;\n let uvOffset = 0;\n const totalWidth = n * width + (n - 1) * spacing;\n const xOffsetBase = -totalWidth / 2;\n\n for (let i = 0; i < n; i++) {\n const xOffset = xOffsetBase + i * (width + spacing);\n const uvXOffset = Math.random() * 300;\n const uvYOffset = Math.random() * 300;\n\n for (let j = 0; j <= heightSegments; j++) {\n const y = height * (j / heightSegments - 0.5);\n const v0 = [xOffset, y, 0];\n const v1 = [xOffset + width, y, 0];\n positions.set([...v0, ...v1], vertexOffset * 3);\n\n const uvY = j / heightSegments;\n uvs.set([uvXOffset, uvY + uvYOffset, uvXOffset + 1, uvY + uvYOffset], uvOffset);\n\n if (j < heightSegments) {\n const a = vertexOffset,\n b = vertexOffset + 1,\n c = vertexOffset + 2,\n d = vertexOffset + 3;\n indices.set([a, b, c, c, b, d], indexOffset);\n indexOffset += 6;\n }\n vertexOffset += 2;\n uvOffset += 4;\n }\n }\n\n geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));\n geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));\n geometry.setIndex(new THREE.BufferAttribute(indices, 1));\n geometry.computeVertexNormals();\n return geometry;\n}\n\nconst MergedPlanes = forwardRef(({ material, width, count, height }, ref) => {\n const mesh = useRef(null);\n useImperativeHandle(ref, () => mesh.current);\n const geometry = useMemo(\n () => createStackedPlanesBufferGeometry(count, width, height, 0, 100),\n [count, width, height]\n );\n useFrame((_, delta) => {\n mesh.current.material.uniforms.time.value += 0.1 * delta;\n });\n return ;\n});\nMergedPlanes.displayName = 'MergedPlanes';\n\nconst PlaneNoise = forwardRef((props, ref) => (\n \n));\nPlaneNoise.displayName = 'PlaneNoise';\n\nconst DirLight = ({ position, color }) => {\n const dir = useRef(null);\n useEffect(() => {\n if (!dir.current) return;\n const cam = dir.current.shadow.camera;\n if (!cam) return;\n cam.top = 24;\n cam.bottom = -24;\n cam.left = -24;\n cam.right = 24;\n cam.far = 64;\n dir.current.shadow.bias = -0.004;\n }, []);\n return ;\n};\n\nexport default Beams;\n" } ], "registryDependencies": [], "dependencies": [ "three@^0.167.1", "@react-three/fiber@^9.3.0", "@react-three/drei@^10.7.4" ] } ================================================ FILE: public/r/Beams-TS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Beams-TS-CSS", "title": "Beams", "description": "Crossing animated ribbons with customizable properties.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Beams/Beams.css", "content": ".beams-container {\n position: relative;\n width: 100%;\n height: 100%;\n}\n" }, { "type": "registry:component", "path": "Beams/Beams.tsx", "content": "import { forwardRef, useImperativeHandle, useEffect, useRef, useMemo, FC, ReactNode } from 'react';\n\nimport * as THREE from 'three';\n\nimport { Canvas, useFrame } from '@react-three/fiber';\nimport { PerspectiveCamera } from '@react-three/drei';\nimport { degToRad } from 'three/src/math/MathUtils.js';\n\nimport './Beams.css';\n\ntype UniformValue = THREE.IUniform | unknown;\n\ninterface ExtendMaterialConfig {\n header: string;\n vertexHeader?: string;\n fragmentHeader?: string;\n material?: THREE.MeshPhysicalMaterialParameters & { fog?: boolean };\n uniforms?: Record;\n vertex?: Record;\n fragment?: Record;\n}\n\ntype ShaderWithDefines = THREE.ShaderLibShader & {\n defines?: Record;\n};\n\nfunction extendMaterial(\n BaseMaterial: new (params?: THREE.MaterialParameters) => T,\n cfg: ExtendMaterialConfig\n): THREE.ShaderMaterial {\n const physical = THREE.ShaderLib.physical as ShaderWithDefines;\n const { vertexShader: baseVert, fragmentShader: baseFrag, uniforms: baseUniforms } = physical;\n const baseDefines = physical.defines ?? {};\n\n const uniforms: Record = THREE.UniformsUtils.clone(baseUniforms);\n\n const defaults = new BaseMaterial(cfg.material || {}) as T & {\n color?: THREE.Color;\n roughness?: number;\n metalness?: number;\n envMap?: THREE.Texture;\n envMapIntensity?: number;\n };\n\n if (defaults.color) uniforms.diffuse.value = defaults.color;\n if ('roughness' in defaults) uniforms.roughness.value = defaults.roughness;\n if ('metalness' in defaults) uniforms.metalness.value = defaults.metalness;\n if ('envMap' in defaults) uniforms.envMap.value = defaults.envMap;\n if ('envMapIntensity' in defaults) uniforms.envMapIntensity.value = defaults.envMapIntensity;\n\n Object.entries(cfg.uniforms ?? {}).forEach(([key, u]) => {\n uniforms[key] =\n u !== null && typeof u === 'object' && 'value' in u\n ? (u as THREE.IUniform)\n : ({ value: u } as THREE.IUniform);\n });\n\n let vert = `${cfg.header}\\n${cfg.vertexHeader ?? ''}\\n${baseVert}`;\n let frag = `${cfg.header}\\n${cfg.fragmentHeader ?? ''}\\n${baseFrag}`;\n\n for (const [inc, code] of Object.entries(cfg.vertex ?? {})) {\n vert = vert.replace(inc, `${inc}\\n${code}`);\n }\n for (const [inc, code] of Object.entries(cfg.fragment ?? {})) {\n frag = frag.replace(inc, `${inc}\\n${code}`);\n }\n\n const mat = new THREE.ShaderMaterial({\n defines: { ...baseDefines },\n uniforms,\n vertexShader: vert,\n fragmentShader: frag,\n lights: true,\n fog: !!cfg.material?.fog\n });\n\n return mat;\n}\n\nconst CanvasWrapper: FC<{ children: ReactNode }> = ({ children }) => (\n \n {children}\n \n);\n\nconst hexToNormalizedRGB = (hex: string): [number, number, number] => {\n const clean = hex.replace('#', '');\n const r = parseInt(clean.substring(0, 2), 16);\n const g = parseInt(clean.substring(2, 4), 16);\n const b = parseInt(clean.substring(4, 6), 16);\n return [r / 255, g / 255, b / 255];\n};\n\nconst noise = `\nfloat random (in vec2 st) {\n return fract(sin(dot(st.xy,\n vec2(12.9898,78.233)))*\n 43758.5453123);\n}\nfloat noise (in vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = random(i);\n float b = random(i + vec2(1.0, 0.0));\n float c = random(i + vec2(0.0, 1.0));\n float d = random(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n return mix(a, b, u.x) +\n (c - a)* u.y * (1.0 - u.x) +\n (d - b) * u.x * u.y;\n}\nvec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\nvec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\nvec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}\nfloat cnoise(vec3 P){\n vec3 Pi0 = floor(P);\n vec3 Pi1 = Pi0 + vec3(1.0);\n Pi0 = mod(Pi0, 289.0);\n Pi1 = mod(Pi1, 289.0);\n vec3 Pf0 = fract(P);\n vec3 Pf1 = Pf0 - vec3(1.0);\n vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);\n vec4 iy = vec4(Pi0.yy, Pi1.yy);\n vec4 iz0 = Pi0.zzzz;\n vec4 iz1 = Pi1.zzzz;\n vec4 ixy = permute(permute(ix) + iy);\n vec4 ixy0 = permute(ixy + iz0);\n vec4 ixy1 = permute(ixy + iz1);\n vec4 gx0 = ixy0 / 7.0;\n vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;\n gx0 = fract(gx0);\n vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);\n vec4 sz0 = step(gz0, vec4(0.0));\n gx0 -= sz0 * (step(0.0, gx0) - 0.5);\n gy0 -= sz0 * (step(0.0, gy0) - 0.5);\n vec4 gx1 = ixy1 / 7.0;\n vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;\n gx1 = fract(gx1);\n vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);\n vec4 sz1 = step(gz1, vec4(0.0));\n gx1 -= sz1 * (step(0.0, gx1) - 0.5);\n gy1 -= sz1 * (step(0.0, gy1) - 0.5);\n vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);\n vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);\n vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);\n vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);\n vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);\n vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);\n vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);\n vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);\n vec4 norm0 = taylorInvSqrt(vec4(dot(g000,g000),dot(g010,g010),dot(g100,g100),dot(g110,g110)));\n g000 *= norm0.x; g010 *= norm0.y; g100 *= norm0.z; g110 *= norm0.w;\n vec4 norm1 = taylorInvSqrt(vec4(dot(g001,g001),dot(g011,g011),dot(g101,g101),dot(g111,g111)));\n g001 *= norm1.x; g011 *= norm1.y; g101 *= norm1.z; g111 *= norm1.w;\n float n000 = dot(g000, Pf0);\n float n100 = dot(g100, vec3(Pf1.x,Pf0.yz));\n float n010 = dot(g010, vec3(Pf0.x,Pf1.y,Pf0.z));\n float n110 = dot(g110, vec3(Pf1.xy,Pf0.z));\n float n001 = dot(g001, vec3(Pf0.xy,Pf1.z));\n float n101 = dot(g101, vec3(Pf1.x,Pf0.y,Pf1.z));\n float n011 = dot(g011, vec3(Pf0.x,Pf1.yz));\n float n111 = dot(g111, Pf1);\n vec3 fade_xyz = fade(Pf0);\n vec4 n_z = mix(vec4(n000,n100,n010,n110),vec4(n001,n101,n011,n111),fade_xyz.z);\n vec2 n_yz = mix(n_z.xy,n_z.zw,fade_xyz.y);\n float n_xyz = mix(n_yz.x,n_yz.y,fade_xyz.x);\n return 2.2 * n_xyz;\n}\n`;\n\ninterface BeamsProps {\n beamWidth?: number;\n beamHeight?: number;\n beamNumber?: number;\n lightColor?: string;\n speed?: number;\n noiseIntensity?: number;\n scale?: number;\n rotation?: number;\n}\n\nconst Beams: FC = ({\n beamWidth = 2,\n beamHeight = 15,\n beamNumber = 12,\n lightColor = '#ffffff',\n speed = 2,\n noiseIntensity = 1.75,\n scale = 0.2,\n rotation = 0\n}) => {\n const meshRef = useRef>(null!);\n\n const beamMaterial = useMemo(\n () =>\n extendMaterial(THREE.MeshStandardMaterial, {\n header: `\n varying vec3 vEye;\n varying float vNoise;\n varying vec2 vUv;\n varying vec3 vPosition;\n uniform float time;\n uniform float uSpeed;\n uniform float uNoiseIntensity;\n uniform float uScale;\n ${noise}`,\n vertexHeader: `\n float getPos(vec3 pos) {\n vec3 noisePos =\n vec3(pos.x * 0., pos.y - uv.y, pos.z + time * uSpeed * 3.) * uScale;\n return cnoise(noisePos);\n }\n vec3 getCurrentPos(vec3 pos) {\n vec3 newpos = pos;\n newpos.z += getPos(pos);\n return newpos;\n }\n vec3 getNormal(vec3 pos) {\n vec3 curpos = getCurrentPos(pos);\n vec3 nextposX = getCurrentPos(pos + vec3(0.01, 0.0, 0.0));\n vec3 nextposZ = getCurrentPos(pos + vec3(0.0, -0.01, 0.0));\n vec3 tangentX = normalize(nextposX - curpos);\n vec3 tangentZ = normalize(nextposZ - curpos);\n return normalize(cross(tangentZ, tangentX));\n }`,\n fragmentHeader: '',\n vertex: {\n '#include ': `transformed.z += getPos(transformed.xyz);`,\n '#include ': `objectNormal = getNormal(position.xyz);`\n },\n fragment: {\n '#include ': `\n float randomNoise = noise(gl_FragCoord.xy);\n gl_FragColor.rgb -= randomNoise / 15. * uNoiseIntensity;`\n },\n material: { fog: true },\n uniforms: {\n diffuse: new THREE.Color(...hexToNormalizedRGB('#000000')),\n time: { shared: true, mixed: true, linked: true, value: 0 },\n roughness: 0.3,\n metalness: 0.3,\n uSpeed: { shared: true, mixed: true, linked: true, value: speed },\n envMapIntensity: 10,\n uNoiseIntensity: noiseIntensity,\n uScale: scale\n }\n }),\n [speed, noiseIntensity, scale]\n );\n\n return (\n \n \n \n \n \n \n \n \n \n );\n};\n\nfunction createStackedPlanesBufferGeometry(\n n: number,\n width: number,\n height: number,\n spacing: number,\n heightSegments: number\n): THREE.BufferGeometry {\n const geometry = new THREE.BufferGeometry();\n const numVertices = n * (heightSegments + 1) * 2;\n const numFaces = n * heightSegments * 2;\n const positions = new Float32Array(numVertices * 3);\n const indices = new Uint32Array(numFaces * 3);\n const uvs = new Float32Array(numVertices * 2);\n\n let vertexOffset = 0;\n let indexOffset = 0;\n let uvOffset = 0;\n const totalWidth = n * width + (n - 1) * spacing;\n const xOffsetBase = -totalWidth / 2;\n\n for (let i = 0; i < n; i++) {\n const xOffset = xOffsetBase + i * (width + spacing);\n const uvXOffset = Math.random() * 300;\n const uvYOffset = Math.random() * 300;\n\n for (let j = 0; j <= heightSegments; j++) {\n const y = height * (j / heightSegments - 0.5);\n const v0 = [xOffset, y, 0];\n const v1 = [xOffset + width, y, 0];\n positions.set([...v0, ...v1], vertexOffset * 3);\n\n const uvY = j / heightSegments;\n uvs.set([uvXOffset, uvY + uvYOffset, uvXOffset + 1, uvY + uvYOffset], uvOffset);\n\n if (j < heightSegments) {\n const a = vertexOffset,\n b = vertexOffset + 1,\n c = vertexOffset + 2,\n d = vertexOffset + 3;\n indices.set([a, b, c, c, b, d], indexOffset);\n indexOffset += 6;\n }\n vertexOffset += 2;\n uvOffset += 4;\n }\n }\n\n geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));\n geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));\n geometry.setIndex(new THREE.BufferAttribute(indices, 1));\n geometry.computeVertexNormals();\n return geometry;\n}\n\nconst MergedPlanes = forwardRef<\n THREE.Mesh,\n {\n material: THREE.ShaderMaterial;\n width: number;\n count: number;\n height: number;\n }\n>(({ material, width, count, height }, ref) => {\n const mesh = useRef>(null!);\n useImperativeHandle(ref, () => mesh.current);\n const geometry = useMemo(\n () => createStackedPlanesBufferGeometry(count, width, height, 0, 100),\n [count, width, height]\n );\n useFrame((_, delta) => {\n mesh.current.material.uniforms.time.value += 0.1 * delta;\n });\n return ;\n});\nMergedPlanes.displayName = 'MergedPlanes';\n\nconst PlaneNoise = forwardRef<\n THREE.Mesh,\n {\n material: THREE.ShaderMaterial;\n width: number;\n count: number;\n height: number;\n }\n>((props, ref) => (\n \n));\nPlaneNoise.displayName = 'PlaneNoise';\n\nconst DirLight: FC<{ position: [number, number, number]; color: string }> = ({ position, color }) => {\n const dir = useRef(null!);\n useEffect(() => {\n if (!dir.current) return;\n const cam = dir.current.shadow.camera as THREE.Camera & {\n top: number;\n bottom: number;\n left: number;\n right: number;\n far: number;\n };\n cam.top = 24;\n cam.bottom = -24;\n cam.left = -24;\n cam.right = 24;\n cam.far = 64;\n dir.current.shadow.bias = -0.004;\n }, []);\n return ;\n};\n\nexport default Beams;\n" } ], "registryDependencies": [], "dependencies": [ "three@^0.167.1", "@react-three/fiber@^9.3.0", "@react-three/drei@^10.7.4" ] } ================================================ FILE: public/r/Beams-TS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "Beams-TS-TW", "title": "Beams", "description": "Crossing animated ribbons with customizable properties.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "Beams/Beams.tsx", "content": "import { forwardRef, useImperativeHandle, useEffect, useRef, useMemo, FC, ReactNode } from 'react';\n\nimport * as THREE from 'three';\n\nimport { Canvas, useFrame } from '@react-three/fiber';\nimport { PerspectiveCamera } from '@react-three/drei';\nimport { degToRad } from 'three/src/math/MathUtils.js';\n\ntype UniformValue = THREE.IUniform | unknown;\n\ninterface ExtendMaterialConfig {\n header: string;\n vertexHeader?: string;\n fragmentHeader?: string;\n material?: THREE.MeshPhysicalMaterialParameters & { fog?: boolean };\n uniforms?: Record;\n vertex?: Record;\n fragment?: Record;\n}\n\ntype ShaderWithDefines = THREE.ShaderLibShader & {\n defines?: Record;\n};\n\nfunction extendMaterial(\n BaseMaterial: new (params?: THREE.MaterialParameters) => T,\n cfg: ExtendMaterialConfig\n): THREE.ShaderMaterial {\n const physical = THREE.ShaderLib.physical as ShaderWithDefines;\n const { vertexShader: baseVert, fragmentShader: baseFrag, uniforms: baseUniforms } = physical;\n const baseDefines = physical.defines ?? {};\n\n const uniforms: Record = THREE.UniformsUtils.clone(baseUniforms);\n\n const defaults = new BaseMaterial(cfg.material || {}) as T & {\n color?: THREE.Color;\n roughness?: number;\n metalness?: number;\n envMap?: THREE.Texture;\n envMapIntensity?: number;\n };\n\n if (defaults.color) uniforms.diffuse.value = defaults.color;\n if ('roughness' in defaults) uniforms.roughness.value = defaults.roughness;\n if ('metalness' in defaults) uniforms.metalness.value = defaults.metalness;\n if ('envMap' in defaults) uniforms.envMap.value = defaults.envMap;\n if ('envMapIntensity' in defaults) uniforms.envMapIntensity.value = defaults.envMapIntensity;\n\n Object.entries(cfg.uniforms ?? {}).forEach(([key, u]) => {\n uniforms[key] =\n u !== null && typeof u === 'object' && 'value' in u\n ? (u as THREE.IUniform)\n : ({ value: u } as THREE.IUniform);\n });\n\n let vert = `${cfg.header}\\n${cfg.vertexHeader ?? ''}\\n${baseVert}`;\n let frag = `${cfg.header}\\n${cfg.fragmentHeader ?? ''}\\n${baseFrag}`;\n\n for (const [inc, code] of Object.entries(cfg.vertex ?? {})) {\n vert = vert.replace(inc, `${inc}\\n${code}`);\n }\n for (const [inc, code] of Object.entries(cfg.fragment ?? {})) {\n frag = frag.replace(inc, `${inc}\\n${code}`);\n }\n\n const mat = new THREE.ShaderMaterial({\n defines: { ...baseDefines },\n uniforms,\n vertexShader: vert,\n fragmentShader: frag,\n lights: true,\n fog: !!cfg.material?.fog\n });\n\n return mat;\n}\n\nconst CanvasWrapper: FC<{ children: ReactNode }> = ({ children }) => (\n \n {children}\n \n);\n\nconst hexToNormalizedRGB = (hex: string): [number, number, number] => {\n const clean = hex.replace('#', '');\n const r = parseInt(clean.substring(0, 2), 16);\n const g = parseInt(clean.substring(2, 4), 16);\n const b = parseInt(clean.substring(4, 6), 16);\n return [r / 255, g / 255, b / 255];\n};\n\nconst noise = `\nfloat random (in vec2 st) {\n return fract(sin(dot(st.xy,\n vec2(12.9898,78.233)))*\n 43758.5453123);\n}\nfloat noise (in vec2 st) {\n vec2 i = floor(st);\n vec2 f = fract(st);\n float a = random(i);\n float b = random(i + vec2(1.0, 0.0));\n float c = random(i + vec2(0.0, 1.0));\n float d = random(i + vec2(1.0, 1.0));\n vec2 u = f * f * (3.0 - 2.0 * f);\n return mix(a, b, u.x) +\n (c - a)* u.y * (1.0 - u.x) +\n (d - b) * u.x * u.y;\n}\nvec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\nvec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\nvec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}\nfloat cnoise(vec3 P){\n vec3 Pi0 = floor(P);\n vec3 Pi1 = Pi0 + vec3(1.0);\n Pi0 = mod(Pi0, 289.0);\n Pi1 = mod(Pi1, 289.0);\n vec3 Pf0 = fract(P);\n vec3 Pf1 = Pf0 - vec3(1.0);\n vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);\n vec4 iy = vec4(Pi0.yy, Pi1.yy);\n vec4 iz0 = Pi0.zzzz;\n vec4 iz1 = Pi1.zzzz;\n vec4 ixy = permute(permute(ix) + iy);\n vec4 ixy0 = permute(ixy + iz0);\n vec4 ixy1 = permute(ixy + iz1);\n vec4 gx0 = ixy0 / 7.0;\n vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;\n gx0 = fract(gx0);\n vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);\n vec4 sz0 = step(gz0, vec4(0.0));\n gx0 -= sz0 * (step(0.0, gx0) - 0.5);\n gy0 -= sz0 * (step(0.0, gy0) - 0.5);\n vec4 gx1 = ixy1 / 7.0;\n vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;\n gx1 = fract(gx1);\n vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);\n vec4 sz1 = step(gz1, vec4(0.0));\n gx1 -= sz1 * (step(0.0, gx1) - 0.5);\n gy1 -= sz1 * (step(0.0, gy1) - 0.5);\n vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);\n vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);\n vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);\n vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);\n vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);\n vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);\n vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);\n vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);\n vec4 norm0 = taylorInvSqrt(vec4(dot(g000,g000),dot(g010,g010),dot(g100,g100),dot(g110,g110)));\n g000 *= norm0.x; g010 *= norm0.y; g100 *= norm0.z; g110 *= norm0.w;\n vec4 norm1 = taylorInvSqrt(vec4(dot(g001,g001),dot(g011,g011),dot(g101,g101),dot(g111,g111)));\n g001 *= norm1.x; g011 *= norm1.y; g101 *= norm1.z; g111 *= norm1.w;\n float n000 = dot(g000, Pf0);\n float n100 = dot(g100, vec3(Pf1.x,Pf0.yz));\n float n010 = dot(g010, vec3(Pf0.x,Pf1.y,Pf0.z));\n float n110 = dot(g110, vec3(Pf1.xy,Pf0.z));\n float n001 = dot(g001, vec3(Pf0.xy,Pf1.z));\n float n101 = dot(g101, vec3(Pf1.x,Pf0.y,Pf1.z));\n float n011 = dot(g011, vec3(Pf0.x,Pf1.yz));\n float n111 = dot(g111, Pf1);\n vec3 fade_xyz = fade(Pf0);\n vec4 n_z = mix(vec4(n000,n100,n010,n110),vec4(n001,n101,n011,n111),fade_xyz.z);\n vec2 n_yz = mix(n_z.xy,n_z.zw,fade_xyz.y);\n float n_xyz = mix(n_yz.x,n_yz.y,fade_xyz.x);\n return 2.2 * n_xyz;\n}\n`;\n\ninterface BeamsProps {\n beamWidth?: number;\n beamHeight?: number;\n beamNumber?: number;\n lightColor?: string;\n speed?: number;\n noiseIntensity?: number;\n scale?: number;\n rotation?: number;\n}\n\nconst Beams: FC = ({\n beamWidth = 2,\n beamHeight = 15,\n beamNumber = 12,\n lightColor = '#ffffff',\n speed = 2,\n noiseIntensity = 1.75,\n scale = 0.2,\n rotation = 0\n}) => {\n const meshRef = useRef>(null!);\n\n const beamMaterial = useMemo(\n () =>\n extendMaterial(THREE.MeshStandardMaterial, {\n header: `\n varying vec3 vEye;\n varying float vNoise;\n varying vec2 vUv;\n varying vec3 vPosition;\n uniform float time;\n uniform float uSpeed;\n uniform float uNoiseIntensity;\n uniform float uScale;\n ${noise}`,\n vertexHeader: `\n float getPos(vec3 pos) {\n vec3 noisePos =\n vec3(pos.x * 0., pos.y - uv.y, pos.z + time * uSpeed * 3.) * uScale;\n return cnoise(noisePos);\n }\n vec3 getCurrentPos(vec3 pos) {\n vec3 newpos = pos;\n newpos.z += getPos(pos);\n return newpos;\n }\n vec3 getNormal(vec3 pos) {\n vec3 curpos = getCurrentPos(pos);\n vec3 nextposX = getCurrentPos(pos + vec3(0.01, 0.0, 0.0));\n vec3 nextposZ = getCurrentPos(pos + vec3(0.0, -0.01, 0.0));\n vec3 tangentX = normalize(nextposX - curpos);\n vec3 tangentZ = normalize(nextposZ - curpos);\n return normalize(cross(tangentZ, tangentX));\n }`,\n fragmentHeader: '',\n vertex: {\n '#include ': `transformed.z += getPos(transformed.xyz);`,\n '#include ': `objectNormal = getNormal(position.xyz);`\n },\n fragment: {\n '#include ': `\n float randomNoise = noise(gl_FragCoord.xy);\n gl_FragColor.rgb -= randomNoise / 15. * uNoiseIntensity;`\n },\n material: { fog: true },\n uniforms: {\n diffuse: new THREE.Color(...hexToNormalizedRGB('#000000')),\n time: { shared: true, mixed: true, linked: true, value: 0 },\n roughness: 0.3,\n metalness: 0.3,\n uSpeed: { shared: true, mixed: true, linked: true, value: speed },\n envMapIntensity: 10,\n uNoiseIntensity: noiseIntensity,\n uScale: scale\n }\n }),\n [speed, noiseIntensity, scale]\n );\n\n return (\n \n \n \n \n \n \n \n \n \n );\n};\n\nfunction createStackedPlanesBufferGeometry(\n n: number,\n width: number,\n height: number,\n spacing: number,\n heightSegments: number\n): THREE.BufferGeometry {\n const geometry = new THREE.BufferGeometry();\n const numVertices = n * (heightSegments + 1) * 2;\n const numFaces = n * heightSegments * 2;\n const positions = new Float32Array(numVertices * 3);\n const indices = new Uint32Array(numFaces * 3);\n const uvs = new Float32Array(numVertices * 2);\n\n let vertexOffset = 0;\n let indexOffset = 0;\n let uvOffset = 0;\n const totalWidth = n * width + (n - 1) * spacing;\n const xOffsetBase = -totalWidth / 2;\n\n for (let i = 0; i < n; i++) {\n const xOffset = xOffsetBase + i * (width + spacing);\n const uvXOffset = Math.random() * 300;\n const uvYOffset = Math.random() * 300;\n\n for (let j = 0; j <= heightSegments; j++) {\n const y = height * (j / heightSegments - 0.5);\n const v0 = [xOffset, y, 0];\n const v1 = [xOffset + width, y, 0];\n positions.set([...v0, ...v1], vertexOffset * 3);\n\n const uvY = j / heightSegments;\n uvs.set([uvXOffset, uvY + uvYOffset, uvXOffset + 1, uvY + uvYOffset], uvOffset);\n\n if (j < heightSegments) {\n const a = vertexOffset,\n b = vertexOffset + 1,\n c = vertexOffset + 2,\n d = vertexOffset + 3;\n indices.set([a, b, c, c, b, d], indexOffset);\n indexOffset += 6;\n }\n vertexOffset += 2;\n uvOffset += 4;\n }\n }\n\n geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));\n geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));\n geometry.setIndex(new THREE.BufferAttribute(indices, 1));\n geometry.computeVertexNormals();\n return geometry;\n}\n\nconst MergedPlanes = forwardRef<\n THREE.Mesh,\n {\n material: THREE.ShaderMaterial;\n width: number;\n count: number;\n height: number;\n }\n>(({ material, width, count, height }, ref) => {\n const mesh = useRef>(null!);\n useImperativeHandle(ref, () => mesh.current);\n const geometry = useMemo(\n () => createStackedPlanesBufferGeometry(count, width, height, 0, 100),\n [count, width, height]\n );\n useFrame((_, delta) => {\n mesh.current.material.uniforms.time.value += 0.1 * delta;\n });\n return ;\n});\nMergedPlanes.displayName = 'MergedPlanes';\n\nconst PlaneNoise = forwardRef<\n THREE.Mesh,\n {\n material: THREE.ShaderMaterial;\n width: number;\n count: number;\n height: number;\n }\n>((props, ref) => (\n \n));\nPlaneNoise.displayName = 'PlaneNoise';\n\nconst DirLight: FC<{ position: [number, number, number]; color: string }> = ({ position, color }) => {\n const dir = useRef(null!);\n useEffect(() => {\n if (!dir.current) return;\n const cam = dir.current.shadow.camera as THREE.Camera & {\n top: number;\n bottom: number;\n left: number;\n right: number;\n far: number;\n };\n cam.top = 24;\n cam.bottom = -24;\n cam.left = -24;\n cam.right = 24;\n cam.far = 64;\n dir.current.shadow.bias = -0.004;\n }, []);\n return ;\n};\n\nexport default Beams;\n" } ], "registryDependencies": [], "dependencies": [ "three@^0.167.1", "@react-three/fiber@^9.3.0", "@react-three/drei@^10.7.4" ] } ================================================ FILE: public/r/BlobCursor-JS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BlobCursor-JS-CSS", "title": "BlobCursor", "description": "Organic blob cursor that smoothly follows the pointer with inertia and elastic morphing.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BlobCursor/BlobCursor.css", "content": ".blob-container {\n position: relative;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n}\n\n.blob-main {\n pointer-events: none;\n position: absolute;\n width: 100%;\n height: 100%;\n overflow: hidden;\n background: transparent;\n user-select: none;\n cursor: default;\n}\n\n.blob {\n position: absolute;\n will-change: transform;\n transform: translate(-50%, -50%);\n}\n\n.inner-dot {\n position: absolute;\n}\n" }, { "type": "registry:component", "path": "BlobCursor/BlobCursor.jsx", "content": "'use client';\n\nimport { useRef, useEffect, useCallback } from 'react';\nimport gsap from 'gsap';\nimport './BlobCursor.css';\n\nexport default function BlobCursor({\n blobType = 'circle',\n fillColor = '#5227FF',\n trailCount = 3,\n sizes = [60, 125, 75],\n innerSizes = [20, 35, 25],\n innerColor = 'rgba(255,255,255,0.8)',\n opacities = [0.6, 0.6, 0.6],\n shadowColor = 'rgba(0,0,0,0.75)',\n shadowBlur = 5,\n shadowOffsetX = 10,\n shadowOffsetY = 10,\n filterId = 'blob',\n filterStdDeviation = 30,\n filterColorMatrixValues = '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 35 -10',\n useFilter = true,\n fastDuration = 0.1,\n slowDuration = 0.5,\n fastEase = 'power3.out',\n slowEase = 'power1.out',\n zIndex = 100\n}) {\n const containerRef = useRef(null);\n const blobsRef = useRef([]);\n\n const updateOffset = useCallback(() => {\n if (!containerRef.current) return { left: 0, top: 0 };\n const rect = containerRef.current.getBoundingClientRect();\n return { left: rect.left, top: rect.top };\n }, []);\n\n const handleMove = useCallback(\n e => {\n const { left, top } = updateOffset();\n const x = 'clientX' in e ? e.clientX : e.touches[0].clientX;\n const y = 'clientY' in e ? e.clientY : e.touches[0].clientY;\n\n blobsRef.current.forEach((el, i) => {\n if (!el) return;\n const isLead = i === 0;\n gsap.to(el, {\n x: x - left,\n y: y - top,\n duration: isLead ? fastDuration : slowDuration,\n ease: isLead ? fastEase : slowEase\n });\n });\n },\n [updateOffset, fastDuration, slowDuration, fastEase, slowEase]\n );\n\n useEffect(() => {\n const onResize = () => updateOffset();\n window.addEventListener('resize', onResize);\n return () => window.removeEventListener('resize', onResize);\n }, [updateOffset]);\n\n return (\n \n {useFilter && (\n \n \n \n \n \n \n )}\n\n
\n {Array.from({ length: trailCount }).map((_, i) => (\n (blobsRef.current[i] = el)}\n className=\"blob\"\n style={{\n width: sizes[i],\n height: sizes[i],\n borderRadius: blobType === 'circle' ? '50%' : '0%',\n backgroundColor: fillColor,\n opacity: opacities[i],\n boxShadow: `${shadowOffsetX}px ${shadowOffsetY}px ${shadowBlur}px 0 ${shadowColor}`\n }}\n >\n \n
\n ))}\n
\n
\n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/BlobCursor-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BlobCursor-JS-TW", "title": "BlobCursor", "description": "Organic blob cursor that smoothly follows the pointer with inertia and elastic morphing.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BlobCursor/BlobCursor.jsx", "content": "'use client';\n\nimport { useRef, useEffect, useCallback } from 'react';\nimport gsap from 'gsap';\n\nexport default function BlobCursor({\n blobType = 'circle',\n fillColor = '#5227FF',\n trailCount = 3,\n sizes = [60, 125, 75],\n innerSizes = [20, 35, 25],\n innerColor = 'rgba(255,255,255,0.8)',\n opacities = [0.6, 0.6, 0.6],\n shadowColor = 'rgba(0,0,0,0.75)',\n shadowBlur = 5,\n shadowOffsetX = 10,\n shadowOffsetY = 10,\n filterId = 'blob',\n filterStdDeviation = 30,\n filterColorMatrixValues = '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 35 -10',\n useFilter = true,\n fastDuration = 0.1,\n slowDuration = 0.5,\n fastEase = 'power3.out',\n slowEase = 'power1.out',\n zIndex = 100\n}) {\n const containerRef = useRef(null);\n const blobsRef = useRef([]);\n\n const updateOffset = useCallback(() => {\n if (!containerRef.current) return { left: 0, top: 0 };\n const rect = containerRef.current.getBoundingClientRect();\n return { left: rect.left, top: rect.top };\n }, []);\n\n const handleMove = useCallback(\n e => {\n const { left, top } = updateOffset();\n const x = 'clientX' in e ? e.clientX : e.touches[0].clientX;\n const y = 'clientY' in e ? e.clientY : e.touches[0].clientY;\n\n blobsRef.current.forEach((el, i) => {\n if (!el) return;\n const isLead = i === 0;\n gsap.to(el, {\n x: x - left,\n y: y - top,\n duration: isLead ? fastDuration : slowDuration,\n ease: isLead ? fastEase : slowEase\n });\n });\n },\n [updateOffset, fastDuration, slowDuration, fastEase, slowEase]\n );\n\n useEffect(() => {\n const onResize = () => updateOffset();\n window.addEventListener('resize', onResize);\n return () => window.removeEventListener('resize', onResize);\n }, [updateOffset]);\n\n return (\n \n {useFilter && (\n \n \n \n \n \n \n )}\n\n \n {Array.from({ length: trailCount }).map((_, i) => (\n (blobsRef.current[i] = el)}\n className=\"absolute will-change-transform transform -translate-x-1/2 -translate-y-1/2\"\n style={{\n width: sizes[i],\n height: sizes[i],\n borderRadius: blobType === 'circle' ? '50%' : '0',\n backgroundColor: fillColor,\n opacity: opacities[i],\n boxShadow: `${shadowOffsetX}px ${shadowOffsetY}px ${shadowBlur}px 0 ${shadowColor}`\n }}\n >\n \n
\n ))}\n
\n
\n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/BlobCursor-TS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BlobCursor-TS-CSS", "title": "BlobCursor", "description": "Organic blob cursor that smoothly follows the pointer with inertia and elastic morphing.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BlobCursor/BlobCursor.css", "content": ".blob-container {\n position: relative;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n}\n\n.blob-main {\n pointer-events: none;\n position: absolute;\n width: 100%;\n height: 100%;\n overflow: hidden;\n background: transparent;\n user-select: none;\n cursor: default;\n}\n\n.blob {\n position: absolute;\n will-change: transform;\n transform: translate(-50%, -50%);\n}\n\n.inner-dot {\n position: absolute;\n}\n" }, { "type": "registry:component", "path": "BlobCursor/BlobCursor.tsx", "content": "'use client';\n\nimport React, { useRef, useEffect, useCallback } from 'react';\nimport gsap from 'gsap';\nimport './BlobCursor.css';\n\nexport interface BlobCursorProps {\n blobType?: 'circle' | 'square';\n fillColor?: string;\n trailCount?: number;\n sizes?: number[];\n innerSizes?: number[];\n innerColor?: string;\n opacities?: number[];\n shadowColor?: string;\n shadowBlur?: number;\n shadowOffsetX?: number;\n shadowOffsetY?: number;\n filterId?: string;\n filterStdDeviation?: number;\n filterColorMatrixValues?: string;\n useFilter?: boolean;\n fastDuration?: number;\n slowDuration?: number;\n fastEase?: string;\n slowEase?: string;\n zIndex?: number;\n}\n\nexport default function BlobCursor({\n blobType = 'circle',\n fillColor = '#5227FF',\n trailCount = 3,\n sizes = [60, 125, 75],\n innerSizes = [20, 35, 25],\n innerColor = 'rgba(255,255,255,0.8)',\n opacities = [0.6, 0.6, 0.6],\n shadowColor = 'rgba(0,0,0,0.75)',\n shadowBlur = 5,\n shadowOffsetX = 10,\n shadowOffsetY = 10,\n filterId = 'blob',\n filterStdDeviation = 30,\n filterColorMatrixValues = '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 35 -10',\n useFilter = true,\n fastDuration = 0.1,\n slowDuration = 0.5,\n fastEase = 'power3.out',\n slowEase = 'power1.out',\n zIndex = 100\n}: BlobCursorProps) {\n const containerRef = useRef(null);\n const blobsRef = useRef<(HTMLDivElement | null)[]>([]);\n\n const updateOffset = useCallback(() => {\n if (!containerRef.current) return { left: 0, top: 0 };\n const rect = containerRef.current.getBoundingClientRect();\n return { left: rect.left, top: rect.top };\n }, []);\n\n const handleMove = useCallback(\n (e: React.MouseEvent | React.TouchEvent) => {\n const { left, top } = updateOffset();\n const x = 'clientX' in e ? e.clientX : e.touches[0].clientX;\n const y = 'clientY' in e ? e.clientY : e.touches[0].clientY;\n\n blobsRef.current.forEach((el, i) => {\n if (!el) return;\n const isLead = i === 0;\n gsap.to(el, {\n x: x - left,\n y: y - top,\n duration: isLead ? fastDuration : slowDuration,\n ease: isLead ? fastEase : slowEase\n });\n });\n },\n [updateOffset, fastDuration, slowDuration, fastEase, slowEase]\n );\n\n useEffect(() => {\n const onResize = () => updateOffset();\n window.addEventListener('resize', onResize);\n return () => window.removeEventListener('resize', onResize);\n }, [updateOffset]);\n\n return (\n \n {useFilter && (\n \n \n \n \n \n \n )}\n\n
\n {Array.from({ length: trailCount }).map((_, i) => (\n {\n blobsRef.current[i] = el;\n }}\n className=\"blob\"\n style={{\n width: sizes[i],\n height: sizes[i],\n borderRadius: blobType === 'circle' ? '50%' : '0%',\n backgroundColor: fillColor,\n opacity: opacities[i],\n boxShadow: `${shadowOffsetX}px ${shadowOffsetY}px ${shadowBlur}px 0 ${shadowColor}`\n }}\n >\n \n
\n ))}\n
\n
\n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/BlobCursor-TS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BlobCursor-TS-TW", "title": "BlobCursor", "description": "Organic blob cursor that smoothly follows the pointer with inertia and elastic morphing.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BlobCursor/BlobCursor.tsx", "content": "'use client';\n\nimport React, { useRef, useEffect, useCallback } from 'react';\nimport gsap from 'gsap';\n\nexport interface BlobCursorProps {\n blobType?: 'circle' | 'square';\n fillColor?: string;\n trailCount?: number;\n sizes?: number[];\n innerSizes?: number[];\n innerColor?: string;\n opacities?: number[];\n shadowColor?: string;\n shadowBlur?: number;\n shadowOffsetX?: number;\n shadowOffsetY?: number;\n filterId?: string;\n filterStdDeviation?: number;\n filterColorMatrixValues?: string;\n useFilter?: boolean;\n fastDuration?: number;\n slowDuration?: number;\n fastEase?: string;\n slowEase?: string;\n zIndex?: number;\n}\n\nexport default function BlobCursor({\n blobType = 'circle',\n fillColor = '#5227FF',\n trailCount = 3,\n sizes = [60, 125, 75],\n innerSizes = [20, 35, 25],\n innerColor = 'rgba(255,255,255,0.8)',\n opacities = [0.6, 0.6, 0.6],\n shadowColor = 'rgba(0,0,0,0.75)',\n shadowBlur = 5,\n shadowOffsetX = 10,\n shadowOffsetY = 10,\n filterId = 'blob',\n filterStdDeviation = 30,\n filterColorMatrixValues = '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 35 -10',\n useFilter = true,\n fastDuration = 0.1,\n slowDuration = 0.5,\n fastEase = 'power3.out',\n slowEase = 'power1.out',\n zIndex = 100\n}: BlobCursorProps) {\n const containerRef = useRef(null);\n const blobsRef = useRef<(HTMLDivElement | null)[]>([]);\n\n const updateOffset = useCallback(() => {\n if (!containerRef.current) return { left: 0, top: 0 };\n const rect = containerRef.current.getBoundingClientRect();\n return { left: rect.left, top: rect.top };\n }, []);\n\n const handleMove = useCallback(\n (e: React.MouseEvent | React.TouchEvent) => {\n const { left, top } = updateOffset();\n const x = 'clientX' in e ? e.clientX : e.touches[0].clientX;\n const y = 'clientY' in e ? e.clientY : e.touches[0].clientY;\n\n blobsRef.current.forEach((el, i) => {\n if (!el) return;\n const isLead = i === 0;\n gsap.to(el, {\n x: x - left,\n y: y - top,\n duration: isLead ? fastDuration : slowDuration,\n ease: isLead ? fastEase : slowEase\n });\n });\n },\n [updateOffset, fastDuration, slowDuration, fastEase, slowEase]\n );\n\n useEffect(() => {\n const onResize = () => updateOffset();\n window.addEventListener('resize', onResize);\n return () => window.removeEventListener('resize', onResize);\n }, [updateOffset]);\n\n return (\n \n {useFilter && (\n \n \n \n \n \n \n )}\n\n \n {Array.from({ length: trailCount }).map((_, i) => (\n {\n blobsRef.current[i] = el;\n }}\n className=\"absolute will-change-transform transform -translate-x-1/2 -translate-y-1/2\"\n style={{\n width: sizes[i],\n height: sizes[i],\n borderRadius: blobType === 'circle' ? '50%' : '0',\n backgroundColor: fillColor,\n opacity: opacities[i],\n boxShadow: `${shadowOffsetX}px ${shadowOffsetY}px ${shadowBlur}px 0 ${shadowColor}`\n }}\n >\n \n
\n ))}\n \n \n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/BlurText-JS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BlurText-JS-CSS", "title": "BlurText", "description": "Text starts blurred then crisply resolves for a soft-focus reveal effect.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BlurText/BlurText.jsx", "content": "import { motion } from 'motion/react';\nimport { useEffect, useRef, useState, useMemo } from 'react';\n\nconst buildKeyframes = (from, steps) => {\n const keys = new Set([...Object.keys(from), ...steps.flatMap(s => Object.keys(s))]);\n\n const keyframes = {};\n keys.forEach(k => {\n keyframes[k] = [from[k], ...steps.map(s => s[k])];\n });\n return keyframes;\n};\n\nconst BlurText = ({\n text = '',\n delay = 200,\n className = '',\n animateBy = 'words',\n direction = 'top',\n threshold = 0.1,\n rootMargin = '0px',\n animationFrom,\n animationTo,\n easing = t => t,\n onAnimationComplete,\n stepDuration = 0.35\n}) => {\n const elements = animateBy === 'words' ? text.split(' ') : text.split('');\n const [inView, setInView] = useState(false);\n const ref = useRef(null);\n\n useEffect(() => {\n if (!ref.current) return;\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setInView(true);\n observer.unobserve(ref.current);\n }\n },\n { threshold, rootMargin }\n );\n observer.observe(ref.current);\n return () => observer.disconnect();\n }, [threshold, rootMargin]);\n\n const defaultFrom = useMemo(\n () =>\n direction === 'top' ? { filter: 'blur(10px)', opacity: 0, y: -50 } : { filter: 'blur(10px)', opacity: 0, y: 50 },\n [direction]\n );\n\n const defaultTo = useMemo(\n () => [\n {\n filter: 'blur(5px)',\n opacity: 0.5,\n y: direction === 'top' ? 5 : -5\n },\n { filter: 'blur(0px)', opacity: 1, y: 0 }\n ],\n [direction]\n );\n\n const fromSnapshot = animationFrom ?? defaultFrom;\n const toSnapshots = animationTo ?? defaultTo;\n\n const stepCount = toSnapshots.length + 1;\n const totalDuration = stepDuration * (stepCount - 1);\n const times = Array.from({ length: stepCount }, (_, i) => (stepCount === 1 ? 0 : i / (stepCount - 1)));\n\n return (\n

\n {elements.map((segment, index) => {\n const animateKeyframes = buildKeyframes(fromSnapshot, toSnapshots);\n\n const spanTransition = {\n duration: totalDuration,\n times,\n delay: (index * delay) / 1000\n };\n spanTransition.ease = easing;\n\n return (\n \n {segment === ' ' ? '\\u00A0' : segment}\n {animateBy === 'words' && index < elements.length - 1 && '\\u00A0'}\n \n );\n })}\n

\n );\n};\n\nexport default BlurText;\n" } ], "registryDependencies": [], "dependencies": [ "motion@^12.23.12" ] } ================================================ FILE: public/r/BlurText-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BlurText-JS-TW", "title": "BlurText", "description": "Text starts blurred then crisply resolves for a soft-focus reveal effect.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BlurText/BlurText.jsx", "content": "import { motion } from 'motion/react';\nimport { useEffect, useRef, useState, useMemo } from 'react';\n\nconst buildKeyframes = (from, steps) => {\n const keys = new Set([...Object.keys(from), ...steps.flatMap(s => Object.keys(s))]);\n\n const keyframes = {};\n keys.forEach(k => {\n keyframes[k] = [from[k], ...steps.map(s => s[k])];\n });\n return keyframes;\n};\n\nconst BlurText = ({\n text = '',\n delay = 200,\n className = '',\n animateBy = 'words',\n direction = 'top',\n threshold = 0.1,\n rootMargin = '0px',\n animationFrom,\n animationTo,\n easing = t => t,\n onAnimationComplete,\n stepDuration = 0.35\n}) => {\n const elements = animateBy === 'words' ? text.split(' ') : text.split('');\n const [inView, setInView] = useState(false);\n const ref = useRef(null);\n\n useEffect(() => {\n if (!ref.current) return;\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setInView(true);\n observer.unobserve(ref.current);\n }\n },\n { threshold, rootMargin }\n );\n observer.observe(ref.current);\n return () => observer.disconnect();\n }, [threshold, rootMargin]);\n\n const defaultFrom = useMemo(\n () =>\n direction === 'top' ? { filter: 'blur(10px)', opacity: 0, y: -50 } : { filter: 'blur(10px)', opacity: 0, y: 50 },\n [direction]\n );\n\n const defaultTo = useMemo(\n () => [\n {\n filter: 'blur(5px)',\n opacity: 0.5,\n y: direction === 'top' ? 5 : -5\n },\n { filter: 'blur(0px)', opacity: 1, y: 0 }\n ],\n [direction]\n );\n\n const fromSnapshot = animationFrom ?? defaultFrom;\n const toSnapshots = animationTo ?? defaultTo;\n\n const stepCount = toSnapshots.length + 1;\n const totalDuration = stepDuration * (stepCount - 1);\n const times = Array.from({ length: stepCount }, (_, i) => (stepCount === 1 ? 0 : i / (stepCount - 1)));\n\n return (\n

\n {elements.map((segment, index) => {\n const animateKeyframes = buildKeyframes(fromSnapshot, toSnapshots);\n\n const spanTransition = {\n duration: totalDuration,\n times,\n delay: (index * delay) / 1000\n };\n spanTransition.ease = easing;\n\n return (\n \n {segment === ' ' ? '\\u00A0' : segment}\n {animateBy === 'words' && index < elements.length - 1 && '\\u00A0'}\n \n );\n })}\n

\n );\n};\n\nexport default BlurText;\n" } ], "registryDependencies": [], "dependencies": [ "motion@^12.23.12" ] } ================================================ FILE: public/r/BlurText-TS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BlurText-TS-CSS", "title": "BlurText", "description": "Text starts blurred then crisply resolves for a soft-focus reveal effect.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BlurText/BlurText.tsx", "content": "import { motion, Transition } from 'motion/react';\nimport { useEffect, useRef, useState, useMemo } from 'react';\n\ntype BlurTextProps = {\n text?: string;\n delay?: number;\n className?: string;\n animateBy?: 'words' | 'letters';\n direction?: 'top' | 'bottom';\n threshold?: number;\n rootMargin?: string;\n animationFrom?: Record;\n animationTo?: Array>;\n easing?: (t: number) => number;\n onAnimationComplete?: () => void;\n stepDuration?: number;\n};\n\nconst buildKeyframes = (\n from: Record,\n steps: Array>\n): Record> => {\n const keys = new Set([...Object.keys(from), ...steps.flatMap(s => Object.keys(s))]);\n\n const keyframes: Record> = {};\n keys.forEach(k => {\n keyframes[k] = [from[k], ...steps.map(s => s[k])];\n });\n return keyframes;\n};\n\nconst BlurText: React.FC = ({\n text = '',\n delay = 200,\n className = '',\n animateBy = 'words',\n direction = 'top',\n threshold = 0.1,\n rootMargin = '0px',\n animationFrom,\n animationTo,\n easing = (t: number) => t,\n onAnimationComplete,\n stepDuration = 0.35\n}) => {\n const elements = animateBy === 'words' ? text.split(' ') : text.split('');\n const [inView, setInView] = useState(false);\n const ref = useRef(null);\n\n useEffect(() => {\n if (!ref.current) return;\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setInView(true);\n observer.unobserve(ref.current as Element);\n }\n },\n { threshold, rootMargin }\n );\n observer.observe(ref.current);\n return () => observer.disconnect();\n }, [threshold, rootMargin]);\n\n const defaultFrom = useMemo(\n () =>\n direction === 'top' ? { filter: 'blur(10px)', opacity: 0, y: -50 } : { filter: 'blur(10px)', opacity: 0, y: 50 },\n [direction]\n );\n\n const defaultTo = useMemo(\n () => [\n {\n filter: 'blur(5px)',\n opacity: 0.5,\n y: direction === 'top' ? 5 : -5\n },\n { filter: 'blur(0px)', opacity: 1, y: 0 }\n ],\n [direction]\n );\n\n const fromSnapshot = animationFrom ?? defaultFrom;\n const toSnapshots = animationTo ?? defaultTo;\n\n const stepCount = toSnapshots.length + 1;\n const totalDuration = stepDuration * (stepCount - 1);\n const times = Array.from({ length: stepCount }, (_, i) => (stepCount === 1 ? 0 : i / (stepCount - 1)));\n\n return (\n

\n {elements.map((segment, index) => {\n const animateKeyframes = buildKeyframes(fromSnapshot, toSnapshots);\n\n const spanTransition: Transition = {\n duration: totalDuration,\n times,\n delay: (index * delay) / 1000,\n ease: easing\n };\n\n return (\n \n {segment === ' ' ? '\\u00A0' : segment}\n {animateBy === 'words' && index < elements.length - 1 && '\\u00A0'}\n \n );\n })}\n

\n );\n};\n\nexport default BlurText;\n" } ], "registryDependencies": [], "dependencies": [ "motion@^12.23.12" ] } ================================================ FILE: public/r/BlurText-TS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BlurText-TS-TW", "title": "BlurText", "description": "Text starts blurred then crisply resolves for a soft-focus reveal effect.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BlurText/BlurText.tsx", "content": "import { motion, Transition, Easing } from 'motion/react';\nimport { useEffect, useRef, useState, useMemo } from 'react';\n\ntype BlurTextProps = {\n text?: string;\n delay?: number;\n className?: string;\n animateBy?: 'words' | 'letters';\n direction?: 'top' | 'bottom';\n threshold?: number;\n rootMargin?: string;\n animationFrom?: Record;\n animationTo?: Array>;\n easing?: Easing | Easing[];\n onAnimationComplete?: () => void;\n stepDuration?: number;\n};\n\nconst buildKeyframes = (\n from: Record,\n steps: Array>\n): Record> => {\n const keys = new Set([...Object.keys(from), ...steps.flatMap(s => Object.keys(s))]);\n\n const keyframes: Record> = {};\n keys.forEach(k => {\n keyframes[k] = [from[k], ...steps.map(s => s[k])];\n });\n return keyframes;\n};\n\nconst BlurText: React.FC = ({\n text = '',\n delay = 200,\n className = '',\n animateBy = 'words',\n direction = 'top',\n threshold = 0.1,\n rootMargin = '0px',\n animationFrom,\n animationTo,\n easing = (t: number) => t,\n onAnimationComplete,\n stepDuration = 0.35\n}) => {\n const elements = animateBy === 'words' ? text.split(' ') : text.split('');\n const [inView, setInView] = useState(false);\n const ref = useRef(null);\n\n useEffect(() => {\n if (!ref.current) return;\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n setInView(true);\n observer.unobserve(ref.current as Element);\n }\n },\n { threshold, rootMargin }\n );\n observer.observe(ref.current);\n return () => observer.disconnect();\n }, [threshold, rootMargin]);\n\n const defaultFrom = useMemo(\n () =>\n direction === 'top' ? { filter: 'blur(10px)', opacity: 0, y: -50 } : { filter: 'blur(10px)', opacity: 0, y: 50 },\n [direction]\n );\n\n const defaultTo = useMemo(\n () => [\n {\n filter: 'blur(5px)',\n opacity: 0.5,\n y: direction === 'top' ? 5 : -5\n },\n { filter: 'blur(0px)', opacity: 1, y: 0 }\n ],\n [direction]\n );\n\n const fromSnapshot = animationFrom ?? defaultFrom;\n const toSnapshots = animationTo ?? defaultTo;\n\n const stepCount = toSnapshots.length + 1;\n const totalDuration = stepDuration * (stepCount - 1);\n const times = Array.from({ length: stepCount }, (_, i) => (stepCount === 1 ? 0 : i / (stepCount - 1)));\n\n return (\n

\n {elements.map((segment, index) => {\n const animateKeyframes = buildKeyframes(fromSnapshot, toSnapshots);\n\n const spanTransition: Transition = {\n duration: totalDuration,\n times,\n delay: (index * delay) / 1000,\n ease: easing\n };\n\n return (\n \n {segment === ' ' ? '\\u00A0' : segment}\n {animateBy === 'words' && index < elements.length - 1 && '\\u00A0'}\n \n );\n })}\n

\n );\n};\n\nexport default BlurText;\n" } ], "registryDependencies": [], "dependencies": [ "motion@^12.23.12" ] } ================================================ FILE: public/r/BorderGlow-JS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BorderGlow-JS-CSS", "title": "BorderGlow", "description": "Glowing mesh-gradient border that follows cursor direction and intensifies near edges.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BorderGlow/BorderGlow.css", "content": ".border-glow-card {\n --edge-proximity: 0;\n --cursor-angle: 45deg;\n --edge-sensitivity: 30;\n --color-sensitivity: calc(var(--edge-sensitivity) + 20);\n --border-radius: 28px;\n --glow-padding: 40px;\n --cone-spread: 25;\n\n position: relative;\n border-radius: var(--border-radius);\n isolation: isolate;\n transform: translate3d(0, 0, 0.01px);\n display: grid;\n border: 1px solid rgb(255 255 255 / 15%);\n background: var(--card-bg, #060010);\n overflow: visible;\n box-shadow:\n rgba(0, 0, 0, 0.1) 0px 1px 2px,\n rgba(0, 0, 0, 0.1) 0px 2px 4px,\n rgba(0, 0, 0, 0.1) 0px 4px 8px,\n rgba(0, 0, 0, 0.1) 0px 8px 16px,\n rgba(0, 0, 0, 0.1) 0px 16px 32px,\n rgba(0, 0, 0, 0.1) 0px 32px 64px;\n}\n\n.border-glow-card::before,\n.border-glow-card::after,\n.border-glow-card > .edge-light {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n transition: opacity 0.25s ease-out;\n z-index: -1;\n}\n\n.border-glow-card:not(:hover):not(.sweep-active)::before,\n.border-glow-card:not(:hover):not(.sweep-active)::after,\n.border-glow-card:not(:hover):not(.sweep-active) > .edge-light {\n opacity: 0;\n transition: opacity 0.75s ease-in-out;\n}\n\n/* colored mesh-gradient border */\n.border-glow-card::before {\n border: 1px solid transparent;\n background:\n linear-gradient(var(--card-bg, #060010) 0 100%) padding-box,\n linear-gradient(rgb(255 255 255 / 0%) 0% 100%) border-box,\n var(--gradient-one, radial-gradient(at 80% 55%, hsla(268, 100%, 76%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-two, radial-gradient(at 69% 34%, hsla(349, 100%, 74%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-three, radial-gradient(at 8% 6%, hsla(136, 100%, 78%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-four, radial-gradient(at 41% 38%, hsla(192, 100%, 64%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-five, radial-gradient(at 86% 85%, hsla(186, 100%, 74%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-six, radial-gradient(at 82% 18%, hsla(52, 100%, 65%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-seven, radial-gradient(at 51% 4%, hsla(12, 100%, 72%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-base, linear-gradient(#c299ff 0 100%)) border-box;\n\n opacity: calc((var(--edge-proximity) - var(--color-sensitivity)) / (100 - var(--color-sensitivity)));\n\n mask-image:\n conic-gradient(\n from var(--cursor-angle) at center,\n black calc(var(--cone-spread) * 1%),\n transparent calc((var(--cone-spread) + 15) * 1%),\n transparent calc((100 - var(--cone-spread) - 15) * 1%),\n black calc((100 - var(--cone-spread)) * 1%)\n );\n}\n\n/* colored mesh-gradient background fill near edges */\n.border-glow-card::after {\n border: 1px solid transparent;\n background:\n var(--gradient-one, radial-gradient(at 80% 55%, hsla(268, 100%, 76%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-two, radial-gradient(at 69% 34%, hsla(349, 100%, 74%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-three, radial-gradient(at 8% 6%, hsla(136, 100%, 78%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-four, radial-gradient(at 41% 38%, hsla(192, 100%, 64%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-five, radial-gradient(at 86% 85%, hsla(186, 100%, 74%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-six, radial-gradient(at 82% 18%, hsla(52, 100%, 65%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-seven, radial-gradient(at 51% 4%, hsla(12, 100%, 72%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-base, linear-gradient(#c299ff 0 100%)) padding-box;\n\n mask-image:\n linear-gradient(to bottom, black, black),\n radial-gradient(ellipse at 50% 50%, black 40%, transparent 65%),\n radial-gradient(ellipse at 66% 66%, black 5%, transparent 40%),\n radial-gradient(ellipse at 33% 33%, black 5%, transparent 40%),\n radial-gradient(ellipse at 66% 33%, black 5%, transparent 40%),\n radial-gradient(ellipse at 33% 66%, black 5%, transparent 40%),\n conic-gradient(from var(--cursor-angle) at center, transparent 5%, black 15%, black 85%, transparent 95%);\n\n mask-composite: subtract, add, add, add, add, add;\n opacity: calc(var(--fill-opacity, 0.5) * (var(--edge-proximity) - var(--color-sensitivity)) / (100 - var(--color-sensitivity)));\n mix-blend-mode: soft-light;\n}\n\n/* outer glow layer */\n.border-glow-card > .edge-light {\n inset: calc(var(--glow-padding) * -1);\n pointer-events: none;\n z-index: 1;\n\n mask-image:\n conic-gradient(\n from var(--cursor-angle) at center, black 2.5%, transparent 10%, transparent 90%, black 97.5%\n );\n\n opacity: calc((var(--edge-proximity) - var(--edge-sensitivity)) / (100 - var(--edge-sensitivity)));\n mix-blend-mode: plus-lighter;\n}\n\n.border-glow-card > .edge-light::before {\n content: \"\";\n position: absolute;\n inset: var(--glow-padding);\n border-radius: inherit;\n box-shadow:\n inset 0 0 0 1px var(--glow-color, hsl(40deg 80% 80% / 100%)),\n inset 0 0 1px 0 var(--glow-color-60, hsl(40deg 80% 80% / 60%)),\n inset 0 0 3px 0 var(--glow-color-50, hsl(40deg 80% 80% / 50%)),\n inset 0 0 6px 0 var(--glow-color-40, hsl(40deg 80% 80% / 40%)),\n inset 0 0 15px 0 var(--glow-color-30, hsl(40deg 80% 80% / 30%)),\n inset 0 0 25px 2px var(--glow-color-20, hsl(40deg 80% 80% / 20%)),\n inset 0 0 50px 2px var(--glow-color-10, hsl(40deg 80% 80% / 10%)),\n 0 0 1px 0 var(--glow-color-60, hsl(40deg 80% 80% / 60%)),\n 0 0 3px 0 var(--glow-color-50, hsl(40deg 80% 80% / 50%)),\n 0 0 6px 0 var(--glow-color-40, hsl(40deg 80% 80% / 40%)),\n 0 0 15px 0 var(--glow-color-30, hsl(40deg 80% 80% / 30%)),\n 0 0 25px 2px var(--glow-color-20, hsl(40deg 80% 80% / 20%)),\n 0 0 50px 2px var(--glow-color-10, hsl(40deg 80% 80% / 10%));\n}\n\n.border-glow-inner {\n display: flex;\n flex-direction: column;\n position: relative;\n overflow: auto;\n z-index: 1;\n}\n" }, { "type": "registry:component", "path": "BorderGlow/BorderGlow.jsx", "content": "import { useRef, useCallback, useEffect } from 'react';\nimport './BorderGlow.css';\n\nfunction parseHSL(hslStr) {\n const match = hslStr.match(/([\\d.]+)\\s*([\\d.]+)%?\\s*([\\d.]+)%?/);\n if (!match) return { h: 40, s: 80, l: 80 };\n return { h: parseFloat(match[1]), s: parseFloat(match[2]), l: parseFloat(match[3]) };\n}\n\nfunction buildGlowVars(glowColor, intensity) {\n const { h, s, l } = parseHSL(glowColor);\n const base = `${h}deg ${s}% ${l}%`;\n const opacities = [100, 60, 50, 40, 30, 20, 10];\n const keys = ['', '-60', '-50', '-40', '-30', '-20', '-10'];\n const vars = {};\n for (let i = 0; i < opacities.length; i++) {\n vars[`--glow-color${keys[i]}`] = `hsl(${base} / ${Math.min(opacities[i] * intensity, 100)}%)`;\n }\n return vars;\n}\n\nconst GRADIENT_POSITIONS = ['80% 55%', '69% 34%', '8% 6%', '41% 38%', '86% 85%', '82% 18%', '51% 4%'];\nconst GRADIENT_KEYS = ['--gradient-one', '--gradient-two', '--gradient-three', '--gradient-four', '--gradient-five', '--gradient-six', '--gradient-seven'];\nconst COLOR_MAP = [0, 1, 2, 0, 1, 2, 1];\n\nfunction buildGradientVars(colors) {\n const vars = {};\n for (let i = 0; i < 7; i++) {\n const c = colors[Math.min(COLOR_MAP[i], colors.length - 1)];\n vars[GRADIENT_KEYS[i]] = `radial-gradient(at ${GRADIENT_POSITIONS[i]}, ${c} 0px, transparent 50%)`;\n }\n vars['--gradient-base'] = `linear-gradient(${colors[0]} 0 100%)`;\n return vars;\n}\n\nfunction easeOutCubic(x) { return 1 - Math.pow(1 - x, 3); }\nfunction easeInCubic(x) { return x * x * x; }\n\nfunction animateValue({ start = 0, end = 100, duration = 1000, delay = 0, ease = easeOutCubic, onUpdate, onEnd }) {\n const t0 = performance.now() + delay;\n function tick() {\n const elapsed = performance.now() - t0;\n const t = Math.min(elapsed / duration, 1);\n onUpdate(start + (end - start) * ease(t));\n if (t < 1) requestAnimationFrame(tick);\n else if (onEnd) onEnd();\n }\n setTimeout(() => requestAnimationFrame(tick), delay);\n}\n\nconst BorderGlow = ({\n children,\n className = '',\n edgeSensitivity = 30,\n glowColor = '40 80 80',\n backgroundColor = '#060010',\n borderRadius = 28,\n glowRadius = 40,\n glowIntensity = 1.0,\n coneSpread = 25,\n animated = false,\n colors = ['#c084fc', '#f472b6', '#38bdf8'],\n fillOpacity = 0.5,\n}) => {\n const cardRef = useRef(null);\n\n const getCenterOfElement = useCallback((el) => {\n const { width, height } = el.getBoundingClientRect();\n return [width / 2, height / 2];\n }, []);\n\n const getEdgeProximity = useCallback((el, x, y) => {\n const [cx, cy] = getCenterOfElement(el);\n const dx = x - cx;\n const dy = y - cy;\n let kx = Infinity;\n let ky = Infinity;\n if (dx !== 0) kx = cx / Math.abs(dx);\n if (dy !== 0) ky = cy / Math.abs(dy);\n return Math.min(Math.max(1 / Math.min(kx, ky), 0), 1);\n }, [getCenterOfElement]);\n\n const getCursorAngle = useCallback((el, x, y) => {\n const [cx, cy] = getCenterOfElement(el);\n const dx = x - cx;\n const dy = y - cy;\n if (dx === 0 && dy === 0) return 0;\n const radians = Math.atan2(dy, dx);\n let degrees = radians * (180 / Math.PI) + 90;\n if (degrees < 0) degrees += 360;\n return degrees;\n }, [getCenterOfElement]);\n\n const handlePointerMove = useCallback((e) => {\n const card = cardRef.current;\n if (!card) return;\n\n const rect = card.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n\n const edge = getEdgeProximity(card, x, y);\n const angle = getCursorAngle(card, x, y);\n\n card.style.setProperty('--edge-proximity', `${(edge * 100).toFixed(3)}`);\n card.style.setProperty('--cursor-angle', `${angle.toFixed(3)}deg`);\n }, [getEdgeProximity, getCursorAngle]);\n\n useEffect(() => {\n if (!animated || !cardRef.current) return;\n const card = cardRef.current;\n const angleStart = 110;\n const angleEnd = 465;\n card.classList.add('sweep-active');\n card.style.setProperty('--cursor-angle', `${angleStart}deg`);\n\n animateValue({ duration: 500, onUpdate: v => card.style.setProperty('--edge-proximity', v) });\n animateValue({ ease: easeInCubic, duration: 1500, end: 50, onUpdate: v => {\n card.style.setProperty('--cursor-angle', `${(angleEnd - angleStart) * (v / 100) + angleStart}deg`);\n }});\n animateValue({ ease: easeOutCubic, delay: 1500, duration: 2250, start: 50, end: 100, onUpdate: v => {\n card.style.setProperty('--cursor-angle', `${(angleEnd - angleStart) * (v / 100) + angleStart}deg`);\n }});\n animateValue({ ease: easeInCubic, delay: 2500, duration: 1500, start: 100, end: 0,\n onUpdate: v => card.style.setProperty('--edge-proximity', v),\n onEnd: () => card.classList.remove('sweep-active'),\n });\n }, [animated]);\n\n const glowVars = buildGlowVars(glowColor, glowIntensity);\n\n return (\n \n \n
\n {children}\n
\n \n );\n};\n\nexport default BorderGlow;\n" } ], "registryDependencies": [], "dependencies": [] } ================================================ FILE: public/r/BorderGlow-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BorderGlow-JS-TW", "title": "BorderGlow", "description": "Glowing mesh-gradient border that follows cursor direction and intensifies near edges.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BorderGlow/BorderGlow.jsx", "content": "import { useRef, useCallback, useState, useEffect } from 'react';\n\nfunction parseHSL(hslStr) {\n const match = hslStr.match(/([\\d.]+)\\s*([\\d.]+)%?\\s*([\\d.]+)%?/);\n if (!match) return { h: 40, s: 80, l: 80 };\n return { h: parseFloat(match[1]), s: parseFloat(match[2]), l: parseFloat(match[3]) };\n}\n\nfunction buildBoxShadow(glowColor, intensity) {\n const { h, s, l } = parseHSL(glowColor);\n const base = `${h}deg ${s}% ${l}%`;\n const layers = [\n [0, 0, 0, 1, 100, true], [0, 0, 1, 0, 60, true], [0, 0, 3, 0, 50, true],\n [0, 0, 6, 0, 40, true], [0, 0, 15, 0, 30, true], [0, 0, 25, 2, 20, true],\n [0, 0, 50, 2, 10, true],\n [0, 0, 1, 0, 60, false], [0, 0, 3, 0, 50, false], [0, 0, 6, 0, 40, false],\n [0, 0, 15, 0, 30, false], [0, 0, 25, 2, 20, false], [0, 0, 50, 2, 10, false],\n ];\n return layers.map(([x, y, blur, spread, alpha, inset]) => {\n const a = Math.min(alpha * intensity, 100);\n return `${inset ? 'inset ' : ''}${x}px ${y}px ${blur}px ${spread}px hsl(${base} / ${a}%)`;\n }).join(', ');\n}\n\nfunction easeOutCubic(x) { return 1 - Math.pow(1 - x, 3); }\nfunction easeInCubic(x) { return x * x * x; }\n\nfunction animateValue({ start = 0, end = 100, duration = 1000, delay = 0, ease = easeOutCubic, onUpdate, onEnd }) {\n const t0 = performance.now() + delay;\n function tick() {\n const elapsed = performance.now() - t0;\n const t = Math.min(elapsed / duration, 1);\n onUpdate(start + (end - start) * ease(t));\n if (t < 1) requestAnimationFrame(tick);\n else if (onEnd) onEnd();\n }\n setTimeout(() => requestAnimationFrame(tick), delay);\n}\n\nconst GRADIENT_POSITIONS = ['80% 55%', '69% 34%', '8% 6%', '41% 38%', '86% 85%', '82% 18%', '51% 4%'];\nconst COLOR_MAP = [0, 1, 2, 0, 1, 2, 1];\n\nfunction buildMeshGradients(colors) {\n const gradients = [];\n for (let i = 0; i < 7; i++) {\n const c = colors[Math.min(COLOR_MAP[i], colors.length - 1)];\n gradients.push(`radial-gradient(at ${GRADIENT_POSITIONS[i]}, ${c} 0px, transparent 50%)`);\n }\n gradients.push(`linear-gradient(${colors[0]} 0 100%)`);\n return gradients;\n}\n\nconst BorderGlow = ({\n children,\n className = '',\n edgeSensitivity = 30,\n glowColor = '40 80 80',\n backgroundColor = '#060010',\n borderRadius = 28,\n glowRadius = 40,\n glowIntensity = 1.0,\n coneSpread = 25,\n animated = false,\n colors = ['#c084fc', '#f472b6', '#38bdf8'],\n fillOpacity = 0.5,\n}) => {\n const cardRef = useRef(null);\n const [isHovered, setIsHovered] = useState(false);\n const [cursorAngle, setCursorAngle] = useState(45);\n const [edgeProximity, setEdgeProximity] = useState(0);\n const [sweepActive, setSweepActive] = useState(false);\n\n const getCenterOfElement = useCallback((el) => {\n const { width, height } = el.getBoundingClientRect();\n return [width / 2, height / 2];\n }, []);\n\n const getEdgeProximity = useCallback((el, x, y) => {\n const [cx, cy] = getCenterOfElement(el);\n const dx = x - cx;\n const dy = y - cy;\n let kx = Infinity;\n let ky = Infinity;\n if (dx !== 0) kx = cx / Math.abs(dx);\n if (dy !== 0) ky = cy / Math.abs(dy);\n return Math.min(Math.max(1 / Math.min(kx, ky), 0), 1);\n }, [getCenterOfElement]);\n\n const getCursorAngle = useCallback((el, x, y) => {\n const [cx, cy] = getCenterOfElement(el);\n const dx = x - cx;\n const dy = y - cy;\n if (dx === 0 && dy === 0) return 0;\n const radians = Math.atan2(dy, dx);\n let degrees = radians * (180 / Math.PI) + 90;\n if (degrees < 0) degrees += 360;\n return degrees;\n }, [getCenterOfElement]);\n\n const handlePointerMove = useCallback((e) => {\n const card = cardRef.current;\n if (!card) return;\n const rect = card.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n setEdgeProximity(getEdgeProximity(card, x, y));\n setCursorAngle(getCursorAngle(card, x, y));\n }, [getEdgeProximity, getCursorAngle]);\n\n useEffect(() => {\n if (!animated) return;\n const angleStart = 110;\n const angleEnd = 465;\n setSweepActive(true);\n setCursorAngle(angleStart);\n\n animateValue({ duration: 500, onUpdate: v => setEdgeProximity(v / 100) });\n animateValue({ ease: easeInCubic, duration: 1500, end: 50, onUpdate: v => {\n setCursorAngle((angleEnd - angleStart) * (v / 100) + angleStart);\n }});\n animateValue({ ease: easeOutCubic, delay: 1500, duration: 2250, start: 50, end: 100, onUpdate: v => {\n setCursorAngle((angleEnd - angleStart) * (v / 100) + angleStart);\n }});\n animateValue({ ease: easeInCubic, delay: 2500, duration: 1500, start: 100, end: 0,\n onUpdate: v => setEdgeProximity(v / 100),\n onEnd: () => setSweepActive(false),\n });\n }, [animated]);\n\n const colorSensitivity = edgeSensitivity + 20;\n const isVisible = isHovered || sweepActive;\n const borderOpacity = isVisible\n ? Math.max(0, (edgeProximity * 100 - colorSensitivity) / (100 - colorSensitivity))\n : 0;\n const glowOpacity = isVisible\n ? Math.max(0, (edgeProximity * 100 - edgeSensitivity) / (100 - edgeSensitivity))\n : 0;\n\n const meshGradients = buildMeshGradients(colors);\n const borderBg = meshGradients.map(g => `${g} border-box`);\n const fillBg = meshGradients.map(g => `${g} padding-box`);\n const angleDeg = `${cursorAngle.toFixed(3)}deg`;\n\n return (\n setIsHovered(true)}\n onPointerLeave={() => setIsHovered(false)}\n className={`relative grid isolate border border-white/15 ${className}`}\n style={{\n background: backgroundColor,\n borderRadius: `${borderRadius}px`,\n transform: 'translate3d(0, 0, 0.01px)',\n boxShadow: 'rgba(0,0,0,0.1) 0 1px 2px, rgba(0,0,0,0.1) 0 2px 4px, rgba(0,0,0,0.1) 0 4px 8px, rgba(0,0,0,0.1) 0 8px 16px, rgba(0,0,0,0.1) 0 16px 32px, rgba(0,0,0,0.1) 0 32px 64px',\n }}\n >\n {/* mesh gradient border */}\n \n\n {/* mesh gradient fill near edges */}\n \n\n {/* outer glow */}\n \n \n
\n\n
\n {children}\n
\n \n );\n};\n\nexport default BorderGlow;\n" } ], "registryDependencies": [], "dependencies": [] } ================================================ FILE: public/r/BorderGlow-TS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BorderGlow-TS-CSS", "title": "BorderGlow", "description": "Glowing mesh-gradient border that follows cursor direction and intensifies near edges.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BorderGlow/BorderGlow.css", "content": ".border-glow-card {\n --edge-proximity: 0;\n --cursor-angle: 45deg;\n --edge-sensitivity: 30;\n --color-sensitivity: calc(var(--edge-sensitivity) + 20);\n --border-radius: 28px;\n --glow-padding: 40px;\n --cone-spread: 25;\n\n position: relative;\n border-radius: var(--border-radius);\n isolation: isolate;\n transform: translate3d(0, 0, 0.01px);\n display: grid;\n border: 1px solid rgb(255 255 255 / 15%);\n background: var(--card-bg, #060010);\n overflow: visible;\n box-shadow:\n rgba(0, 0, 0, 0.1) 0px 1px 2px,\n rgba(0, 0, 0, 0.1) 0px 2px 4px,\n rgba(0, 0, 0, 0.1) 0px 4px 8px,\n rgba(0, 0, 0, 0.1) 0px 8px 16px,\n rgba(0, 0, 0, 0.1) 0px 16px 32px,\n rgba(0, 0, 0, 0.1) 0px 32px 64px;\n}\n\n.border-glow-card::before,\n.border-glow-card::after,\n.border-glow-card > .edge-light {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n transition: opacity 0.25s ease-out;\n z-index: -1;\n}\n\n.border-glow-card:not(:hover):not(.sweep-active)::before,\n.border-glow-card:not(:hover):not(.sweep-active)::after,\n.border-glow-card:not(:hover):not(.sweep-active) > .edge-light {\n opacity: 0;\n transition: opacity 0.75s ease-in-out;\n}\n\n/* colored mesh-gradient border */\n.border-glow-card::before {\n border: 1px solid transparent;\n background:\n linear-gradient(var(--card-bg, #060010) 0 100%) padding-box,\n linear-gradient(rgb(255 255 255 / 0%) 0% 100%) border-box,\n var(--gradient-one, radial-gradient(at 80% 55%, hsla(268, 100%, 76%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-two, radial-gradient(at 69% 34%, hsla(349, 100%, 74%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-three, radial-gradient(at 8% 6%, hsla(136, 100%, 78%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-four, radial-gradient(at 41% 38%, hsla(192, 100%, 64%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-five, radial-gradient(at 86% 85%, hsla(186, 100%, 74%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-six, radial-gradient(at 82% 18%, hsla(52, 100%, 65%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-seven, radial-gradient(at 51% 4%, hsla(12, 100%, 72%, 1) 0px, transparent 50%)) border-box,\n var(--gradient-base, linear-gradient(#c299ff 0 100%)) border-box;\n\n opacity: calc((var(--edge-proximity) - var(--color-sensitivity)) / (100 - var(--color-sensitivity)));\n\n mask-image:\n conic-gradient(\n from var(--cursor-angle) at center,\n black calc(var(--cone-spread) * 1%),\n transparent calc((var(--cone-spread) + 15) * 1%),\n transparent calc((100 - var(--cone-spread) - 15) * 1%),\n black calc((100 - var(--cone-spread)) * 1%)\n );\n}\n\n/* colored mesh-gradient background fill near edges */\n.border-glow-card::after {\n border: 1px solid transparent;\n background:\n var(--gradient-one, radial-gradient(at 80% 55%, hsla(268, 100%, 76%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-two, radial-gradient(at 69% 34%, hsla(349, 100%, 74%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-three, radial-gradient(at 8% 6%, hsla(136, 100%, 78%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-four, radial-gradient(at 41% 38%, hsla(192, 100%, 64%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-five, radial-gradient(at 86% 85%, hsla(186, 100%, 74%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-six, radial-gradient(at 82% 18%, hsla(52, 100%, 65%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-seven, radial-gradient(at 51% 4%, hsla(12, 100%, 72%, 1) 0px, transparent 50%)) padding-box,\n var(--gradient-base, linear-gradient(#c299ff 0 100%)) padding-box;\n\n mask-image:\n linear-gradient(to bottom, black, black),\n radial-gradient(ellipse at 50% 50%, black 40%, transparent 65%),\n radial-gradient(ellipse at 66% 66%, black 5%, transparent 40%),\n radial-gradient(ellipse at 33% 33%, black 5%, transparent 40%),\n radial-gradient(ellipse at 66% 33%, black 5%, transparent 40%),\n radial-gradient(ellipse at 33% 66%, black 5%, transparent 40%),\n conic-gradient(from var(--cursor-angle) at center, transparent 5%, black 15%, black 85%, transparent 95%);\n\n mask-composite: subtract, add, add, add, add, add;\n opacity: calc(var(--fill-opacity, 0.5) * (var(--edge-proximity) - var(--color-sensitivity)) / (100 - var(--color-sensitivity)));\n mix-blend-mode: soft-light;\n}\n\n/* outer glow layer */\n.border-glow-card > .edge-light {\n inset: calc(var(--glow-padding) * -1);\n pointer-events: none;\n z-index: 1;\n\n mask-image:\n conic-gradient(\n from var(--cursor-angle) at center, black 2.5%, transparent 10%, transparent 90%, black 97.5%\n );\n\n opacity: calc((var(--edge-proximity) - var(--edge-sensitivity)) / (100 - var(--edge-sensitivity)));\n mix-blend-mode: plus-lighter;\n}\n\n.border-glow-card > .edge-light::before {\n content: \"\";\n position: absolute;\n inset: var(--glow-padding);\n border-radius: inherit;\n box-shadow:\n inset 0 0 0 1px var(--glow-color, hsl(40deg 80% 80% / 100%)),\n inset 0 0 1px 0 var(--glow-color-60, hsl(40deg 80% 80% / 60%)),\n inset 0 0 3px 0 var(--glow-color-50, hsl(40deg 80% 80% / 50%)),\n inset 0 0 6px 0 var(--glow-color-40, hsl(40deg 80% 80% / 40%)),\n inset 0 0 15px 0 var(--glow-color-30, hsl(40deg 80% 80% / 30%)),\n inset 0 0 25px 2px var(--glow-color-20, hsl(40deg 80% 80% / 20%)),\n inset 0 0 50px 2px var(--glow-color-10, hsl(40deg 80% 80% / 10%)),\n 0 0 1px 0 var(--glow-color-60, hsl(40deg 80% 80% / 60%)),\n 0 0 3px 0 var(--glow-color-50, hsl(40deg 80% 80% / 50%)),\n 0 0 6px 0 var(--glow-color-40, hsl(40deg 80% 80% / 40%)),\n 0 0 15px 0 var(--glow-color-30, hsl(40deg 80% 80% / 30%)),\n 0 0 25px 2px var(--glow-color-20, hsl(40deg 80% 80% / 20%)),\n 0 0 50px 2px var(--glow-color-10, hsl(40deg 80% 80% / 10%));\n}\n\n.border-glow-inner {\n display: flex;\n flex-direction: column;\n position: relative;\n overflow: auto;\n z-index: 1;\n}\n" }, { "type": "registry:component", "path": "BorderGlow/BorderGlow.tsx", "content": "import { useRef, useCallback, useEffect, type ReactNode } from 'react';\nimport './BorderGlow.css';\n\ninterface BorderGlowProps {\n children?: ReactNode;\n className?: string;\n edgeSensitivity?: number;\n glowColor?: string;\n backgroundColor?: string;\n borderRadius?: number;\n glowRadius?: number;\n glowIntensity?: number;\n coneSpread?: number;\n animated?: boolean;\n colors?: string[];\n fillOpacity?: number;\n}\n\nfunction parseHSL(hslStr: string): { h: number; s: number; l: number } {\n const match = hslStr.match(/([\\d.]+)\\s*([\\d.]+)%?\\s*([\\d.]+)%?/);\n if (!match) return { h: 40, s: 80, l: 80 };\n return { h: parseFloat(match[1]), s: parseFloat(match[2]), l: parseFloat(match[3]) };\n}\n\nfunction buildGlowVars(glowColor: string, intensity: number): Record {\n const { h, s, l } = parseHSL(glowColor);\n const base = `${h}deg ${s}% ${l}%`;\n const opacities = [100, 60, 50, 40, 30, 20, 10];\n const keys = ['', '-60', '-50', '-40', '-30', '-20', '-10'];\n const vars: Record = {};\n for (let i = 0; i < opacities.length; i++) {\n vars[`--glow-color${keys[i]}`] = `hsl(${base} / ${Math.min(opacities[i] * intensity, 100)}%)`;\n }\n return vars;\n}\n\nconst GRADIENT_POSITIONS = ['80% 55%', '69% 34%', '8% 6%', '41% 38%', '86% 85%', '82% 18%', '51% 4%'];\nconst GRADIENT_KEYS = ['--gradient-one', '--gradient-two', '--gradient-three', '--gradient-four', '--gradient-five', '--gradient-six', '--gradient-seven'];\nconst COLOR_MAP = [0, 1, 2, 0, 1, 2, 1];\n\nfunction buildGradientVars(colors: string[]): Record {\n const vars: Record = {};\n for (let i = 0; i < 7; i++) {\n const c = colors[Math.min(COLOR_MAP[i], colors.length - 1)];\n vars[GRADIENT_KEYS[i]] = `radial-gradient(at ${GRADIENT_POSITIONS[i]}, ${c} 0px, transparent 50%)`;\n }\n vars['--gradient-base'] = `linear-gradient(${colors[0]} 0 100%)`;\n return vars;\n}\n\nfunction easeOutCubic(x: number) { return 1 - Math.pow(1 - x, 3); }\nfunction easeInCubic(x: number) { return x * x * x; }\n\ninterface AnimateOpts {\n start?: number; end?: number; duration?: number; delay?: number;\n ease?: (t: number) => number; onUpdate: (v: number) => void; onEnd?: () => void;\n}\n\nfunction animateValue({ start = 0, end = 100, duration = 1000, delay = 0, ease = easeOutCubic, onUpdate, onEnd }: AnimateOpts) {\n const t0 = performance.now() + delay;\n function tick() {\n const elapsed = performance.now() - t0;\n const t = Math.min(elapsed / duration, 1);\n onUpdate(start + (end - start) * ease(t));\n if (t < 1) requestAnimationFrame(tick);\n else if (onEnd) onEnd();\n }\n setTimeout(() => requestAnimationFrame(tick), delay);\n}\n\nconst BorderGlow: React.FC = ({\n children,\n className = '',\n edgeSensitivity = 30,\n glowColor = '40 80 80',\n backgroundColor = '#060010',\n borderRadius = 28,\n glowRadius = 40,\n glowIntensity = 1.0,\n coneSpread = 25,\n animated = false,\n colors = ['#c084fc', '#f472b6', '#38bdf8'],\n fillOpacity = 0.5,\n}) => {\n const cardRef = useRef(null);\n\n const getCenterOfElement = useCallback((el: HTMLElement) => {\n const { width, height } = el.getBoundingClientRect();\n return [width / 2, height / 2];\n }, []);\n\n const getEdgeProximity = useCallback((el: HTMLElement, x: number, y: number) => {\n const [cx, cy] = getCenterOfElement(el);\n const dx = x - cx;\n const dy = y - cy;\n let kx = Infinity;\n let ky = Infinity;\n if (dx !== 0) kx = cx / Math.abs(dx);\n if (dy !== 0) ky = cy / Math.abs(dy);\n return Math.min(Math.max(1 / Math.min(kx, ky), 0), 1);\n }, [getCenterOfElement]);\n\n const getCursorAngle = useCallback((el: HTMLElement, x: number, y: number) => {\n const [cx, cy] = getCenterOfElement(el);\n const dx = x - cx;\n const dy = y - cy;\n if (dx === 0 && dy === 0) return 0;\n const radians = Math.atan2(dy, dx);\n let degrees = radians * (180 / Math.PI) + 90;\n if (degrees < 0) degrees += 360;\n return degrees;\n }, [getCenterOfElement]);\n\n const handlePointerMove = useCallback((e: React.PointerEvent) => {\n const card = cardRef.current;\n if (!card) return;\n\n const rect = card.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n\n const edge = getEdgeProximity(card, x, y);\n const angle = getCursorAngle(card, x, y);\n\n card.style.setProperty('--edge-proximity', `${(edge * 100).toFixed(3)}`);\n card.style.setProperty('--cursor-angle', `${angle.toFixed(3)}deg`);\n }, [getEdgeProximity, getCursorAngle]);\n\n useEffect(() => {\n if (!animated || !cardRef.current) return;\n const card = cardRef.current;\n const angleStart = 110;\n const angleEnd = 465;\n card.classList.add('sweep-active');\n card.style.setProperty('--cursor-angle', `${angleStart}deg`);\n\n animateValue({ duration: 500, onUpdate: v => card.style.setProperty('--edge-proximity', `${v}`) });\n animateValue({ ease: easeInCubic, duration: 1500, end: 50, onUpdate: v => {\n card.style.setProperty('--cursor-angle', `${(angleEnd - angleStart) * (v / 100) + angleStart}deg`);\n }});\n animateValue({ ease: easeOutCubic, delay: 1500, duration: 2250, start: 50, end: 100, onUpdate: v => {\n card.style.setProperty('--cursor-angle', `${(angleEnd - angleStart) * (v / 100) + angleStart}deg`);\n }});\n animateValue({ ease: easeInCubic, delay: 2500, duration: 1500, start: 100, end: 0,\n onUpdate: v => card.style.setProperty('--edge-proximity', `${v}`),\n onEnd: () => card.classList.remove('sweep-active'),\n });\n }, [animated]);\n\n const glowVars = buildGlowVars(glowColor, glowIntensity);\n\n return (\n \n \n
\n {children}\n
\n \n );\n};\n\nexport default BorderGlow;\n" } ], "registryDependencies": [], "dependencies": [] } ================================================ FILE: public/r/BorderGlow-TS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BorderGlow-TS-TW", "title": "BorderGlow", "description": "Glowing mesh-gradient border that follows cursor direction and intensifies near edges.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BorderGlow/BorderGlow.tsx", "content": "import { useRef, useCallback, useState, useEffect, type ReactNode } from 'react';\n\ninterface BorderGlowProps {\n children?: ReactNode;\n className?: string;\n edgeSensitivity?: number;\n glowColor?: string;\n backgroundColor?: string;\n borderRadius?: number;\n glowRadius?: number;\n glowIntensity?: number;\n coneSpread?: number;\n animated?: boolean;\n colors?: string[];\n fillOpacity?: number;\n}\n\nfunction parseHSL(hslStr: string): { h: number; s: number; l: number } {\n const match = hslStr.match(/([\\d.]+)\\s*([\\d.]+)%?\\s*([\\d.]+)%?/);\n if (!match) return { h: 40, s: 80, l: 80 };\n return { h: parseFloat(match[1]), s: parseFloat(match[2]), l: parseFloat(match[3]) };\n}\n\nfunction buildBoxShadow(glowColor: string, intensity: number): string {\n const { h, s, l } = parseHSL(glowColor);\n const base = `${h}deg ${s}% ${l}%`;\n const layers: [number, number, number, number, number, boolean][] = [\n [0, 0, 0, 1, 100, true], [0, 0, 1, 0, 60, true], [0, 0, 3, 0, 50, true],\n [0, 0, 6, 0, 40, true], [0, 0, 15, 0, 30, true], [0, 0, 25, 2, 20, true],\n [0, 0, 50, 2, 10, true],\n [0, 0, 1, 0, 60, false], [0, 0, 3, 0, 50, false], [0, 0, 6, 0, 40, false],\n [0, 0, 15, 0, 30, false], [0, 0, 25, 2, 20, false], [0, 0, 50, 2, 10, false],\n ];\n return layers.map(([x, y, blur, spread, alpha, inset]) => {\n const a = Math.min(alpha * intensity, 100);\n return `${inset ? 'inset ' : ''}${x}px ${y}px ${blur}px ${spread}px hsl(${base} / ${a}%)`;\n }).join(', ');\n}\n\nfunction easeOutCubic(x: number) { return 1 - Math.pow(1 - x, 3); }\nfunction easeInCubic(x: number) { return x * x * x; }\n\ninterface AnimateOpts {\n start?: number; end?: number; duration?: number; delay?: number;\n ease?: (t: number) => number; onUpdate: (v: number) => void; onEnd?: () => void;\n}\n\nfunction animateValue({ start = 0, end = 100, duration = 1000, delay = 0, ease = easeOutCubic, onUpdate, onEnd }: AnimateOpts) {\n const t0 = performance.now() + delay;\n function tick() {\n const elapsed = performance.now() - t0;\n const t = Math.min(elapsed / duration, 1);\n onUpdate(start + (end - start) * ease(t));\n if (t < 1) requestAnimationFrame(tick);\n else if (onEnd) onEnd();\n }\n setTimeout(() => requestAnimationFrame(tick), delay);\n}\n\nconst GRADIENT_POSITIONS = ['80% 55%', '69% 34%', '8% 6%', '41% 38%', '86% 85%', '82% 18%', '51% 4%'];\nconst COLOR_MAP = [0, 1, 2, 0, 1, 2, 1];\n\nfunction buildMeshGradients(colors: string[]): string[] {\n const gradients: string[] = [];\n for (let i = 0; i < 7; i++) {\n const c = colors[Math.min(COLOR_MAP[i], colors.length - 1)];\n gradients.push(`radial-gradient(at ${GRADIENT_POSITIONS[i]}, ${c} 0px, transparent 50%)`);\n }\n gradients.push(`linear-gradient(${colors[0]} 0 100%)`);\n return gradients;\n}\n\nconst BorderGlow: React.FC = ({\n children,\n className = '',\n edgeSensitivity = 30,\n glowColor = '40 80 80',\n backgroundColor = '#060010',\n borderRadius = 28,\n glowRadius = 40,\n glowIntensity = 1.0,\n coneSpread = 25,\n animated = false,\n colors = ['#c084fc', '#f472b6', '#38bdf8'],\n fillOpacity = 0.5,\n}) => {\n const cardRef = useRef(null);\n const [isHovered, setIsHovered] = useState(false);\n const [cursorAngle, setCursorAngle] = useState(45);\n const [edgeProximity, setEdgeProximity] = useState(0);\n const [sweepActive, setSweepActive] = useState(false);\n\n const getCenterOfElement = useCallback((el: HTMLElement) => {\n const { width, height } = el.getBoundingClientRect();\n return [width / 2, height / 2];\n }, []);\n\n const getEdgeProximity = useCallback((el: HTMLElement, x: number, y: number) => {\n const [cx, cy] = getCenterOfElement(el);\n const dx = x - cx;\n const dy = y - cy;\n let kx = Infinity;\n let ky = Infinity;\n if (dx !== 0) kx = cx / Math.abs(dx);\n if (dy !== 0) ky = cy / Math.abs(dy);\n return Math.min(Math.max(1 / Math.min(kx, ky), 0), 1);\n }, [getCenterOfElement]);\n\n const getCursorAngle = useCallback((el: HTMLElement, x: number, y: number) => {\n const [cx, cy] = getCenterOfElement(el);\n const dx = x - cx;\n const dy = y - cy;\n if (dx === 0 && dy === 0) return 0;\n const radians = Math.atan2(dy, dx);\n let degrees = radians * (180 / Math.PI) + 90;\n if (degrees < 0) degrees += 360;\n return degrees;\n }, [getCenterOfElement]);\n\n const handlePointerMove = useCallback((e: React.PointerEvent) => {\n const card = cardRef.current;\n if (!card) return;\n const rect = card.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n setEdgeProximity(getEdgeProximity(card, x, y));\n setCursorAngle(getCursorAngle(card, x, y));\n }, [getEdgeProximity, getCursorAngle]);\n\n useEffect(() => {\n if (!animated) return;\n const angleStart = 110;\n const angleEnd = 465;\n setSweepActive(true);\n setCursorAngle(angleStart);\n\n animateValue({ duration: 500, onUpdate: v => setEdgeProximity(v / 100) });\n animateValue({ ease: easeInCubic, duration: 1500, end: 50, onUpdate: v => {\n setCursorAngle((angleEnd - angleStart) * (v / 100) + angleStart);\n }});\n animateValue({ ease: easeOutCubic, delay: 1500, duration: 2250, start: 50, end: 100, onUpdate: v => {\n setCursorAngle((angleEnd - angleStart) * (v / 100) + angleStart);\n }});\n animateValue({ ease: easeInCubic, delay: 2500, duration: 1500, start: 100, end: 0,\n onUpdate: v => setEdgeProximity(v / 100),\n onEnd: () => setSweepActive(false),\n });\n }, [animated]);\n\n const colorSensitivity = edgeSensitivity + 20;\n const isVisible = isHovered || sweepActive;\n const borderOpacity = isVisible\n ? Math.max(0, (edgeProximity * 100 - colorSensitivity) / (100 - colorSensitivity))\n : 0;\n const glowOpacity = isVisible\n ? Math.max(0, (edgeProximity * 100 - edgeSensitivity) / (100 - edgeSensitivity))\n : 0;\n\n const meshGradients = buildMeshGradients(colors);\n const borderBg = meshGradients.map(g => `${g} border-box`);\n const fillBg = meshGradients.map(g => `${g} padding-box`);\n const angleDeg = `${cursorAngle.toFixed(3)}deg`;\n\n return (\n setIsHovered(true)}\n onPointerLeave={() => setIsHovered(false)}\n className={`relative grid isolate border border-white/15 ${className}`}\n style={{\n background: backgroundColor,\n borderRadius: `${borderRadius}px`,\n transform: 'translate3d(0, 0, 0.01px)',\n boxShadow: 'rgba(0,0,0,0.1) 0 1px 2px, rgba(0,0,0,0.1) 0 2px 4px, rgba(0,0,0,0.1) 0 4px 8px, rgba(0,0,0,0.1) 0 8px 16px, rgba(0,0,0,0.1) 0 16px 32px, rgba(0,0,0,0.1) 0 32px 64px',\n }}\n >\n {/* mesh gradient border */}\n \n\n {/* mesh gradient fill near edges */}\n \n\n {/* outer glow */}\n \n \n
\n\n
\n {children}\n
\n \n );\n};\n\nexport default BorderGlow;\n" } ], "registryDependencies": [], "dependencies": [] } ================================================ FILE: public/r/BounceCards-JS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BounceCards-JS-CSS", "title": "BounceCards", "description": "Cards bounce that bounce in on mount.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BounceCards/BounceCards.css", "content": ".bounceCardsContainer {\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n width: 400px;\n height: 400px;\n}\n\n.card {\n position: absolute;\n width: 200px;\n aspect-ratio: 1;\n border: 5px solid #fff;\n border-radius: 25px;\n overflow: hidden;\n box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);\n}\n\n.card .image {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n" }, { "type": "registry:component", "path": "BounceCards/BounceCards.jsx", "content": "import { useEffect, useRef } from 'react';\nimport { gsap } from 'gsap';\nimport './BounceCards.css';\n\nexport default function BounceCards({\n className = '',\n images = [],\n containerWidth = 400,\n containerHeight = 400,\n animationDelay = 0.5,\n animationStagger = 0.06,\n easeType = 'elastic.out(1, 0.8)',\n transformStyles = [\n 'rotate(10deg) translate(-170px)',\n 'rotate(5deg) translate(-85px)',\n 'rotate(-3deg)',\n 'rotate(-10deg) translate(85px)',\n 'rotate(2deg) translate(170px)'\n ],\n enableHover = true\n}) {\n const containerRef = useRef(null);\n useEffect(() => {\n const ctx = gsap.context(() => {\n gsap.fromTo(\n '.card',\n { scale: 0 },\n {\n scale: 1,\n stagger: animationStagger,\n ease: easeType,\n delay: animationDelay\n }\n );\n }, containerRef);\n return () => ctx.revert();\n }, [animationStagger, easeType, animationDelay]);\n\n const getNoRotationTransform = transformStr => {\n const hasRotate = /rotate\\([\\s\\S]*?\\)/.test(transformStr);\n if (hasRotate) {\n return transformStr.replace(/rotate\\([\\s\\S]*?\\)/, 'rotate(0deg)');\n } else if (transformStr === 'none') {\n return 'rotate(0deg)';\n } else {\n return `${transformStr} rotate(0deg)`;\n }\n };\n\n const getPushedTransform = (baseTransform, offsetX) => {\n const translateRegex = /translate\\(([-0-9.]+)px\\)/;\n const match = baseTransform.match(translateRegex);\n if (match) {\n const currentX = parseFloat(match[1]);\n const newX = currentX + offsetX;\n return baseTransform.replace(translateRegex, `translate(${newX}px)`);\n } else {\n return baseTransform === 'none' ? `translate(${offsetX}px)` : `${baseTransform} translate(${offsetX}px)`;\n }\n };\n\n const pushSiblings = hoveredIdx => {\n if (!enableHover || !containerRef.current) return;\n\n const q = gsap.utils.selector(containerRef);\n\n images.forEach((_, i) => {\n const target = q(`.card-${i}`);\n gsap.killTweensOf(target);\n\n const baseTransform = transformStyles[i] || 'none';\n\n if (i === hoveredIdx) {\n const noRotationTransform = getNoRotationTransform(baseTransform);\n gsap.to(target, {\n transform: noRotationTransform,\n duration: 0.4,\n ease: 'back.out(1.4)',\n overwrite: 'auto'\n });\n } else {\n const offsetX = i < hoveredIdx ? -160 : 160;\n const pushedTransform = getPushedTransform(baseTransform, offsetX);\n\n const distance = Math.abs(hoveredIdx - i);\n const delay = distance * 0.05;\n\n gsap.to(target, {\n transform: pushedTransform,\n duration: 0.4,\n ease: 'back.out(1.4)',\n delay,\n overwrite: 'auto'\n });\n }\n });\n };\n\n const resetSiblings = () => {\n if (!enableHover || !containerRef.current) return;\n\n const q = gsap.utils.selector(containerRef);\n\n images.forEach((_, i) => {\n const target = q(`.card-${i}`);\n gsap.killTweensOf(target);\n const baseTransform = transformStyles[i] || 'none';\n gsap.to(target, {\n transform: baseTransform,\n duration: 0.4,\n ease: 'back.out(1.4)',\n overwrite: 'auto'\n });\n });\n };\n\n return (\n \n {images.map((src, idx) => (\n pushSiblings(idx)}\n onMouseLeave={resetSiblings}\n >\n {`card-${idx}`}\n \n ))}\n \n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/BounceCards-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BounceCards-JS-TW", "title": "BounceCards", "description": "Cards bounce that bounce in on mount.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BounceCards/BounceCards.jsx", "content": "import { useEffect, useRef } from 'react';\nimport { gsap } from 'gsap';\n\nexport default function BounceCards({\n className = '',\n images = [],\n containerWidth = 400,\n containerHeight = 400,\n animationDelay = 0.5,\n animationStagger = 0.06,\n easeType = 'elastic.out(1, 0.8)',\n transformStyles = [\n 'rotate(10deg) translate(-170px)',\n 'rotate(5deg) translate(-85px)',\n 'rotate(-3deg)',\n 'rotate(-10deg) translate(85px)',\n 'rotate(2deg) translate(170px)'\n ],\n enableHover = false\n}) {\n const containerRef = useRef(null);\n useEffect(() => {\n const ctx = gsap.context(() => {\n gsap.fromTo(\n '.card',\n { scale: 0 },\n {\n scale: 1,\n stagger: animationStagger,\n ease: easeType,\n delay: animationDelay\n }\n );\n }, containerRef);\n return () => ctx.revert();\n }, [animationStagger, easeType, animationDelay]);\n\n const getNoRotationTransform = transformStr => {\n const hasRotate = /rotate\\([\\s\\S]*?\\)/.test(transformStr);\n if (hasRotate) {\n return transformStr.replace(/rotate\\([\\s\\S]*?\\)/, 'rotate(0deg)');\n } else if (transformStr === 'none') {\n return 'rotate(0deg)';\n } else {\n return `${transformStr} rotate(0deg)`;\n }\n };\n\n const getPushedTransform = (baseTransform, offsetX) => {\n const translateRegex = /translate\\(([-0-9.]+)px\\)/;\n const match = baseTransform.match(translateRegex);\n if (match) {\n const currentX = parseFloat(match[1]);\n const newX = currentX + offsetX;\n return baseTransform.replace(translateRegex, `translate(${newX}px)`);\n } else {\n return baseTransform === 'none' ? `translate(${offsetX}px)` : `${baseTransform} translate(${offsetX}px)`;\n }\n };\n\n const pushSiblings = hoveredIdx => {\n if (!enableHover || !containerRef.current) return;\n\n const q = gsap.utils.selector(containerRef);\n images.forEach((_, i) => {\n const selector = q(`.card-${i}`);\n gsap.killTweensOf(selector);\n\n const baseTransform = transformStyles[i] || 'none';\n\n if (i === hoveredIdx) {\n const noRotation = getNoRotationTransform(baseTransform);\n gsap.to(selector, {\n transform: noRotation,\n duration: 0.4,\n ease: 'back.out(1.4)',\n overwrite: 'auto'\n });\n } else {\n const offsetX = i < hoveredIdx ? -160 : 160;\n const pushedTransform = getPushedTransform(baseTransform, offsetX);\n\n const distance = Math.abs(hoveredIdx - i);\n const delay = distance * 0.05;\n\n gsap.to(selector, {\n transform: pushedTransform,\n duration: 0.4,\n ease: 'back.out(1.4)',\n delay,\n overwrite: 'auto'\n });\n }\n });\n };\n\n const resetSiblings = () => {\n if (!enableHover || !containerRef.current) return;\n const q = gsap.utils.selector(containerRef);\n images.forEach((_, i) => {\n const selector = q(`.card-${i}`);\n gsap.killTweensOf(selector);\n\n const baseTransform = transformStyles[i] || 'none';\n gsap.to(selector, {\n transform: baseTransform,\n duration: 0.4,\n ease: 'back.out(1.4)',\n overwrite: 'auto'\n });\n });\n };\n\n return (\n \n {images.map((src, idx) => (\n pushSiblings(idx)}\n onMouseLeave={resetSiblings}\n >\n {`card-${idx}`}\n \n ))}\n \n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/BounceCards-TS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BounceCards-TS-CSS", "title": "BounceCards", "description": "Cards bounce that bounce in on mount.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BounceCards/BounceCards.css", "content": ".bounceCardsContainer {\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n width: 400px;\n height: 400px;\n}\n\n.card {\n position: absolute;\n width: 200px;\n aspect-ratio: 1;\n border: 5px solid #fff;\n border-radius: 25px;\n overflow: hidden;\n box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);\n}\n\n.card .image {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n" }, { "type": "registry:component", "path": "BounceCards/BounceCards.tsx", "content": "import { useEffect, useRef } from 'react';\nimport { gsap } from 'gsap';\nimport './BounceCards.css';\n\ninterface BounceCardsProps {\n className?: string;\n images?: string[];\n containerWidth?: number;\n containerHeight?: number;\n animationDelay?: number;\n animationStagger?: number;\n easeType?: string;\n transformStyles?: string[];\n enableHover?: boolean;\n}\n\nexport default function BounceCards({\n className = '',\n images = [],\n containerWidth = 400,\n containerHeight = 400,\n animationDelay = 0.5,\n animationStagger = 0.06,\n easeType = 'elastic.out(1, 0.8)',\n transformStyles = [\n 'rotate(10deg) translate(-170px)',\n 'rotate(5deg) translate(-85px)',\n 'rotate(-3deg)',\n 'rotate(-10deg) translate(85px)',\n 'rotate(2deg) translate(170px)'\n ],\n enableHover = false\n}: BounceCardsProps) {\n const containerRef = useRef(null);\n\n useEffect(() => {\n const ctx = gsap.context(() => {\n gsap.fromTo(\n '.card',\n { scale: 0 },\n {\n scale: 1,\n stagger: animationStagger,\n ease: easeType,\n delay: animationDelay\n }\n );\n }, containerRef);\n return () => ctx.revert();\n }, [animationStagger, easeType, animationDelay]);\n\n const getNoRotationTransform = (transformStr: string): string => {\n const hasRotate = /rotate\\([\\s\\S]*?\\)/.test(transformStr);\n if (hasRotate) {\n return transformStr.replace(/rotate\\([\\s\\S]*?\\)/, 'rotate(0deg)');\n } else if (transformStr === 'none') {\n return 'rotate(0deg)';\n } else {\n return `${transformStr} rotate(0deg)`;\n }\n };\n\n const getPushedTransform = (baseTransform: string, offsetX: number): string => {\n const translateRegex = /translate\\(([-0-9.]+)px\\)/;\n const match = baseTransform.match(translateRegex);\n if (match) {\n const currentX = parseFloat(match[1]);\n const newX = currentX + offsetX;\n return baseTransform.replace(translateRegex, `translate(${newX}px)`);\n } else {\n return baseTransform === 'none' ? `translate(${offsetX}px)` : `${baseTransform} translate(${offsetX}px)`;\n }\n };\n\n const pushSiblings = (hoveredIdx: number) => {\n if (!enableHover || !containerRef.current) return;\n const q = gsap.utils.selector(containerRef);\n images.forEach((_, i) => {\n const selector = q(`.card-${i}`);\n gsap.killTweensOf(selector);\n\n const baseTransform = transformStyles[i] || 'none';\n\n if (i === hoveredIdx) {\n const noRotation = getNoRotationTransform(baseTransform);\n gsap.to(selector, {\n transform: noRotation,\n duration: 0.4,\n ease: 'back.out(1.4)',\n overwrite: 'auto'\n });\n } else {\n const offsetX = i < hoveredIdx ? -160 : 160;\n const pushedTransform = getPushedTransform(baseTransform, offsetX);\n\n const distance = Math.abs(hoveredIdx - i);\n const delay = distance * 0.05;\n\n gsap.to(selector, {\n transform: pushedTransform,\n duration: 0.4,\n ease: 'back.out(1.4)',\n delay,\n overwrite: 'auto'\n });\n }\n });\n };\n\n const resetSiblings = () => {\n if (!enableHover || !containerRef.current) return;\n const q = gsap.utils.selector(containerRef);\n images.forEach((_, i) => {\n const selector = q(`.card-${i}`);\n gsap.killTweensOf(selector);\n const baseTransform = transformStyles[i] || 'none';\n gsap.to(selector, {\n transform: baseTransform,\n duration: 0.4,\n ease: 'back.out(1.4)',\n overwrite: 'auto'\n });\n });\n };\n\n return (\n \n {images.map((src, idx) => (\n pushSiblings(idx)}\n onMouseLeave={resetSiblings}\n >\n {`card-${idx}`}\n \n ))}\n \n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/BounceCards-TS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BounceCards-TS-TW", "title": "BounceCards", "description": "Cards bounce that bounce in on mount.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BounceCards/BounceCards.tsx", "content": "import { useEffect, useRef } from 'react';\nimport { gsap } from 'gsap';\n\ninterface BounceCardsProps {\n className?: string;\n images?: string[];\n containerWidth?: number;\n containerHeight?: number;\n animationDelay?: number;\n animationStagger?: number;\n easeType?: string;\n transformStyles?: string[];\n enableHover?: boolean;\n}\n\nexport default function BounceCards({\n className = '',\n images = [],\n containerWidth = 400,\n containerHeight = 400,\n animationDelay = 0.5,\n animationStagger = 0.06,\n easeType = 'elastic.out(1, 0.8)',\n transformStyles = [\n 'rotate(10deg) translate(-170px)',\n 'rotate(5deg) translate(-85px)',\n 'rotate(-3deg)',\n 'rotate(-10deg) translate(85px)',\n 'rotate(2deg) translate(170px)'\n ],\n enableHover = false\n}: BounceCardsProps) {\n const containerRef = useRef(null);\n useEffect(() => {\n const ctx = gsap.context(() => {\n gsap.fromTo(\n '.card',\n { scale: 0 },\n {\n scale: 1,\n stagger: animationStagger,\n ease: easeType,\n delay: animationDelay\n }\n );\n }, containerRef);\n return () => ctx.revert();\n }, [animationDelay, animationStagger, easeType]);\n\n const getNoRotationTransform = (transformStr: string): string => {\n const hasRotate = /rotate\\([\\s\\S]*?\\)/.test(transformStr);\n if (hasRotate) {\n return transformStr.replace(/rotate\\([\\s\\S]*?\\)/, 'rotate(0deg)');\n } else if (transformStr === 'none') {\n return 'rotate(0deg)';\n } else {\n return `${transformStr} rotate(0deg)`;\n }\n };\n\n const getPushedTransform = (baseTransform: string, offsetX: number): string => {\n const translateRegex = /translate\\(([-0-9.]+)px\\)/;\n const match = baseTransform.match(translateRegex);\n if (match) {\n const currentX = parseFloat(match[1]);\n const newX = currentX + offsetX;\n return baseTransform.replace(translateRegex, `translate(${newX}px)`);\n } else {\n return baseTransform === 'none' ? `translate(${offsetX}px)` : `${baseTransform} translate(${offsetX}px)`;\n }\n };\n\n const pushSiblings = (hoveredIdx: number) => {\n const q = gsap.utils.selector(containerRef);\n if (!enableHover || !containerRef.current) return;\n\n images.forEach((_, i) => {\n const selector = q(`.card-${i}`);\n gsap.killTweensOf(selector);\n\n const baseTransform = transformStyles[i] || 'none';\n\n if (i === hoveredIdx) {\n const noRotation = getNoRotationTransform(baseTransform);\n gsap.to(selector, {\n transform: noRotation,\n duration: 0.4,\n ease: 'back.out(1.4)',\n overwrite: 'auto'\n });\n } else {\n const offsetX = i < hoveredIdx ? -160 : 160;\n const pushedTransform = getPushedTransform(baseTransform, offsetX);\n\n const distance = Math.abs(hoveredIdx - i);\n const delay = distance * 0.05;\n\n gsap.to(selector, {\n transform: pushedTransform,\n duration: 0.4,\n ease: 'back.out(1.4)',\n delay,\n overwrite: 'auto'\n });\n }\n });\n };\n\n const resetSiblings = () => {\n if (!enableHover || !containerRef.current) return;\n const q = gsap.utils.selector(containerRef);\n\n images.forEach((_, i) => {\n const selector = q(`.card-${i}`);\n gsap.killTweensOf(selector);\n\n const baseTransform = transformStyles[i] || 'none';\n gsap.to(selector, {\n transform: baseTransform,\n duration: 0.4,\n ease: 'back.out(1.4)',\n overwrite: 'auto'\n });\n });\n };\n\n return (\n \n {images.map((src, idx) => (\n pushSiblings(idx)}\n onMouseLeave={resetSiblings}\n >\n {`card-${idx}`}\n \n ))}\n \n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/BubbleMenu-JS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BubbleMenu-JS-CSS", "title": "BubbleMenu", "description": "Floating circular expanding menu with staggered item reveal.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BubbleMenu/BubbleMenu.css", "content": ".bubble-menu {\n left: 0;\n right: 0;\n top: 2em;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n padding: 0 2em;\n pointer-events: none;\n z-index: 99;\n}\n\n.bubble-menu.fixed {\n position: fixed;\n}\n\n.bubble-menu.absolute {\n position: absolute;\n}\n\n.bubble-menu .bubble {\n --bubble-size: 48px;\n width: var(--bubble-size);\n height: var(--bubble-size);\n border-radius: 50%;\n background: #fff;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n pointer-events: auto;\n}\n\n.bubble-menu .logo-bubble,\n.bubble-menu .toggle-bubble {\n will-change: transform;\n}\n\n.bubble-menu .logo-bubble {\n width: auto;\n min-height: var(--bubble-size);\n height: var(--bubble-size);\n padding: 0 16px;\n border-radius: calc(var(--bubble-size) / 2);\n gap: 8px;\n}\n\n.bubble-menu .toggle-bubble {\n width: var(--bubble-size);\n height: var(--bubble-size);\n}\n\n.bubble-menu .bubble-logo {\n max-height: 60%;\n max-width: 100%;\n object-fit: contain;\n display: block;\n}\n\n.bubble-menu .logo-content {\n --logo-max-height: 60%;\n --logo-max-width: 100%;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 120px;\n height: 100%;\n}\n\n.bubble-menu .logo-content > .bubble-logo,\n.bubble-menu .logo-content > img,\n.bubble-menu .logo-content > svg {\n max-height: var(--logo-max-height);\n max-width: var(--logo-max-width);\n}\n\n.bubble-menu .menu-btn {\n border: none;\n background: #fff;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 0;\n}\n\n.bubble-menu .menu-line {\n width: 26px;\n height: 2px;\n background: #111;\n border-radius: 2px;\n display: block;\n margin: 0 auto;\n transition:\n transform 0.3s ease,\n opacity 0.3s ease;\n transform-origin: center;\n}\n\n.bubble-menu .menu-line + .menu-line {\n margin-top: 6px;\n}\n\n.bubble-menu .menu-btn.open .menu-line:first-child {\n transform: translateY(4px) rotate(45deg);\n}\n\n.bubble-menu .menu-btn.open .menu-line:last-child {\n transform: translateY(-4px) rotate(-45deg);\n}\n\n@media (min-width: 768px) {\n .bubble-menu .bubble {\n --bubble-size: 56px;\n }\n\n .bubble-menu .logo-bubble {\n padding: 0 16px;\n }\n}\n\n.bubble-menu-items {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n z-index: 98;\n}\n\n.bubble-menu-items.fixed {\n position: fixed;\n}\n\n.bubble-menu-items.absolute {\n position: absolute;\n}\n\n.bubble-menu-items .pill-list {\n list-style: none;\n margin: 0;\n padding: 0 24px;\n display: flex;\n flex-wrap: wrap;\n gap: 0;\n row-gap: 4px;\n width: 100%;\n max-width: 1600px;\n margin-left: auto;\n margin-right: auto;\n pointer-events: auto;\n justify-content: stretch;\n}\n\n.bubble-menu-items .pill-list .pill-spacer {\n width: 100%;\n height: 0;\n pointer-events: none;\n}\n\n.bubble-menu-items .pill-list .pill-col {\n display: flex;\n justify-content: center;\n align-items: stretch;\n flex: 0 0 calc(100% / 3);\n box-sizing: border-box;\n}\n\n.bubble-menu-items .pill-list .pill-col:nth-child(4):nth-last-child(2) {\n margin-left: calc(100% / 6);\n}\n\n.bubble-menu-items .pill-list .pill-col:nth-child(4):last-child {\n margin-left: calc(100% / 3);\n}\n\n.bubble-menu-items .pill-link {\n --pill-bg: #ffffff;\n --pill-color: #111;\n --pill-border: rgba(0, 0, 0, 0.12);\n --item-rot: 0deg;\n --pill-min-h: 160px;\n --hover-bg: #f3f4f6;\n --hover-color: #111;\n width: 100%;\n min-height: var(--pill-min-h);\n padding: clamp(1.5rem, 3vw, 8rem) 0;\n font-size: clamp(1.5rem, 4vw, 4rem);\n font-weight: 400;\n line-height: 0;\n border-radius: 999px;\n background: var(--pill-bg);\n color: var(--pill-color);\n text-decoration: none;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.1);\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n transition:\n background 0.3s ease,\n color 0.3s ease;\n will-change: transform;\n box-sizing: border-box;\n white-space: nowrap;\n overflow: hidden;\n height: 10px;\n}\n\n@media (min-width: 900px) {\n .bubble-menu-items .pill-link {\n transform: rotate(var(--item-rot));\n }\n\n .bubble-menu-items .pill-link:hover {\n transform: rotate(var(--item-rot)) scale(1.06);\n background: var(--hover-bg);\n color: var(--hover-color);\n }\n\n .bubble-menu-items .pill-link:active {\n transform: rotate(var(--item-rot)) scale(0.94);\n }\n}\n\n.bubble-menu-items .pill-link .pill-label {\n display: inline-block;\n will-change: transform, opacity;\n height: 1.2em;\n line-height: 1.2;\n}\n\n@media (max-width: 899px) {\n .bubble-menu-items {\n padding-top: 0px;\n align-items: flex-start;\n padding-top: 120px;\n }\n\n .bubble-menu-items .pill-list {\n row-gap: 16px;\n }\n\n .bubble-menu-items .pill-list .pill-col {\n flex: 0 0 100%;\n margin-left: 0 !important;\n overflow: visible;\n }\n\n .bubble-menu-items .pill-link {\n font-size: clamp(1.2rem, 3vw, 4rem);\n padding: clamp(1rem, 2vw, 2rem) 0;\n min-height: 80px;\n }\n\n .bubble-menu-items .pill-link:hover {\n transform: scale(1.06);\n background: var(--hover-bg);\n color: var(--hover-color);\n }\n\n .bubble-menu-items .pill-link:active {\n transform: scale(0.94);\n }\n}\n" }, { "type": "registry:component", "path": "BubbleMenu/BubbleMenu.jsx", "content": "import { useState, useRef, useEffect } from 'react';\nimport { gsap } from 'gsap';\n\nimport './BubbleMenu.css';\n\nconst DEFAULT_ITEMS = [\n {\n label: 'home',\n href: '#',\n ariaLabel: 'Home',\n rotation: -8,\n hoverStyles: { bgColor: '#3b82f6', textColor: '#ffffff' }\n },\n {\n label: 'about',\n href: '#',\n ariaLabel: 'About',\n rotation: 8,\n hoverStyles: { bgColor: '#10b981', textColor: '#ffffff' }\n },\n {\n label: 'projects',\n href: '#',\n ariaLabel: 'Documentation',\n rotation: 8,\n hoverStyles: { bgColor: '#f59e0b', textColor: '#ffffff' }\n },\n {\n label: 'blog',\n href: '#',\n ariaLabel: 'Blog',\n rotation: 8,\n hoverStyles: { bgColor: '#ef4444', textColor: '#ffffff' }\n },\n {\n label: 'contact',\n href: '#',\n ariaLabel: 'Contact',\n rotation: -8,\n hoverStyles: { bgColor: '#8b5cf6', textColor: '#ffffff' }\n }\n];\n\nexport default function BubbleMenu({\n logo,\n onMenuClick,\n className,\n style,\n menuAriaLabel = 'Toggle menu',\n menuBg = '#fff',\n menuContentColor = '#111',\n useFixedPosition = false,\n items,\n animationEase = 'back.out(1.5)',\n animationDuration = 0.5,\n staggerDelay = 0.12\n}) {\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [showOverlay, setShowOverlay] = useState(false);\n\n const overlayRef = useRef(null);\n const bubblesRef = useRef([]);\n const labelRefs = useRef([]);\n\n const menuItems = items?.length ? items : DEFAULT_ITEMS;\n const containerClassName = ['bubble-menu', useFixedPosition ? 'fixed' : 'absolute', className]\n .filter(Boolean)\n .join(' ');\n\n const handleToggle = () => {\n const nextState = !isMenuOpen;\n if (nextState) setShowOverlay(true);\n setIsMenuOpen(nextState);\n onMenuClick?.(nextState);\n };\n\n useEffect(() => {\n const overlay = overlayRef.current;\n const bubbles = bubblesRef.current.filter(Boolean);\n const labels = labelRefs.current.filter(Boolean);\n\n if (!overlay || !bubbles.length) return;\n\n if (isMenuOpen) {\n gsap.set(overlay, { display: 'flex' });\n gsap.killTweensOf([...bubbles, ...labels]);\n gsap.set(bubbles, { scale: 0, transformOrigin: '50% 50%' });\n gsap.set(labels, { y: 24, autoAlpha: 0 });\n\n bubbles.forEach((bubble, i) => {\n const delay = i * staggerDelay + gsap.utils.random(-0.05, 0.05);\n const tl = gsap.timeline({ delay });\n\n tl.to(bubble, {\n scale: 1,\n duration: animationDuration,\n ease: animationEase\n });\n if (labels[i]) {\n tl.to(\n labels[i],\n {\n y: 0,\n autoAlpha: 1,\n duration: animationDuration,\n ease: 'power3.out'\n },\n `-=${animationDuration * 0.9}`\n );\n }\n });\n } else if (showOverlay) {\n gsap.killTweensOf([...bubbles, ...labels]);\n gsap.to(labels, {\n y: 24,\n autoAlpha: 0,\n duration: 0.2,\n ease: 'power3.in'\n });\n gsap.to(bubbles, {\n scale: 0,\n duration: 0.2,\n ease: 'power3.in',\n onComplete: () => {\n gsap.set(overlay, { display: 'none' });\n setShowOverlay(false);\n }\n });\n }\n }, [isMenuOpen, showOverlay, animationEase, animationDuration, staggerDelay]);\n\n useEffect(() => {\n const handleResize = () => {\n if (isMenuOpen) {\n const bubbles = bubblesRef.current.filter(Boolean);\n const isDesktop = window.innerWidth >= 900;\n\n bubbles.forEach((bubble, i) => {\n const item = menuItems[i];\n if (bubble && item) {\n const rotation = isDesktop ? (item.rotation ?? 0) : 0;\n gsap.set(bubble, { rotation });\n }\n });\n }\n };\n\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n }, [isMenuOpen, menuItems]);\n\n return (\n <>\n \n {showOverlay && (\n \n
    \n {menuItems.map((item, idx) => (\n
  • \n {\n if (el) bubblesRef.current[idx] = el;\n }}\n >\n {\n if (el) labelRefs.current[idx] = el;\n }}\n >\n {item.label}\n \n \n
  • \n ))}\n
\n \n )}\n \n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/BubbleMenu-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BubbleMenu-JS-TW", "title": "BubbleMenu", "description": "Floating circular expanding menu with staggered item reveal.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BubbleMenu/BubbleMenu.jsx", "content": "import { useEffect, useRef, useState } from 'react';\nimport { gsap } from 'gsap';\n\nconst DEFAULT_ITEMS = [\n {\n label: 'home',\n href: '#',\n ariaLabel: 'Home',\n rotation: -8,\n hoverStyles: { bgColor: '#3b82f6', textColor: '#ffffff' }\n },\n {\n label: 'about',\n href: '#',\n ariaLabel: 'About',\n rotation: 8,\n hoverStyles: { bgColor: '#10b981', textColor: '#ffffff' }\n },\n {\n label: 'projects',\n href: '#',\n ariaLabel: 'Documentation',\n rotation: 8,\n hoverStyles: { bgColor: '#f59e0b', textColor: '#ffffff' }\n },\n {\n label: 'blog',\n href: '#',\n ariaLabel: 'Blog',\n rotation: 8,\n hoverStyles: { bgColor: '#ef4444', textColor: '#ffffff' }\n },\n {\n label: 'contact',\n href: '#',\n ariaLabel: 'Contact',\n rotation: -8,\n hoverStyles: { bgColor: '#8b5cf6', textColor: '#ffffff' }\n }\n];\n\nexport default function BubbleMenu({\n logo,\n onMenuClick,\n className,\n style,\n menuAriaLabel = 'Toggle menu',\n menuBg = '#fff',\n menuContentColor = '#111',\n useFixedPosition = false,\n items,\n animationEase = 'back.out(1.5)',\n animationDuration = 0.5,\n staggerDelay = 0.12\n}) {\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [showOverlay, setShowOverlay] = useState(false);\n\n const overlayRef = useRef(null);\n const bubblesRef = useRef([]);\n const labelRefs = useRef([]);\n\n const menuItems = items?.length ? items : DEFAULT_ITEMS;\n\n const containerClassName = [\n 'bubble-menu',\n useFixedPosition ? 'fixed' : 'absolute',\n 'left-0 right-0 top-8',\n 'flex items-center justify-between',\n 'gap-4 px-8',\n 'pointer-events-none',\n 'z-[1001]',\n className\n ]\n .filter(Boolean)\n .join(' ');\n\n const handleToggle = () => {\n const nextState = !isMenuOpen;\n if (nextState) setShowOverlay(true);\n setIsMenuOpen(nextState);\n onMenuClick?.(nextState);\n };\n\n useEffect(() => {\n const overlay = overlayRef.current;\n const bubbles = bubblesRef.current.filter(Boolean);\n const labels = labelRefs.current.filter(Boolean);\n if (!overlay || !bubbles.length) return;\n\n if (isMenuOpen) {\n gsap.set(overlay, { display: 'flex' });\n gsap.killTweensOf([...bubbles, ...labels]);\n gsap.set(bubbles, { scale: 0, transformOrigin: '50% 50%' });\n gsap.set(labels, { y: 24, autoAlpha: 0 });\n\n bubbles.forEach((bubble, i) => {\n const delay = i * staggerDelay + gsap.utils.random(-0.05, 0.05);\n const tl = gsap.timeline({ delay });\n tl.to(bubble, {\n scale: 1,\n duration: animationDuration,\n ease: animationEase\n });\n if (labels[i]) {\n tl.to(\n labels[i],\n {\n y: 0,\n autoAlpha: 1,\n duration: animationDuration,\n ease: 'power3.out'\n },\n '-=' + animationDuration * 0.9\n );\n }\n });\n } else if (showOverlay) {\n gsap.killTweensOf([...bubbles, ...labels]);\n gsap.to(labels, {\n y: 24,\n autoAlpha: 0,\n duration: 0.2,\n ease: 'power3.in'\n });\n gsap.to(bubbles, {\n scale: 0,\n duration: 0.2,\n ease: 'power3.in',\n onComplete: () => {\n gsap.set(overlay, { display: 'none' });\n setShowOverlay(false);\n }\n });\n }\n }, [isMenuOpen, showOverlay, animationEase, animationDuration, staggerDelay]);\n\n useEffect(() => {\n const handleResize = () => {\n if (isMenuOpen) {\n const bubbles = bubblesRef.current.filter(Boolean);\n const isDesktop = window.innerWidth >= 900;\n bubbles.forEach((bubble, i) => {\n const item = menuItems[i];\n if (bubble && item) {\n const rotation = isDesktop ? (item.rotation ?? 0) : 0;\n gsap.set(bubble, { rotation });\n }\n });\n }\n };\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n }, [isMenuOpen, menuItems]);\n\n return (\n <>\n {/* Workaround for silly Tailwind capabilities */}\n \n\n \n\n {showOverlay && (\n \n \n {menuItems.map((item, idx) => (\n \n {\n if (el) bubblesRef.current[idx] = el;\n }}\n >\n {\n if (el) labelRefs.current[idx] = el;\n }}\n >\n {item.label}\n \n \n \n ))}\n \n \n )}\n \n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/BubbleMenu-TS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BubbleMenu-TS-CSS", "title": "BubbleMenu", "description": "Floating circular expanding menu with staggered item reveal.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BubbleMenu/BubbleMenu.css", "content": ".bubble-menu {\n left: 0;\n right: 0;\n top: 2em;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n padding: 0 2em;\n pointer-events: none;\n z-index: 99;\n}\n\n.bubble-menu.fixed {\n position: fixed;\n}\n\n.bubble-menu.absolute {\n position: absolute;\n}\n\n.bubble-menu .bubble {\n --bubble-size: 48px;\n width: var(--bubble-size);\n height: var(--bubble-size);\n border-radius: 50%;\n background: #fff;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n pointer-events: auto;\n}\n\n.bubble-menu .logo-bubble,\n.bubble-menu .toggle-bubble {\n will-change: transform;\n}\n\n.bubble-menu .logo-bubble {\n width: auto;\n min-height: var(--bubble-size);\n height: var(--bubble-size);\n padding: 0 16px;\n border-radius: calc(var(--bubble-size) / 2);\n gap: 8px;\n}\n\n.bubble-menu .toggle-bubble {\n width: var(--bubble-size);\n height: var(--bubble-size);\n}\n\n.bubble-menu .bubble-logo {\n max-height: 60%;\n max-width: 100%;\n object-fit: contain;\n display: block;\n}\n\n.bubble-menu .logo-content {\n --logo-max-height: 60%;\n --logo-max-width: 100%;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 120px;\n height: 100%;\n}\n\n.bubble-menu .logo-content > .bubble-logo,\n.bubble-menu .logo-content > img,\n.bubble-menu .logo-content > svg {\n max-height: var(--logo-max-height);\n max-width: var(--logo-max-width);\n}\n\n.bubble-menu .menu-btn {\n border: none;\n background: #fff;\n cursor: pointer;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 0;\n}\n\n.bubble-menu .menu-line {\n width: 26px;\n height: 2px;\n background: #111;\n border-radius: 2px;\n display: block;\n margin: 0 auto;\n transition:\n transform 0.3s ease,\n opacity 0.3s ease;\n transform-origin: center;\n}\n\n.bubble-menu .menu-line + .menu-line {\n margin-top: 6px;\n}\n\n.bubble-menu .menu-btn.open .menu-line:first-child {\n transform: translateY(4px) rotate(45deg);\n}\n\n.bubble-menu .menu-btn.open .menu-line:last-child {\n transform: translateY(-4px) rotate(-45deg);\n}\n\n@media (min-width: 768px) {\n .bubble-menu .bubble {\n --bubble-size: 56px;\n }\n\n .bubble-menu .logo-bubble {\n padding: 0 16px;\n }\n}\n\n.bubble-menu-items {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n z-index: 98;\n}\n\n.bubble-menu-items.fixed {\n position: fixed;\n}\n\n.bubble-menu-items.absolute {\n position: absolute;\n}\n\n.bubble-menu-items .pill-list {\n list-style: none;\n margin: 0;\n padding: 0 24px;\n display: flex;\n flex-wrap: wrap;\n gap: 0;\n row-gap: 4px;\n width: 100%;\n max-width: 1600px;\n margin-left: auto;\n margin-right: auto;\n pointer-events: auto;\n justify-content: stretch;\n}\n\n.bubble-menu-items .pill-list .pill-spacer {\n width: 100%;\n height: 0;\n pointer-events: none;\n}\n\n.bubble-menu-items .pill-list .pill-col {\n display: flex;\n justify-content: center;\n align-items: stretch;\n flex: 0 0 calc(100% / 3);\n box-sizing: border-box;\n}\n\n.bubble-menu-items .pill-list .pill-col:nth-child(4):nth-last-child(2) {\n margin-left: calc(100% / 6);\n}\n\n.bubble-menu-items .pill-list .pill-col:nth-child(4):last-child {\n margin-left: calc(100% / 3);\n}\n\n.bubble-menu-items .pill-link {\n --pill-bg: #ffffff;\n --pill-color: #111;\n --pill-border: rgba(0, 0, 0, 0.12);\n --item-rot: 0deg;\n --pill-min-h: 160px;\n --hover-bg: #f3f4f6;\n --hover-color: #111;\n width: 100%;\n min-height: var(--pill-min-h);\n padding: clamp(1.5rem, 3vw, 8rem) 0;\n font-size: clamp(1.5rem, 4vw, 4rem);\n font-weight: 400;\n line-height: 0;\n border-radius: 999px;\n background: var(--pill-bg);\n color: var(--pill-color);\n text-decoration: none;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.1);\n display: flex;\n align-items: center;\n justify-content: center;\n position: relative;\n transition:\n background 0.3s ease,\n color 0.3s ease;\n will-change: transform;\n box-sizing: border-box;\n white-space: nowrap;\n overflow: hidden;\n height: 10px;\n}\n\n@media (min-width: 900px) {\n .bubble-menu-items .pill-link {\n transform: rotate(var(--item-rot));\n }\n\n .bubble-menu-items .pill-link:hover {\n transform: rotate(var(--item-rot)) scale(1.06);\n background: var(--hover-bg);\n color: var(--hover-color);\n }\n\n .bubble-menu-items .pill-link:active {\n transform: rotate(var(--item-rot)) scale(0.94);\n }\n}\n\n.bubble-menu-items .pill-link .pill-label {\n display: inline-block;\n will-change: transform, opacity;\n height: 1.2em;\n line-height: 1.2;\n}\n\n@media (max-width: 899px) {\n .bubble-menu-items {\n padding-top: 0px;\n align-items: flex-start;\n padding-top: 120px;\n }\n\n .bubble-menu-items .pill-list {\n row-gap: 16px;\n }\n\n .bubble-menu-items .pill-list .pill-col {\n flex: 0 0 100%;\n margin-left: 0 !important;\n overflow: visible;\n }\n\n .bubble-menu-items .pill-link {\n font-size: clamp(1.2rem, 3vw, 4rem);\n padding: clamp(1rem, 2vw, 2rem) 0;\n min-height: 80px;\n }\n\n .bubble-menu-items .pill-link:hover {\n transform: scale(1.06);\n background: var(--hover-bg);\n color: var(--hover-color);\n }\n\n .bubble-menu-items .pill-link:active {\n transform: scale(0.94);\n }\n}\n" }, { "type": "registry:component", "path": "BubbleMenu/BubbleMenu.tsx", "content": "import type { CSSProperties, ReactNode } from 'react';\nimport { useState, useRef, useEffect } from 'react';\nimport { gsap } from 'gsap';\n\nimport './BubbleMenu.css';\n\ntype MenuItem = {\n label: string;\n href: string;\n ariaLabel?: string;\n rotation?: number;\n hoverStyles?: {\n bgColor?: string;\n textColor?: string;\n };\n};\n\nexport type BubbleMenuProps = {\n logo: ReactNode | string;\n onMenuClick?: (open: boolean) => void;\n className?: string;\n style?: CSSProperties;\n menuAriaLabel?: string;\n menuBg?: string;\n menuContentColor?: string;\n useFixedPosition?: boolean;\n items?: MenuItem[];\n animationEase?: string;\n animationDuration?: number;\n staggerDelay?: number;\n};\n\nconst DEFAULT_ITEMS: MenuItem[] = [\n {\n label: 'home',\n href: '#',\n ariaLabel: 'Home',\n rotation: -8,\n hoverStyles: { bgColor: '#3b82f6', textColor: '#ffffff' }\n },\n {\n label: 'about',\n href: '#',\n ariaLabel: 'About',\n rotation: 8,\n hoverStyles: { bgColor: '#10b981', textColor: '#ffffff' }\n },\n {\n label: 'projects',\n href: '#',\n ariaLabel: 'Documentation',\n rotation: 8,\n hoverStyles: { bgColor: '#f59e0b', textColor: '#ffffff' }\n },\n {\n label: 'blog',\n href: '#',\n ariaLabel: 'Blog',\n rotation: 8,\n hoverStyles: { bgColor: '#ef4444', textColor: '#ffffff' }\n },\n {\n label: 'contact',\n href: '#',\n ariaLabel: 'Contact',\n rotation: -8,\n hoverStyles: { bgColor: '#8b5cf6', textColor: '#ffffff' }\n }\n];\n\nexport default function BubbleMenu({\n logo,\n onMenuClick,\n className,\n style,\n menuAriaLabel = 'Toggle menu',\n menuBg = '#fff',\n menuContentColor = '#111',\n useFixedPosition = false,\n items,\n animationEase = 'back.out(1.5)',\n animationDuration = 0.5,\n staggerDelay = 0.12\n}: BubbleMenuProps) {\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [showOverlay, setShowOverlay] = useState(false);\n\n const overlayRef = useRef(null);\n const bubblesRef = useRef([]);\n const labelRefs = useRef([]);\n\n const menuItems = items?.length ? items : DEFAULT_ITEMS;\n const containerClassName = ['bubble-menu', useFixedPosition ? 'fixed' : 'absolute', className]\n .filter(Boolean)\n .join(' ');\n\n const handleToggle = () => {\n const nextState = !isMenuOpen;\n if (nextState) setShowOverlay(true);\n setIsMenuOpen(nextState);\n onMenuClick?.(nextState);\n };\n\n useEffect(() => {\n const overlay = overlayRef.current;\n const bubbles = bubblesRef.current.filter(Boolean);\n const labels = labelRefs.current.filter(Boolean);\n\n if (!overlay || !bubbles.length) return;\n\n if (isMenuOpen) {\n gsap.set(overlay, { display: 'flex' });\n gsap.killTweensOf([...bubbles, ...labels]);\n gsap.set(bubbles, { scale: 0, transformOrigin: '50% 50%' });\n gsap.set(labels, { y: 24, autoAlpha: 0 });\n\n bubbles.forEach((bubble, i) => {\n const delay = i * staggerDelay + gsap.utils.random(-0.05, 0.05);\n const tl = gsap.timeline({ delay });\n\n tl.to(bubble, {\n scale: 1,\n duration: animationDuration,\n ease: animationEase\n });\n if (labels[i]) {\n tl.to(\n labels[i],\n {\n y: 0,\n autoAlpha: 1,\n duration: animationDuration,\n ease: 'power3.out'\n },\n `-=${animationDuration * 0.9}`\n );\n }\n });\n } else if (showOverlay) {\n gsap.killTweensOf([...bubbles, ...labels]);\n gsap.to(labels, {\n y: 24,\n autoAlpha: 0,\n duration: 0.2,\n ease: 'power3.in'\n });\n gsap.to(bubbles, {\n scale: 0,\n duration: 0.2,\n ease: 'power3.in',\n onComplete: () => {\n gsap.set(overlay, { display: 'none' });\n setShowOverlay(false);\n }\n });\n }\n }, [isMenuOpen, showOverlay, animationEase, animationDuration, staggerDelay]);\n\n useEffect(() => {\n const handleResize = () => {\n if (isMenuOpen) {\n const bubbles = bubblesRef.current.filter(Boolean);\n const isDesktop = window.innerWidth >= 900;\n\n bubbles.forEach((bubble, i) => {\n const item = menuItems[i];\n if (bubble && item) {\n const rotation = isDesktop ? (item.rotation ?? 0) : 0;\n gsap.set(bubble, { rotation });\n }\n });\n }\n };\n\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n }, [isMenuOpen, menuItems]);\n\n return (\n <>\n \n {showOverlay && (\n \n
    \n {menuItems.map((item, idx) => (\n
  • \n {\n if (el) bubblesRef.current[idx] = el;\n }}\n >\n {\n if (el) labelRefs.current[idx] = el;\n }}\n >\n {item.label}\n \n \n
  • \n ))}\n
\n \n )}\n \n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/BubbleMenu-TS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "BubbleMenu-TS-TW", "title": "BubbleMenu", "description": "Floating circular expanding menu with staggered item reveal.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "BubbleMenu/BubbleMenu.tsx", "content": "import type { CSSProperties, ReactNode } from 'react';\nimport { useEffect, useRef, useState } from 'react';\nimport { gsap } from 'gsap';\n\ntype MenuItem = {\n label: string;\n href: string;\n ariaLabel?: string;\n rotation?: number;\n hoverStyles?: {\n bgColor?: string;\n textColor?: string;\n };\n};\n\nexport type BubbleMenuProps = {\n logo: ReactNode | string;\n onMenuClick?: (open: boolean) => void;\n className?: string;\n style?: CSSProperties;\n menuAriaLabel?: string;\n menuBg?: string;\n menuContentColor?: string;\n useFixedPosition?: boolean;\n items?: MenuItem[];\n animationEase?: string;\n animationDuration?: number;\n staggerDelay?: number;\n};\n\nconst DEFAULT_ITEMS: MenuItem[] = [\n {\n label: 'home',\n href: '#',\n ariaLabel: 'Home',\n rotation: -8,\n hoverStyles: { bgColor: '#3b82f6', textColor: '#ffffff' }\n },\n {\n label: 'about',\n href: '#',\n ariaLabel: 'About',\n rotation: 8,\n hoverStyles: { bgColor: '#10b981', textColor: '#ffffff' }\n },\n {\n label: 'projects',\n href: '#',\n ariaLabel: 'Documentation',\n rotation: 8,\n hoverStyles: { bgColor: '#f59e0b', textColor: '#ffffff' }\n },\n {\n label: 'blog',\n href: '#',\n ariaLabel: 'Blog',\n rotation: 8,\n hoverStyles: { bgColor: '#ef4444', textColor: '#ffffff' }\n },\n {\n label: 'contact',\n href: '#',\n ariaLabel: 'Contact',\n rotation: -8,\n hoverStyles: { bgColor: '#8b5cf6', textColor: '#ffffff' }\n }\n];\n\nexport default function BubbleMenu({\n logo,\n onMenuClick,\n className,\n style,\n menuAriaLabel = 'Toggle menu',\n menuBg = '#fff',\n menuContentColor = '#111',\n useFixedPosition = false,\n items,\n animationEase = 'back.out(1.5)',\n animationDuration = 0.5,\n staggerDelay = 0.12\n}: BubbleMenuProps) {\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [showOverlay, setShowOverlay] = useState(false);\n\n const overlayRef = useRef(null);\n const bubblesRef = useRef([]);\n const labelRefs = useRef([]);\n\n const menuItems = items?.length ? items : DEFAULT_ITEMS;\n\n const containerClassName = [\n 'bubble-menu',\n useFixedPosition ? 'fixed' : 'absolute',\n 'left-0 right-0 top-8',\n 'flex items-center justify-between',\n 'gap-4 px-8',\n 'pointer-events-none',\n 'z-[1001]',\n className\n ]\n .filter(Boolean)\n .join(' ');\n\n const handleToggle = () => {\n const nextState = !isMenuOpen;\n if (nextState) setShowOverlay(true);\n setIsMenuOpen(nextState);\n onMenuClick?.(nextState);\n };\n\n useEffect(() => {\n const overlay = overlayRef.current;\n const bubbles = bubblesRef.current.filter(Boolean);\n const labels = labelRefs.current.filter(Boolean);\n if (!overlay || !bubbles.length) return;\n\n if (isMenuOpen) {\n gsap.set(overlay, { display: 'flex' });\n gsap.killTweensOf([...bubbles, ...labels]);\n gsap.set(bubbles, { scale: 0, transformOrigin: '50% 50%' });\n gsap.set(labels, { y: 24, autoAlpha: 0 });\n\n bubbles.forEach((bubble, i) => {\n const delay = i * staggerDelay + gsap.utils.random(-0.05, 0.05);\n const tl = gsap.timeline({ delay });\n tl.to(bubble, {\n scale: 1,\n duration: animationDuration,\n ease: animationEase\n });\n if (labels[i]) {\n tl.to(\n labels[i],\n {\n y: 0,\n autoAlpha: 1,\n duration: animationDuration,\n ease: 'power3.out'\n },\n '-=' + animationDuration * 0.9\n );\n }\n });\n } else if (showOverlay) {\n gsap.killTweensOf([...bubbles, ...labels]);\n gsap.to(labels, {\n y: 24,\n autoAlpha: 0,\n duration: 0.2,\n ease: 'power3.in'\n });\n gsap.to(bubbles, {\n scale: 0,\n duration: 0.2,\n ease: 'power3.in',\n onComplete: () => {\n gsap.set(overlay, { display: 'none' });\n setShowOverlay(false);\n }\n });\n }\n }, [isMenuOpen, showOverlay, animationEase, animationDuration, staggerDelay]);\n\n useEffect(() => {\n const handleResize = () => {\n if (isMenuOpen) {\n const bubbles = bubblesRef.current.filter(Boolean);\n const isDesktop = window.innerWidth >= 900;\n bubbles.forEach((bubble, i) => {\n const item = menuItems[i];\n if (bubble && item) {\n const rotation = isDesktop ? (item.rotation ?? 0) : 0;\n gsap.set(bubble, { rotation });\n }\n });\n }\n };\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n }, [isMenuOpen, menuItems]);\n\n return (\n <>\n {/* Workaround for silly Tailwind capabilities */}\n \n\n \n\n {showOverlay && (\n \n \n {menuItems.map((item, idx) => (\n \n {\n if (el) bubblesRef.current[idx] = el;\n }}\n >\n {\n if (el) labelRefs.current[idx] = el;\n }}\n >\n {item.label}\n \n \n \n ))}\n \n \n )}\n \n );\n}\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0" ] } ================================================ FILE: public/r/CardNav-JS-CSS.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "CardNav-JS-CSS", "title": "CardNav", "description": "Expandable navigation bar with card panels revealing nested links.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "CardNav/CardNav.css", "content": ".card-nav-container {\n position: absolute;\n top: 2em;\n left: 50%;\n transform: translateX(-50%);\n width: 90%;\n max-width: 800px;\n z-index: 99;\n box-sizing: border-box;\n}\n\n.card-nav {\n display: block;\n height: 60px;\n padding: 0;\n background-color: white;\n border: 0.5px solid rgba(255, 255, 255, 0.1);\n border-radius: 0.75rem;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n position: relative;\n overflow: hidden;\n will-change: height;\n}\n\n.card-nav-top {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n height: 60px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 0.45rem 0.55rem 1.1rem;\n z-index: 2;\n}\n\n.hamburger-menu {\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n gap: 6px;\n}\n\n.hamburger-menu:hover .hamburger-line {\n opacity: 0.75;\n}\n\n.hamburger-line {\n width: 30px;\n height: 2px;\n background-color: currentColor;\n transition:\n transform 0.25s ease,\n opacity 0.2s ease,\n margin 0.3s ease;\n transform-origin: 50% 50%;\n}\n\n.hamburger-menu.open .hamburger-line:first-child {\n transform: translateY(4px) rotate(45deg);\n}\n\n.hamburger-menu.open .hamburger-line:last-child {\n transform: translateY(-4px) rotate(-45deg);\n}\n\n.logo-container {\n display: flex;\n align-items: center;\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n}\n\n.logo {\n height: 28px;\n}\n\n.card-nav-cta-button {\n background-color: #111;\n color: white;\n border: none;\n border-radius: calc(0.75rem - 0.35rem);\n padding: 0 1rem;\n height: 100%;\n font-weight: 500;\n cursor: pointer;\n transition: background-color 0.3s ease;\n align-items: center;\n}\n\n.card-nav-cta-button:hover {\n background-color: #333;\n}\n\n.card-nav-content {\n position: absolute;\n left: 0;\n right: 0;\n top: 60px;\n bottom: 0;\n padding: 0.5rem;\n display: flex;\n align-items: flex-end;\n gap: 12px;\n visibility: hidden;\n pointer-events: none;\n z-index: 1;\n}\n\n.card-nav.open .card-nav-content {\n visibility: visible;\n pointer-events: auto;\n}\n\n.nav-card {\n height: 100%;\n flex: 1 1 0;\n min-width: 0;\n border-radius: calc(0.75rem - 0.2rem);\n position: relative;\n display: flex;\n flex-direction: column;\n padding: 12px 16px;\n gap: 8px;\n user-select: none;\n}\n\n.nav-card-label {\n font-weight: 400;\n font-size: 22px;\n letter-spacing: -0.5px;\n}\n\n.nav-card-links {\n margin-top: auto;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.nav-card-link {\n font-size: 16px;\n cursor: pointer;\n text-decoration: none;\n transition: opacity 0.3s ease;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.nav-card-link:hover {\n opacity: 0.75;\n}\n\n@media (max-width: 768px) {\n .card-nav-container {\n width: 90%;\n top: 1.2em;\n }\n\n .card-nav-top {\n padding: 0.5rem 1rem;\n justify-content: space-between;\n }\n\n .hamburger-menu {\n order: 2;\n }\n\n .logo-container {\n position: static;\n transform: none;\n order: 1;\n }\n\n .card-nav-cta-button {\n display: none;\n }\n\n .card-nav-content {\n flex-direction: column;\n align-items: stretch;\n gap: 8px;\n padding: 0.5rem;\n bottom: 0;\n justify-content: flex-start;\n }\n\n .nav-card {\n height: auto;\n min-height: 60px;\n flex: 1 1 auto;\n max-height: none;\n }\n\n .nav-card-label {\n font-size: 18px;\n }\n\n .nav-card-link {\n font-size: 15px;\n }\n}\n" }, { "type": "registry:component", "path": "CardNav/CardNav.jsx", "content": "import { useLayoutEffect, useRef, useState } from 'react';\nimport { gsap } from 'gsap';\n// use your own icon import if react-icons is not available\nimport { GoArrowUpRight } from 'react-icons/go';\nimport './CardNav.css';\n\nconst CardNav = ({\n logo,\n logoAlt = 'Logo',\n items,\n className = '',\n ease = 'power3.out',\n baseColor = '#fff',\n menuColor,\n buttonBgColor,\n buttonTextColor\n}) => {\n const [isHamburgerOpen, setIsHamburgerOpen] = useState(false);\n const [isExpanded, setIsExpanded] = useState(false);\n const navRef = useRef(null);\n const cardsRef = useRef([]);\n const tlRef = useRef(null);\n\n const calculateHeight = () => {\n const navEl = navRef.current;\n if (!navEl) return 260;\n\n const isMobile = window.matchMedia('(max-width: 768px)').matches;\n if (isMobile) {\n const contentEl = navEl.querySelector('.card-nav-content');\n if (contentEl) {\n const wasVisible = contentEl.style.visibility;\n const wasPointerEvents = contentEl.style.pointerEvents;\n const wasPosition = contentEl.style.position;\n const wasHeight = contentEl.style.height;\n\n contentEl.style.visibility = 'visible';\n contentEl.style.pointerEvents = 'auto';\n contentEl.style.position = 'static';\n contentEl.style.height = 'auto';\n\n contentEl.offsetHeight;\n\n const topBar = 60;\n const padding = 16;\n const contentHeight = contentEl.scrollHeight;\n\n contentEl.style.visibility = wasVisible;\n contentEl.style.pointerEvents = wasPointerEvents;\n contentEl.style.position = wasPosition;\n contentEl.style.height = wasHeight;\n\n return topBar + contentHeight + padding;\n }\n }\n return 260;\n };\n\n const createTimeline = () => {\n const navEl = navRef.current;\n if (!navEl) return null;\n\n gsap.set(navEl, { height: 60, overflow: 'hidden' });\n gsap.set(cardsRef.current, { y: 50, opacity: 0 });\n\n const tl = gsap.timeline({ paused: true });\n\n tl.to(navEl, {\n height: calculateHeight,\n duration: 0.4,\n ease\n });\n\n tl.to(cardsRef.current, { y: 0, opacity: 1, duration: 0.4, ease, stagger: 0.08 }, '-=0.1');\n\n return tl;\n };\n\n useLayoutEffect(() => {\n const tl = createTimeline();\n tlRef.current = tl;\n\n return () => {\n tl?.kill();\n tlRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [ease, items]);\n\n useLayoutEffect(() => {\n const handleResize = () => {\n if (!tlRef.current) return;\n\n if (isExpanded) {\n const newHeight = calculateHeight();\n gsap.set(navRef.current, { height: newHeight });\n\n tlRef.current.kill();\n const newTl = createTimeline();\n if (newTl) {\n newTl.progress(1);\n tlRef.current = newTl;\n }\n } else {\n tlRef.current.kill();\n const newTl = createTimeline();\n if (newTl) {\n tlRef.current = newTl;\n }\n }\n };\n\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isExpanded]);\n\n const toggleMenu = () => {\n const tl = tlRef.current;\n if (!tl) return;\n if (!isExpanded) {\n setIsHamburgerOpen(true);\n setIsExpanded(true);\n tl.play(0);\n } else {\n setIsHamburgerOpen(false);\n tl.eventCallback('onReverseComplete', () => setIsExpanded(false));\n tl.reverse();\n }\n };\n\n const setCardRef = i => el => {\n if (el) cardsRef.current[i] = el;\n };\n\n return (\n
\n \n
\n );\n};\n\nexport default CardNav;\n" } ], "registryDependencies": [], "dependencies": [ "gsap@^3.13.0", "react-icons@^5.5.0" ] } ================================================ FILE: public/r/CardNav-JS-TW.json ================================================ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "CardNav-JS-TW", "title": "CardNav", "description": "Expandable navigation bar with card panels revealing nested links.", "type": "registry:component", "files": [ { "type": "registry:component", "path": "CardNav/CardNav.jsx", "content": "import { useLayoutEffect, useRef, useState } from 'react';\nimport { gsap } from 'gsap';\n// use your own icon import if react-icons is not available\nimport { GoArrowUpRight } from 'react-icons/go';\n\nconst CardNav = ({\n logo,\n logoAlt = 'Logo',\n items,\n className = '',\n ease = 'power3.out',\n baseColor = '#fff',\n menuColor,\n buttonBgColor,\n buttonTextColor\n}) => {\n const [isHamburgerOpen, setIsHamburgerOpen] = useState(false);\n const [isExpanded, setIsExpanded] = useState(false);\n const navRef = useRef(null);\n const cardsRef = useRef([]);\n const tlRef = useRef(null);\n\n const calculateHeight = () => {\n const navEl = navRef.current;\n if (!navEl) return 260;\n\n const isMobile = window.matchMedia('(max-width: 768px)').matches;\n if (isMobile) {\n const contentEl = navEl.querySelector('.card-nav-content');\n if (contentEl) {\n const wasVisible = contentEl.style.visibility;\n const wasPointerEvents = contentEl.style.pointerEvents;\n const wasPosition = contentEl.style.position;\n const wasHeight = contentEl.style.height;\n\n contentEl.style.visibility = 'visible';\n contentEl.style.pointerEvents = 'auto';\n contentEl.style.position = 'static';\n contentEl.style.height = 'auto';\n\n contentEl.offsetHeight;\n\n const topBar = 60;\n const padding = 16;\n const contentHeight = contentEl.scrollHeight;\n\n contentEl.style.visibility = wasVisible;\n contentEl.style.pointerEvents = wasPointerEvents;\n contentEl.style.position = wasPosition;\n contentEl.style.height = wasHeight;\n\n return topBar + contentHeight + padding;\n }\n }\n return 260;\n };\n\n const createTimeline = () => {\n const navEl = navRef.current;\n if (!navEl) return null;\n\n gsap.set(navEl, { height: 60, overflow: 'hidden' });\n gsap.set(cardsRef.current, { y: 50, opacity: 0 });\n\n const tl = gsap.timeline({ paused: true });\n\n tl.to(navEl, {\n height: calculateHeight,\n duration: 0.4,\n ease\n });\n\n tl.to(cardsRef.current, { y: 0, opacity: 1, duration: 0.4, ease, stagger: 0.08 }, '-=0.1');\n\n return tl;\n };\n\n useLayoutEffect(() => {\n const tl = createTimeline();\n tlRef.current = tl;\n\n return () => {\n tl?.kill();\n tlRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [ease, items]);\n\n useLayoutEffect(() => {\n const handleResize = () => {\n if (!tlRef.current) return;\n\n if (isExpanded) {\n const newHeight = calculateHeight();\n gsap.set(navRef.current, { height: newHeight });\n\n tlRef.current.kill();\n const newTl = createTimeline();\n if (newTl) {\n newTl.progress(1);\n tlRef.current = newTl;\n }\n } else {\n tlRef.current.kill();\n const newTl = createTimeline();\n if (newTl) {\n tlRef.current = newTl;\n }\n }\n };\n\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isExpanded]);\n\n const toggleMenu = () => {\n const tl = tlRef.current;\n if (!tl) return;\n if (!isExpanded) {\n setIsHamburgerOpen(true);\n setIsExpanded(true);\n tl.play(0);\n } else {\n setIsHamburgerOpen(false);\n tl.eventCallback('onReverseComplete', () => setIsExpanded(false));\n tl.reverse();\n }\n };\n\n const setCardRef = i => el => {\n if (el) cardsRef.current[i] = el;\n };\n\n return (\n \n \n
\n \n \n \n
\n\n
\n {logoAlt}\n
\n\n