diff --git a/apps/www/public/llms-full.txt b/apps/www/public/llms-full.txt index fa9715cf4..0b3dae9c8 100644 --- a/apps/www/public/llms-full.txt +++ b/apps/www/public/llms-full.txt @@ -5164,6 +5164,8 @@ export interface CoolParticleOptions extends BaseParticleOptions { speedUp?: number } +const SVG_NS = "http://www.w3.org/2000/svg" + const getContainer = () => { const id = "_coolMode_effect" const existingContainer = document.getElementById(id) @@ -5204,6 +5206,61 @@ const applyParticleEffect = ( const container = getContainer() + const appendCircleParticle = (particle: HTMLDivElement, size: number) => { + const circleSVG = document.createElementNS(SVG_NS, "svg") + const circle = document.createElementNS(SVG_NS, "circle") + + circle.setAttributeNS(null, "cx", (size / 2).toString()) + circle.setAttributeNS(null, "cy", (size / 2).toString()) + circle.setAttributeNS(null, "r", (size / 2).toString()) + circle.setAttributeNS(null, "fill", `hsl(${Math.random() * 360}, 70%, 50%)`) + + circleSVG.appendChild(circle) + circleSVG.setAttribute("width", size.toString()) + circleSVG.setAttribute("height", size.toString()) + + particle.appendChild(circleSVG) + } + + const appendImageParticle = ( + particle: HTMLDivElement, + imageSrc: string, + size: number + ) => { + const image = document.createElement("img") + image.src = imageSrc + image.width = size + image.height = size + image.alt = "" + image.style.borderRadius = "50%" + + particle.appendChild(image) + } + + const appendTextParticle = ( + particle: HTMLDivElement, + particleContent: string, + size: number + ) => { + const fontSizeMultiplier = 3 + const emojiSize = size * fontSizeMultiplier + const content = document.createElement("div") + + content.textContent = particleContent + content.style.fontSize = `${emojiSize}px` + content.style.lineHeight = "1" + content.style.textAlign = "center" + content.style.width = `${size}px` + content.style.height = `${size}px` + content.style.display = "flex" + content.style.alignItems = "center" + content.style.justifyContent = "center" + content.style.transform = `scale(${fontSizeMultiplier})` + content.style.transformOrigin = "center" + + particle.appendChild(content) + } + function generateParticle() { const size = options?.size || sizes[Math.floor(Math.random() * sizes.length)] @@ -5218,34 +5275,14 @@ const applyParticleEffect = ( const particle = document.createElement("div") if (particleType === "circle") { - const svgNS = "http://www.w3.org/2000/svg" - const circleSVG = document.createElementNS(svgNS, "svg") - const circle = document.createElementNS(svgNS, "circle") - circle.setAttributeNS(null, "cx", (size / 2).toString()) - circle.setAttributeNS(null, "cy", (size / 2).toString()) - circle.setAttributeNS(null, "r", (size / 2).toString()) - circle.setAttributeNS( - null, - "fill", - `hsl(${Math.random() * 360}, 70%, 50%)` - ) - - circleSVG.appendChild(circle) - circleSVG.setAttribute("width", size.toString()) - circleSVG.setAttribute("height", size.toString()) - - particle.appendChild(circleSVG) + appendCircleParticle(particle, size) } else if ( particleType.startsWith("http") || particleType.startsWith("/") ) { - // Handle URL-based images - particle.innerHTML = `` + appendImageParticle(particle, particleType, size) } else { - // Handle emoji or text characters - const fontSizeMultiplier = 3 // Make emojis 3x bigger - const emojiSize = size * fontSizeMultiplier - particle.innerHTML = `
${particleType}
` + appendTextParticle(particle, particleType, size) } particle.style.position = "absolute" @@ -5424,8 +5461,7 @@ export default function CoolModeCustom() {
diff --git a/apps/www/public/r/cool-mode-custom.json b/apps/www/public/r/cool-mode-custom.json index e1769f9e8..4969d9bef 100644 --- a/apps/www/public/r/cool-mode-custom.json +++ b/apps/www/public/r/cool-mode-custom.json @@ -10,7 +10,7 @@ "files": [ { "path": "registry/example/cool-mode-custom.tsx", - "content": "import { Button } from \"@/components/ui/button\"\nimport { CoolMode } from \"@/registry/magicui/cool-mode\"\n\nexport default function CoolModeCustom() {\n return (\n
\n \n \n \n
\n )\n}\n", + "content": "import { Button } from \"@/components/ui/button\"\nimport { CoolMode } from \"@/registry/magicui/cool-mode\"\n\nexport default function CoolModeCustom() {\n return (\n
\n \n \n \n
\n )\n}\n", "type": "registry:example" } ] diff --git a/apps/www/public/r/cool-mode.json b/apps/www/public/r/cool-mode.json index 026466d5a..8304a541a 100644 --- a/apps/www/public/r/cool-mode.json +++ b/apps/www/public/r/cool-mode.json @@ -7,7 +7,7 @@ "files": [ { "path": "registry/magicui/cool-mode.tsx", - "content": "\"use client\"\n\nimport React, { ReactNode, useEffect, useRef } from \"react\"\n\nexport interface BaseParticle {\n element: HTMLElement | SVGSVGElement\n left: number\n size: number\n top: number\n}\n\nexport interface BaseParticleOptions {\n particle?: string\n size?: number\n}\n\nexport interface CoolParticle extends BaseParticle {\n direction: number\n speedHorz: number\n speedUp: number\n spinSpeed: number\n spinVal: number\n}\n\nexport interface CoolParticleOptions extends BaseParticleOptions {\n particleCount?: number\n speedHorz?: number\n speedUp?: number\n}\n\nconst getContainer = () => {\n const id = \"_coolMode_effect\"\n const existingContainer = document.getElementById(id)\n\n if (existingContainer) {\n return existingContainer\n }\n\n const container = document.createElement(\"div\")\n container.setAttribute(\"id\", id)\n container.setAttribute(\n \"style\",\n \"overflow:hidden; position:fixed; height:100%; top:0; left:0; right:0; bottom:0; pointer-events:none; z-index:2147483647\"\n )\n\n document.body.appendChild(container)\n\n return container\n}\n\nlet instanceCounter = 0\n\nconst applyParticleEffect = (\n element: HTMLElement,\n options?: CoolParticleOptions\n): (() => void) => {\n instanceCounter++\n\n const defaultParticle = \"circle\"\n const particleType = options?.particle || defaultParticle\n const sizes = [15, 20, 25, 35, 45]\n const limit = 45\n\n let particles: CoolParticle[] = []\n let autoAddParticle = false\n let mouseX = 0\n let mouseY = 0\n\n const container = getContainer()\n\n function generateParticle() {\n const size =\n options?.size || sizes[Math.floor(Math.random() * sizes.length)]\n const speedHorz = options?.speedHorz || Math.random() * 10\n const speedUp = options?.speedUp || Math.random() * 25\n const spinVal = Math.random() * 360\n const spinSpeed = Math.random() * 35 * (Math.random() <= 0.5 ? -1 : 1)\n const top = mouseY - size / 2\n const left = mouseX - size / 2\n const direction = Math.random() <= 0.5 ? -1 : 1\n\n const particle = document.createElement(\"div\")\n\n if (particleType === \"circle\") {\n const svgNS = \"http://www.w3.org/2000/svg\"\n const circleSVG = document.createElementNS(svgNS, \"svg\")\n const circle = document.createElementNS(svgNS, \"circle\")\n circle.setAttributeNS(null, \"cx\", (size / 2).toString())\n circle.setAttributeNS(null, \"cy\", (size / 2).toString())\n circle.setAttributeNS(null, \"r\", (size / 2).toString())\n circle.setAttributeNS(\n null,\n \"fill\",\n `hsl(${Math.random() * 360}, 70%, 50%)`\n )\n\n circleSVG.appendChild(circle)\n circleSVG.setAttribute(\"width\", size.toString())\n circleSVG.setAttribute(\"height\", size.toString())\n\n particle.appendChild(circleSVG)\n } else if (\n particleType.startsWith(\"http\") ||\n particleType.startsWith(\"/\")\n ) {\n // Handle URL-based images\n particle.innerHTML = ``\n } else {\n // Handle emoji or text characters\n const fontSizeMultiplier = 3 // Make emojis 3x bigger\n const emojiSize = size * fontSizeMultiplier\n particle.innerHTML = `
${particleType}
`\n }\n\n particle.style.position = \"absolute\"\n particle.style.transform = `translate3d(${left}px, ${top}px, 0px) rotate(${spinVal}deg)`\n\n container.appendChild(particle)\n\n particles.push({\n direction,\n element: particle,\n left,\n size,\n speedHorz,\n speedUp,\n spinSpeed,\n spinVal,\n top,\n })\n }\n\n function refreshParticles() {\n particles.forEach((p) => {\n p.left = p.left - p.speedHorz * p.direction\n p.top = p.top - p.speedUp\n p.speedUp = Math.min(p.size, p.speedUp - 1)\n p.spinVal = p.spinVal + p.spinSpeed\n\n if (\n p.top >=\n Math.max(window.innerHeight, document.body.clientHeight) + p.size\n ) {\n particles = particles.filter((o) => o !== p)\n p.element.remove()\n }\n\n p.element.setAttribute(\n \"style\",\n [\n \"position:absolute\",\n \"will-change:transform\",\n `top:${p.top}px`,\n `left:${p.left}px`,\n `transform:rotate(${p.spinVal}deg)`,\n ].join(\";\")\n )\n })\n }\n\n let animationFrame: number | undefined\n\n let lastParticleTimestamp = 0\n const particleGenerationDelay = 30\n\n function loop() {\n const currentTime = performance.now()\n if (\n autoAddParticle &&\n particles.length < limit &&\n currentTime - lastParticleTimestamp > particleGenerationDelay\n ) {\n generateParticle()\n lastParticleTimestamp = currentTime\n }\n\n refreshParticles()\n animationFrame = requestAnimationFrame(loop)\n }\n\n loop()\n\n const isTouchInteraction = \"ontouchstart\" in window\n\n const tap = isTouchInteraction ? \"touchstart\" : \"mousedown\"\n const tapEnd = isTouchInteraction ? \"touchend\" : \"mouseup\"\n const move = isTouchInteraction ? \"touchmove\" : \"mousemove\"\n\n const updateMousePosition = (e: MouseEvent | TouchEvent) => {\n if (\"touches\" in e) {\n mouseX = e.touches?.[0].clientX\n mouseY = e.touches?.[0].clientY\n } else {\n mouseX = e.clientX\n mouseY = e.clientY\n }\n }\n\n const tapHandler = (e: MouseEvent | TouchEvent) => {\n updateMousePosition(e)\n autoAddParticle = true\n }\n\n const disableAutoAddParticle = () => {\n autoAddParticle = false\n }\n\n element.addEventListener(move, updateMousePosition, { passive: true })\n element.addEventListener(tap, tapHandler, { passive: true })\n element.addEventListener(tapEnd, disableAutoAddParticle, { passive: true })\n element.addEventListener(\"mouseleave\", disableAutoAddParticle, {\n passive: true,\n })\n\n return () => {\n element.removeEventListener(move, updateMousePosition)\n element.removeEventListener(tap, tapHandler)\n element.removeEventListener(tapEnd, disableAutoAddParticle)\n element.removeEventListener(\"mouseleave\", disableAutoAddParticle)\n\n const interval = setInterval(() => {\n if (animationFrame && particles.length === 0) {\n cancelAnimationFrame(animationFrame)\n clearInterval(interval)\n\n if (--instanceCounter === 0) {\n container.remove()\n }\n }\n }, 500)\n }\n}\n\ninterface CoolModeProps {\n children: ReactNode\n options?: CoolParticleOptions\n}\n\nexport const CoolMode: React.FC = ({ children, options }) => {\n const ref = useRef(null)\n\n useEffect(() => {\n const element = ref.current\n let cleanup: (() => void) | null = null\n\n if (element) {\n cleanup = applyParticleEffect(element, options)\n }\n\n return () => {\n if (cleanup) {\n cleanup()\n }\n }\n }, [options])\n\n return {children}\n}\n", + "content": "\"use client\"\n\nimport React, { ReactNode, useEffect, useRef } from \"react\"\n\nexport interface BaseParticle {\n element: HTMLElement | SVGSVGElement\n left: number\n size: number\n top: number\n}\n\nexport interface BaseParticleOptions {\n particle?: string\n size?: number\n}\n\nexport interface CoolParticle extends BaseParticle {\n direction: number\n speedHorz: number\n speedUp: number\n spinSpeed: number\n spinVal: number\n}\n\nexport interface CoolParticleOptions extends BaseParticleOptions {\n particleCount?: number\n speedHorz?: number\n speedUp?: number\n}\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\"\n\nconst getContainer = () => {\n const id = \"_coolMode_effect\"\n const existingContainer = document.getElementById(id)\n\n if (existingContainer) {\n return existingContainer\n }\n\n const container = document.createElement(\"div\")\n container.setAttribute(\"id\", id)\n container.setAttribute(\n \"style\",\n \"overflow:hidden; position:fixed; height:100%; top:0; left:0; right:0; bottom:0; pointer-events:none; z-index:2147483647\"\n )\n\n document.body.appendChild(container)\n\n return container\n}\n\nlet instanceCounter = 0\n\nconst applyParticleEffect = (\n element: HTMLElement,\n options?: CoolParticleOptions\n): (() => void) => {\n instanceCounter++\n\n const defaultParticle = \"circle\"\n const particleType = options?.particle || defaultParticle\n const sizes = [15, 20, 25, 35, 45]\n const limit = 45\n\n let particles: CoolParticle[] = []\n let autoAddParticle = false\n let mouseX = 0\n let mouseY = 0\n\n const container = getContainer()\n\n const appendCircleParticle = (particle: HTMLDivElement, size: number) => {\n const circleSVG = document.createElementNS(SVG_NS, \"svg\")\n const circle = document.createElementNS(SVG_NS, \"circle\")\n\n circle.setAttributeNS(null, \"cx\", (size / 2).toString())\n circle.setAttributeNS(null, \"cy\", (size / 2).toString())\n circle.setAttributeNS(null, \"r\", (size / 2).toString())\n circle.setAttributeNS(null, \"fill\", `hsl(${Math.random() * 360}, 70%, 50%)`)\n\n circleSVG.appendChild(circle)\n circleSVG.setAttribute(\"width\", size.toString())\n circleSVG.setAttribute(\"height\", size.toString())\n\n particle.appendChild(circleSVG)\n }\n\n const appendImageParticle = (\n particle: HTMLDivElement,\n imageSrc: string,\n size: number\n ) => {\n const image = document.createElement(\"img\")\n image.src = imageSrc\n image.width = size\n image.height = size\n image.alt = \"\"\n image.style.borderRadius = \"50%\"\n\n particle.appendChild(image)\n }\n\n const appendTextParticle = (\n particle: HTMLDivElement,\n particleContent: string,\n size: number\n ) => {\n const fontSizeMultiplier = 3\n const emojiSize = size * fontSizeMultiplier\n const content = document.createElement(\"div\")\n\n content.textContent = particleContent\n content.style.fontSize = `${emojiSize}px`\n content.style.lineHeight = \"1\"\n content.style.textAlign = \"center\"\n content.style.width = `${size}px`\n content.style.height = `${size}px`\n content.style.display = \"flex\"\n content.style.alignItems = \"center\"\n content.style.justifyContent = \"center\"\n content.style.transform = `scale(${fontSizeMultiplier})`\n content.style.transformOrigin = \"center\"\n\n particle.appendChild(content)\n }\n\n function generateParticle() {\n const size =\n options?.size || sizes[Math.floor(Math.random() * sizes.length)]\n const speedHorz = options?.speedHorz || Math.random() * 10\n const speedUp = options?.speedUp || Math.random() * 25\n const spinVal = Math.random() * 360\n const spinSpeed = Math.random() * 35 * (Math.random() <= 0.5 ? -1 : 1)\n const top = mouseY - size / 2\n const left = mouseX - size / 2\n const direction = Math.random() <= 0.5 ? -1 : 1\n\n const particle = document.createElement(\"div\")\n\n if (particleType === \"circle\") {\n appendCircleParticle(particle, size)\n } else if (\n particleType.startsWith(\"http\") ||\n particleType.startsWith(\"/\")\n ) {\n appendImageParticle(particle, particleType, size)\n } else {\n appendTextParticle(particle, particleType, size)\n }\n\n particle.style.position = \"absolute\"\n particle.style.transform = `translate3d(${left}px, ${top}px, 0px) rotate(${spinVal}deg)`\n\n container.appendChild(particle)\n\n particles.push({\n direction,\n element: particle,\n left,\n size,\n speedHorz,\n speedUp,\n spinSpeed,\n spinVal,\n top,\n })\n }\n\n function refreshParticles() {\n particles.forEach((p) => {\n p.left = p.left - p.speedHorz * p.direction\n p.top = p.top - p.speedUp\n p.speedUp = Math.min(p.size, p.speedUp - 1)\n p.spinVal = p.spinVal + p.spinSpeed\n\n if (\n p.top >=\n Math.max(window.innerHeight, document.body.clientHeight) + p.size\n ) {\n particles = particles.filter((o) => o !== p)\n p.element.remove()\n }\n\n p.element.setAttribute(\n \"style\",\n [\n \"position:absolute\",\n \"will-change:transform\",\n `top:${p.top}px`,\n `left:${p.left}px`,\n `transform:rotate(${p.spinVal}deg)`,\n ].join(\";\")\n )\n })\n }\n\n let animationFrame: number | undefined\n\n let lastParticleTimestamp = 0\n const particleGenerationDelay = 30\n\n function loop() {\n const currentTime = performance.now()\n if (\n autoAddParticle &&\n particles.length < limit &&\n currentTime - lastParticleTimestamp > particleGenerationDelay\n ) {\n generateParticle()\n lastParticleTimestamp = currentTime\n }\n\n refreshParticles()\n animationFrame = requestAnimationFrame(loop)\n }\n\n loop()\n\n const isTouchInteraction = \"ontouchstart\" in window\n\n const tap = isTouchInteraction ? \"touchstart\" : \"mousedown\"\n const tapEnd = isTouchInteraction ? \"touchend\" : \"mouseup\"\n const move = isTouchInteraction ? \"touchmove\" : \"mousemove\"\n\n const updateMousePosition = (e: MouseEvent | TouchEvent) => {\n if (\"touches\" in e) {\n mouseX = e.touches?.[0].clientX\n mouseY = e.touches?.[0].clientY\n } else {\n mouseX = e.clientX\n mouseY = e.clientY\n }\n }\n\n const tapHandler = (e: MouseEvent | TouchEvent) => {\n updateMousePosition(e)\n autoAddParticle = true\n }\n\n const disableAutoAddParticle = () => {\n autoAddParticle = false\n }\n\n element.addEventListener(move, updateMousePosition, { passive: true })\n element.addEventListener(tap, tapHandler, { passive: true })\n element.addEventListener(tapEnd, disableAutoAddParticle, { passive: true })\n element.addEventListener(\"mouseleave\", disableAutoAddParticle, {\n passive: true,\n })\n\n return () => {\n element.removeEventListener(move, updateMousePosition)\n element.removeEventListener(tap, tapHandler)\n element.removeEventListener(tapEnd, disableAutoAddParticle)\n element.removeEventListener(\"mouseleave\", disableAutoAddParticle)\n\n const interval = setInterval(() => {\n if (animationFrame && particles.length === 0) {\n cancelAnimationFrame(animationFrame)\n clearInterval(interval)\n\n if (--instanceCounter === 0) {\n container.remove()\n }\n }\n }, 500)\n }\n}\n\ninterface CoolModeProps {\n children: ReactNode\n options?: CoolParticleOptions\n}\n\nexport const CoolMode: React.FC = ({ children, options }) => {\n const ref = useRef(null)\n\n useEffect(() => {\n const element = ref.current\n let cleanup: (() => void) | null = null\n\n if (element) {\n cleanup = applyParticleEffect(element, options)\n }\n\n return () => {\n if (cleanup) {\n cleanup()\n }\n }\n }, [options])\n\n return {children}\n}\n", "type": "registry:ui" } ] diff --git a/apps/www/registry/example/cool-mode-custom.tsx b/apps/www/registry/example/cool-mode-custom.tsx index dcda6105c..86c421c71 100644 --- a/apps/www/registry/example/cool-mode-custom.tsx +++ b/apps/www/registry/example/cool-mode-custom.tsx @@ -6,8 +6,7 @@ export default function CoolModeCustom() {
diff --git a/apps/www/registry/magicui/cool-mode.tsx b/apps/www/registry/magicui/cool-mode.tsx index 3508fd22c..6115a0272 100644 --- a/apps/www/registry/magicui/cool-mode.tsx +++ b/apps/www/registry/magicui/cool-mode.tsx @@ -28,6 +28,8 @@ export interface CoolParticleOptions extends BaseParticleOptions { speedUp?: number } +const SVG_NS = "http://www.w3.org/2000/svg" + const getContainer = () => { const id = "_coolMode_effect" const existingContainer = document.getElementById(id) @@ -68,6 +70,61 @@ const applyParticleEffect = ( const container = getContainer() + const appendCircleParticle = (particle: HTMLDivElement, size: number) => { + const circleSVG = document.createElementNS(SVG_NS, "svg") + const circle = document.createElementNS(SVG_NS, "circle") + + circle.setAttributeNS(null, "cx", (size / 2).toString()) + circle.setAttributeNS(null, "cy", (size / 2).toString()) + circle.setAttributeNS(null, "r", (size / 2).toString()) + circle.setAttributeNS(null, "fill", `hsl(${Math.random() * 360}, 70%, 50%)`) + + circleSVG.appendChild(circle) + circleSVG.setAttribute("width", size.toString()) + circleSVG.setAttribute("height", size.toString()) + + particle.appendChild(circleSVG) + } + + const appendImageParticle = ( + particle: HTMLDivElement, + imageSrc: string, + size: number + ) => { + const image = document.createElement("img") + image.src = imageSrc + image.width = size + image.height = size + image.alt = "" + image.style.borderRadius = "50%" + + particle.appendChild(image) + } + + const appendTextParticle = ( + particle: HTMLDivElement, + particleContent: string, + size: number + ) => { + const fontSizeMultiplier = 3 + const emojiSize = size * fontSizeMultiplier + const content = document.createElement("div") + + content.textContent = particleContent + content.style.fontSize = `${emojiSize}px` + content.style.lineHeight = "1" + content.style.textAlign = "center" + content.style.width = `${size}px` + content.style.height = `${size}px` + content.style.display = "flex" + content.style.alignItems = "center" + content.style.justifyContent = "center" + content.style.transform = `scale(${fontSizeMultiplier})` + content.style.transformOrigin = "center" + + particle.appendChild(content) + } + function generateParticle() { const size = options?.size || sizes[Math.floor(Math.random() * sizes.length)] @@ -82,34 +139,14 @@ const applyParticleEffect = ( const particle = document.createElement("div") if (particleType === "circle") { - const svgNS = "http://www.w3.org/2000/svg" - const circleSVG = document.createElementNS(svgNS, "svg") - const circle = document.createElementNS(svgNS, "circle") - circle.setAttributeNS(null, "cx", (size / 2).toString()) - circle.setAttributeNS(null, "cy", (size / 2).toString()) - circle.setAttributeNS(null, "r", (size / 2).toString()) - circle.setAttributeNS( - null, - "fill", - `hsl(${Math.random() * 360}, 70%, 50%)` - ) - - circleSVG.appendChild(circle) - circleSVG.setAttribute("width", size.toString()) - circleSVG.setAttribute("height", size.toString()) - - particle.appendChild(circleSVG) + appendCircleParticle(particle, size) } else if ( particleType.startsWith("http") || particleType.startsWith("/") ) { - // Handle URL-based images - particle.innerHTML = `` + appendImageParticle(particle, particleType, size) } else { - // Handle emoji or text characters - const fontSizeMultiplier = 3 // Make emojis 3x bigger - const emojiSize = size * fontSizeMultiplier - particle.innerHTML = `
${particleType}
` + appendTextParticle(particle, particleType, size) } particle.style.position = "absolute"