{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "logo-particles",
  "type": "registry:block",
  "title": "Logo particles",
  "description": "Logo particles",
  "files": [
    {
      "path": "components/usages/logoparticlesusage.tsx",
      "content": "\"use client\";\r\n\r\nimport React from \"react\";\r\n\r\nimport LogoParticles from \"@/registry/open-source/logo-particles\";\r\n\r\nexport default function Usage() {\r\n\treturn (\r\n\t\t<div className=\"relative w-full flex items-center justify-center\">\r\n\t\t\t<LogoParticles />\r\n\t\t</div>\r\n\t);\r\n}\r\n",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/logoparticlesusage.tsx",
      "content": "\"use client\";\r\n\r\nimport React from \"react\";\r\n\r\nimport LogoParticles from \"@/registry/open-source/logo-particles\";\r\n\r\nexport default function Usage() {\r\n\treturn (\r\n\t\t<div className=\"relative w-full flex items-center justify-center\">\r\n\t\t\t<LogoParticles />\r\n\t\t</div>\r\n\t);\r\n}\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/logo-particles.tsx",
      "content": "\"use client\";\n\nimport React, { useEffect, useRef, useState } from \"react\";\n\nexport default function LogoParticles() {\n\tconst canvasRef = useRef<HTMLCanvasElement>(null);\n\tconst mousePositionRef = useRef({ x: 0, y: 0 });\n\tconst isTouchingRef = useRef(false);\n\tconst [isMobile, setIsMobile] = useState(false);\n\n\tuseEffect(() => {\n\t\tconst canvas = canvasRef.current;\n\t\tif (!canvas) return;\n\n\t\tconst ctx = canvas.getContext(\"2d\");\n\t\tif (!ctx) return;\n\n\t\tconst updateCanvasSize = () => {\n\t\t\tcanvas.width = window.innerWidth;\n\t\t\tcanvas.height = window.innerHeight;\n\t\t\tsetIsMobile(window.innerWidth < 768); // Set mobile breakpoint\n\t\t};\n\n\t\tupdateCanvasSize();\n\n\t\tlet particles: {\n\t\t\tx: number;\n\t\t\ty: number;\n\t\t\tbaseX: number;\n\t\t\tbaseY: number;\n\t\t\tsize: number;\n\t\t\tcolor: string;\n\t\t\tscatteredColor: string;\n\t\t\tlife: number;\n\t\t\tisAWS: boolean;\n\t\t}[] = [];\n\n\t\tlet textImageData: ImageData | null = null;\n\n\t\tfunction createTextImage() {\n\t\t\tif (!ctx || !canvas) return 0;\n\n\t\t\tctx.fillStyle = \"white\";\n\t\t\tctx.save();\n\n\t\t\tconst logoHeight = isMobile ? 60 : 120;\n\t\t\tconst vercelLogoWidth = logoHeight * (40 / 19.7762); // Maintain aspect ratio\n\t\t\tconst awsLogoWidth = logoHeight * (283 / 140); // Maintain aspect ratio\n\t\t\tconst logoSpacing = isMobile ? 30 : 60; // Increased gap for mobile and desktop\n\t\t\tconst totalWidth = vercelLogoWidth + awsLogoWidth + logoSpacing;\n\n\t\t\tctx.translate(\n\t\t\t\tcanvas.width / 2 - totalWidth / 2,\n\t\t\t\tcanvas.height / 2 - logoHeight / 2\n\t\t\t);\n\n\t\t\t// Draw Vercel logo\n\t\t\tctx.save();\n\t\t\tconst vercelScale = logoHeight / 19.7762;\n\t\t\tconst path1 = new Path2D(\n\t\t\t\t\"M66.57,94.58c-2.77,0-5.41-3.17-11-3.17C48.74,91.41,42.27,95,35.14,95,14.26,95,0,81.11,0,60.1,0,40.42,15.06,25.36,34.74,25.36,42.27,25.36,50.2,30,54.69,30,63,30,60.37,16,60.37,10.7,60.37,6,60.9,0,67.1,0c4.63,0,5.55,4.36,5.55,8.19V88.5C72.65,93.26,71.2,94.58,66.57,94.58ZM35.27,33.82C20.21,33.82,9,44.91,9,60,9,74.24,21,85.6,35.14,85.6c14.79,0,25.49-11.76,25.49-26.29A25.48,25.48,0,0,0,35.27,33.82Z M131.05,35.68c-1.78.5-5.09.1-7.34-.56-4.21-1.23-7.12-1-11.48-1-8.06,0-19.34,2.25-19.34,12.82V87.45c0,3.83.26,8.45-4.89,8.45s-6.6-3.57-6.6-8.06V34.34c0-4.22.26-8.45,5.68-8.45,2.51,0,5.68,2,5.68,4.76,5.41-4.36,17.57-6.34,24.43-6.34,5.29,0,17.61,1.93,17.61,7.12C134.8,33.67,133.55,35,131.05,35.68Z M192.92,92.47a6.22,6.22,0,0,1-5,2.24,5.83,5.83,0,0,1-5.42-2.64C180,88.5,154.74,31.7,154.74,29.85a5.22,5.22,0,0,1,5.16-5.41,5.5,5.5,0,0,1,5.54,3.3c6.48,11.76,10.31,25.1,17.31,36.72a6.88,6.88,0,0,0,6.6,3.83c3.17,0,4.89-1.19,6.47-3.83,4.23-7.39,13.08-33.55,17-37.91a4.69,4.69,0,0,1,3.44-2,3.67,3.67,0,0,1,3.57,3.7C219.87,30.65,194.77,90.62,192.92,92.47Z M272.44,62.48c-11.62.4-23.38.13-35,.13-4.63,0-8.33,2.51-8.33,7.53,0,11.36,14.4,15.46,23.52,15.46,18.36,0,20.6-13.08,26.68-13.08a3.81,3.81,0,0,1,3.7,3.83,9,9,0,0,1-2.51,5.55c-7.66,9.12-16.65,12.68-28.4,12.68-21,0-35.53-12.42-35.53-33.95,0-19.94,15.19-34.87,35-34.87,14.13,0,31.7,11.76,31.7,26.29C283.27,59.44,279.44,62.22,272.44,62.48Zm-20.74-28c-8.19,0-15.19,2.37-19.68,9.64a9.71,9.71,0,0,0-1.85,5c0,5.15,4.76,6.21,9,6.21h12.42c6.21,0,20.74,2.64,20.74-6.47C272.31,40.42,258.84,34.48,251.7,34.48Z M144.05,94.58c-4.47,0-5.66-2.77-5.66-6.85l.13-54c0-3.82,1.32-7,5.67-7a5,5,0,0,1,4.21,2,8.49,8.49,0,0,1,1.45,5c0,18.05.26,36,.26,54C150.11,91.81,148.4,94.58,144.05,94.58Z\"\n\t\t\t);\n\t\t\tctx.fill(path1);\n\t\t\tctx.restore();\n\n\t\t\t// Draw AWS logo\n\t\t\tctx.save();\n\t\t\tctx.translate(vercelLogoWidth + logoSpacing, 0);\n\t\t\tconst awsScale = logoHeight / 140;\n\t\t\tctx.scale(awsScale, awsScale);\n\t\t\tconst path = new Path2D(\n\t\t\t\t\"M66.57,94.58c-2.77,0-5.41-3.17-11-3.17C48.74,91.41,42.27,95,35.14,95,14.26,95,0,81.11,0,60.1,0,40.42,15.06,25.36,34.74,25.36,42.27,25.36,50.2,30,54.69,30,63,30,60.37,16,60.37,10.7,60.37,6,60.9,0,67.1,0c4.63,0,5.55,4.36,5.55,8.19V88.5C72.65,93.26,71.2,94.58,66.57,94.58ZM35.27,33.82C20.21,33.82,9,44.91,9,60,9,74.24,21,85.6,35.14,85.6c14.79,0,25.49-11.76,25.49-26.29A25.48,25.48,0,0,0,35.27,33.82Z M131.05,35.68c-1.78.5-5.09.1-7.34-.56-4.21-1.23-7.12-1-11.48-1-8.06,0-19.34,2.25-19.34,12.82V87.45c0,3.83.26,8.45-4.89,8.45s-6.6-3.57-6.6-8.06V34.34c0-4.22.26-8.45,5.68-8.45,2.51,0,5.68,2,5.68,4.76,5.41-4.36,17.57-6.34,24.43-6.34,5.29,0,17.61,1.93,17.61,7.12C134.8,33.67,133.55,35,131.05,35.68Z M192.92,92.47a6.22,6.22,0,0,1-5,2.24,5.83,5.83,0,0,1-5.42-2.64C180,88.5,154.74,31.7,154.74,29.85a5.22,5.22,0,0,1,5.16-5.41,5.5,5.5,0,0,1,5.54,3.3c6.48,11.76,10.31,25.1,17.31,36.72a6.88,6.88,0,0,0,6.6,3.83c3.17,0,4.89-1.19,6.47-3.83,4.23-7.39,13.08-33.55,17-37.91a4.69,4.69,0,0,1,3.44-2,3.67,3.67,0,0,1,3.57,3.7C219.87,30.65,194.77,90.62,192.92,92.47Z M272.44,62.48c-11.62.4-23.38.13-35,.13-4.63,0-8.33,2.51-8.33,7.53,0,11.36,14.4,15.46,23.52,15.46,18.36,0,20.6-13.08,26.68-13.08a3.81,3.81,0,0,1,3.7,3.83,9,9,0,0,1-2.51,5.55c-7.66,9.12-16.65,12.68-28.4,12.68-21,0-35.53-12.42-35.53-33.95,0-19.94,15.19-34.87,35-34.87,14.13,0,31.7,11.76,31.7,26.29C283.27,59.44,279.44,62.22,272.44,62.48Zm-20.74-28c-8.19,0-15.19,2.37-19.68,9.64a9.71,9.71,0,0,0-1.85,5c0,5.15,4.76,6.21,9,6.21h12.42c6.21,0,20.74,2.64,20.74-6.47C272.31,40.42,258.84,34.48,251.7,34.48Z M144.05,94.58c-4.47,0-5.66-2.77-5.66-6.85l.13-54c0-3.82,1.32-7,5.67-7a5,5,0,0,1,4.21,2,8.49,8.49,0,0,1,1.45,5c0,18.05.26,36,.26,54C150.11,91.81,148.4,94.58,144.05,94.58Z\"\n\t\t\t);\n\t\t\tctx.fill(path);\n\t\t\tctx.restore();\n\n\t\t\tctx.restore();\n\n\t\t\ttextImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n\t\t\tctx.clearRect(0, 0, canvas.width, canvas.height);\n\n\t\t\treturn Math.max(vercelScale, awsScale);\n\t\t}\n\n\t\tfunction createParticle(scale: number) {\n\t\t\tif (!ctx || !canvas || !textImageData) return null;\n\n\t\t\tconst data = textImageData.data;\n\t\t\tconst particleGap = 2;\n\n\t\t\tfor (let attempt = 0; attempt < 100; attempt++) {\n\t\t\t\tconst x = Math.floor(Math.random() * canvas.width);\n\t\t\t\tconst y = Math.floor(Math.random() * canvas.height);\n\n\t\t\t\tif (data[(y * canvas.width + x) * 4 + 3] > 128) {\n\t\t\t\t\tconst logoHeight = isMobile ? 60 : 120;\n\t\t\t\t\tconst vercelLogoWidth = logoHeight * (40 / 19.7762);\n\t\t\t\t\tconst awsLogoWidth = logoHeight * (283 / 140);\n\t\t\t\t\tconst logoSpacing = isMobile ? 30 : 60;\n\t\t\t\t\tconst totalWidth = vercelLogoWidth + awsLogoWidth + logoSpacing;\n\t\t\t\t\tconst centerX = canvas.width / 2;\n\t\t\t\t\tconst centerY = canvas.height / 2;\n\t\t\t\t\tconst isAWSLogo = x >= centerX + totalWidth / 2 - awsLogoWidth;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tx: x,\n\t\t\t\t\t\ty: y,\n\t\t\t\t\t\tbaseX: x,\n\t\t\t\t\t\tbaseY: y,\n\t\t\t\t\t\tsize: Math.random() * 1 + 0.5,\n\t\t\t\t\t\tcolor: \"white\",\n\t\t\t\t\t\tscatteredColor: isAWSLogo ? \"#FF9900\" : \"#00DCFF\",\n\t\t\t\t\t\tisAWS: isAWSLogo,\n\t\t\t\t\t\tlife: Math.random() * 100 + 50,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tfunction createInitialParticles(scale: number) {\n\t\t\tconst baseParticleCount = 7000; // Increased base count for higher density\n\t\t\tconst particleCount = Math.floor(\n\t\t\t\tbaseParticleCount *\n\t\t\t\t\tMath.sqrt((canvas.width * canvas.height) / (1920 * 1080))\n\t\t\t);\n\t\t\tfor (let i = 0; i < particleCount; i++) {\n\t\t\t\tconst particle = createParticle(scale);\n\t\t\t\tif (particle) particles.push(particle);\n\t\t\t}\n\t\t}\n\n\t\tlet animationFrameId: number;\n\n\t\tfunction animate(scale: number) {\n\t\t\tif (!ctx || !canvas) return;\n\t\t\tctx.clearRect(0, 0, canvas.width, canvas.height);\n\t\t\tctx.fillStyle = \"black\";\n\t\t\tctx.fillRect(0, 0, canvas.width, canvas.height);\n\n\t\t\tconst { x: mouseX, y: mouseY } = mousePositionRef.current;\n\t\t\tconst maxDistance = 240;\n\n\t\t\tfor (let i = 0; i < particles.length; i++) {\n\t\t\t\tconst p = particles[i];\n\t\t\t\tconst dx = mouseX - p.x;\n\t\t\t\tconst dy = mouseY - p.y;\n\t\t\t\tconst distance = Math.sqrt(dx * dx + dy * dy);\n\n\t\t\t\tif (\n\t\t\t\t\tdistance < maxDistance &&\n\t\t\t\t\t(isTouchingRef.current || !(\"ontouchstart\" in window))\n\t\t\t\t) {\n\t\t\t\t\tconst force = (maxDistance - distance) / maxDistance;\n\t\t\t\t\tconst angle = Math.atan2(dy, dx);\n\t\t\t\t\tconst moveX = Math.cos(angle) * force * 60;\n\t\t\t\t\tconst moveY = Math.sin(angle) * force * 60;\n\t\t\t\t\tp.x = p.baseX - moveX;\n\t\t\t\t\tp.y = p.baseY - moveY;\n\n\t\t\t\t\tctx.fillStyle = p.scatteredColor;\n\t\t\t\t} else {\n\t\t\t\t\tp.x += (p.baseX - p.x) * 0.1;\n\t\t\t\t\tp.y += (p.baseY - p.y) * 0.1;\n\t\t\t\t\tctx.fillStyle = \"white\";\n\t\t\t\t}\n\n\t\t\t\tctx.fillRect(p.x, p.y, p.size, p.size);\n\n\t\t\t\tp.life--;\n\t\t\t\tif (p.life <= 0) {\n\t\t\t\t\tconst newParticle = createParticle(scale);\n\t\t\t\t\tif (newParticle) {\n\t\t\t\t\t\tparticles[i] = newParticle;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tparticles.splice(i, 1);\n\t\t\t\t\t\ti--;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst baseParticleCount = 7000;\n\t\t\tconst targetParticleCount = Math.floor(\n\t\t\t\tbaseParticleCount *\n\t\t\t\t\tMath.sqrt((canvas.width * canvas.height) / (1920 * 1080))\n\t\t\t);\n\t\t\twhile (particles.length < targetParticleCount) {\n\t\t\t\tconst newParticle = createParticle(scale);\n\t\t\t\tif (newParticle) particles.push(newParticle);\n\t\t\t}\n\n\t\t\tanimationFrameId = requestAnimationFrame(() => animate(scale));\n\t\t}\n\n\t\tconst scale = createTextImage();\n\t\tcreateInitialParticles(scale);\n\t\tanimate(scale);\n\n\t\tconst handleResize = () => {\n\t\t\tupdateCanvasSize();\n\t\t\tconst newScale = createTextImage();\n\t\t\tparticles = [];\n\t\t\tcreateInitialParticles(newScale);\n\t\t};\n\n\t\tconst handleMove = (x: number, y: number) => {\n\t\t\tmousePositionRef.current = { x, y };\n\t\t};\n\n\t\tconst handleMouseMove = (e: MouseEvent) => {\n\t\t\thandleMove(e.clientX, e.clientY);\n\t\t};\n\n\t\tconst handleTouchMove = (e: TouchEvent) => {\n\t\t\tif (e.touches.length > 0) {\n\t\t\t\te.preventDefault();\n\t\t\t\thandleMove(e.touches[0].clientX, e.touches[0].clientY);\n\t\t\t}\n\t\t};\n\n\t\tconst handleTouchStart = () => {\n\t\t\tisTouchingRef.current = true;\n\t\t};\n\n\t\tconst handleTouchEnd = () => {\n\t\t\tisTouchingRef.current = false;\n\t\t\tmousePositionRef.current = { x: 0, y: 0 };\n\t\t};\n\n\t\tconst handleMouseLeave = () => {\n\t\t\tif (!(\"ontouchstart\" in window)) {\n\t\t\t\tmousePositionRef.current = { x: 0, y: 0 };\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"resize\", handleResize);\n\t\tcanvas.addEventListener(\"mousemove\", handleMouseMove);\n\t\tcanvas.addEventListener(\"touchmove\", handleTouchMove, { passive: false });\n\t\tcanvas.addEventListener(\"mouseleave\", handleMouseLeave);\n\t\tcanvas.addEventListener(\"touchstart\", handleTouchStart);\n\t\tcanvas.addEventListener(\"touchend\", handleTouchEnd);\n\n\t\treturn () => {\n\t\t\twindow.removeEventListener(\"resize\", handleResize);\n\t\t\tcanvas.removeEventListener(\"mousemove\", handleMouseMove);\n\t\t\tcanvas.removeEventListener(\"touchmove\", handleTouchMove);\n\t\t\tcanvas.removeEventListener(\"mouseleave\", handleMouseLeave);\n\t\t\tcanvas.removeEventListener(\"touchstart\", handleTouchStart);\n\t\t\tcanvas.removeEventListener(\"touchend\", handleTouchEnd);\n\t\t\tcancelAnimationFrame(animationFrameId);\n\t\t};\n\t}, [isMobile]);\n\n\treturn (\n\t\t<div className=\"relative w-full h-dvh flex flex-col items-center justify-center bg-black\">\n\t\t\t<canvas\n\t\t\t\tref={canvasRef}\n\t\t\t\tclassName=\"w-full h-full absolute top-0 left-0 touch-none\"\n\t\t\t\taria-label=\"Interactive particle effect with Vercel and AWS logos\"\n\t\t\t/>\n\t\t</div>\n\t);\n}\n",
      "type": "registry:ui"
    }
  ]
}