From 050c13d2ee40c39f7a3f28fdf9e2ef421d21ec51 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Sun, 3 May 2026 12:37:55 +0530 Subject: [PATCH 01/11] feat: add ClickSpark component and corresponding documentation --- apps/www/config/docs.ts | 6 + .../content/docs/components/click-spark.mdx | 53 ++++ apps/www/public/llms-full.txt | 195 ++++++++++++++ apps/www/public/llms.txt | 2 + apps/www/public/r/click-spark-demo.json | 18 ++ apps/www/public/r/click-spark.json | 14 + apps/www/public/r/registry.json | 28 ++ apps/www/public/registry.json | 28 ++ apps/www/registry.json | 28 ++ apps/www/registry/__index__.tsx | 34 +++ .../www/registry/example/click-spark-demo.tsx | 20 ++ apps/www/registry/magicui/click-spark.tsx | 242 ++++++++++++++++++ apps/www/registry/registry-examples.ts | 13 + apps/www/registry/registry-ui.ts | 12 + 14 files changed, 693 insertions(+) create mode 100644 apps/www/content/docs/components/click-spark.mdx create mode 100644 apps/www/public/r/click-spark-demo.json create mode 100644 apps/www/public/r/click-spark.json create mode 100644 apps/www/registry/example/click-spark-demo.tsx create mode 100644 apps/www/registry/magicui/click-spark.tsx diff --git a/apps/www/config/docs.ts b/apps/www/config/docs.ts index 0b84cab0d..f2bb035cb 100644 --- a/apps/www/config/docs.ts +++ b/apps/www/config/docs.ts @@ -333,6 +333,12 @@ export const docsConfig: DocsConfig = { { title: "Animations", items: [ + { + title: "Click Spark", + href: `/docs/components/click-spark`, + items: [], + label: "New", + }, { title: "Blur Fade", href: `/docs/components/blur-fade`, diff --git a/apps/www/content/docs/components/click-spark.mdx b/apps/www/content/docs/components/click-spark.mdx new file mode 100644 index 000000000..5432dd966 --- /dev/null +++ b/apps/www/content/docs/components/click-spark.mdx @@ -0,0 +1,53 @@ +--- +title: Click Spark +date: 2024-06-01 +description: A component that renders sparks when clicked. +author: magicui +published: true +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn@latest add @magicui/click-spark +``` + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Props + +| Prop | Type | Default | Description | +| ------------- | -------- | ---------- | --------------------------------- | +| `sparkColor` | `string` | `#fff` | Color of the sparks. | +| `sparkSize` | `number` | `10` | Size of the sparks. | +| `sparkRadius` | `number` | `15` | Radius of the sparks. | +| `sparkCount` | `number` | `8` | Number of sparks. | +| `duration` | `number` | `400` | Duration of the animation. | +| `easing` | `string` | `ease-out` | Easing function of the animation. | +| `extraScale` | `number` | `1.0` | Extra scale of the sparks. | diff --git a/apps/www/public/llms-full.txt b/apps/www/public/llms-full.txt index 832ba066c..918f1d388 100644 --- a/apps/www/public/llms-full.txt +++ b/apps/www/public/llms-full.txt @@ -4661,6 +4661,201 @@ export default function Component() { +===== COMPONENT: click-spark ===== +Title: Click Spark +Description: A component that renders sparks when clicked. + +--- file: magicui/click-spark.tsx --- +import React, { useRef, useEffect, useCallback } from 'react'; + +interface ClickSparkProps { + sparkColor?: string; + sparkSize?: number; + sparkRadius?: number; + sparkCount?: number; + duration?: number; + easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out'; + extraScale?: number; + children?: React.ReactNode; +} + +interface Spark { + x: number; + y: number; + angle: number; + startTime: number; +} + +const ClickSpark: React.FC = ({ + sparkColor = '#fff', + sparkSize = 10, + sparkRadius = 15, + sparkCount = 8, + duration = 400, + easing = 'ease-out', + extraScale = 1.0, + children +}) => { + const canvasRef = useRef(null); + const sparksRef = useRef([]); + const startTimeRef = useRef(null); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + + const parent = canvas.parentElement; + if (!parent) return; + + let resizeTimeout: ReturnType; + + const resizeCanvas = () => { + const { width, height } = parent.getBoundingClientRect(); + if (canvas.width !== width || canvas.height !== height) { + canvas.width = width; + canvas.height = height; + } + }; + + const handleResize = () => { + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(resizeCanvas, 100); + }; + + const ro = new ResizeObserver(handleResize); + ro.observe(parent); + + resizeCanvas(); + + return () => { + ro.disconnect(); + clearTimeout(resizeTimeout); + }; + }, []); + + const easeFunc = useCallback( + (t: number) => { + switch (easing) { + case 'linear': + return t; + case 'ease-in': + return t * t; + case 'ease-in-out': + return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; + default: + return t * (2 - t); + } + }, + [easing] + ); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + let animationId: number; + + const draw = (timestamp: number) => { + if (!startTimeRef.current) { + startTimeRef.current = timestamp; + } + ctx?.clearRect(0, 0, canvas.width, canvas.height); + + sparksRef.current = sparksRef.current.filter((spark: Spark) => { + const elapsed = timestamp - spark.startTime; + if (elapsed >= duration) { + return false; + } + + const progress = elapsed / duration; + const eased = easeFunc(progress); + + const distance = eased * sparkRadius * extraScale; + const lineLength = sparkSize * (1 - eased); + + const x1 = spark.x + distance * Math.cos(spark.angle); + const y1 = spark.y + distance * Math.sin(spark.angle); + const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle); + const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle); + + ctx.strokeStyle = sparkColor; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + + return true; + }); + + animationId = requestAnimationFrame(draw); + }; + + animationId = requestAnimationFrame(draw); + + return () => { + cancelAnimationFrame(animationId); + }; + }, [sparkColor, sparkSize, sparkRadius, sparkCount, duration, easeFunc, extraScale]); + + const handleClick = (e: React.MouseEvent): void => { + const canvas = canvasRef.current; + if (!canvas) return; + const rect = canvas.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + + const now = performance.now(); + const newSparks: Spark[] = Array.from({ length: sparkCount }, (_, i) => ({ + x, + y, + angle: (2 * Math.PI * i) / sparkCount, + startTime: now + })); + + sparksRef.current.push(...newSparks); + }; + + return ( +
+ + {children} +
+ ); +}; + +export default ClickSpark; + + +===== EXAMPLE: click-spark-demo ===== +Title: Click Spark Demo + +--- file: example/click-spark-demo.tsx --- +import ClickSpark from "@/registry/magicui/click-spark"; +import { Button } from "@/components/ui/button"; + +export default function ClickSparkDemo() { + return ( +
+ + + +
+ ); +} + + + ===== COMPONENT: client-tweet-card ===== Title: Client Tweet Card Description: A client-side version of the tweet card that displays a tweet with the author's name, handle, and profile picture. diff --git a/apps/www/public/llms.txt b/apps/www/public/llms.txt index b572884e4..f80757b6a 100644 --- a/apps/www/public/llms.txt +++ b/apps/www/public/llms.txt @@ -20,6 +20,7 @@ This file provides LLM-friendly entry points to documentation and examples. - [Bento Grid](https://magicui.design/docs/components/bento-grid): Bento grid is a layout used to showcase the features of a product in a simple and elegant way. - [Blur Fade](https://magicui.design/docs/components/blur-fade): Blur fade in and out animation. Used to smoothly fade in and out content. - [Border Beam](https://magicui.design/docs/components/border-beam): An animated beam of light which travels along the border of its container. +- [Click Spark](https://magicui.design/docs/components/click-spark): A component that renders sparks when clicked. - [Client Tweet Card](https://magicui.design/docs/components/client-tweet-card): A client-side version of the tweet card that displays a tweet with the author's name, handle, and profile picture. - [Code Comparison](https://magicui.design/docs/components/code-comparison): A component which compares two code snippets. - [Comic Text](https://magicui.design/docs/components/comic-text): Comic text animation @@ -84,6 +85,7 @@ This file provides LLM-friendly entry points to documentation and examples. ## Examples +- [Click Spark Demo](https://github.com/magicuidesign/magicui/blob/main/example/click-spark-demo.tsx): Example usage - [Magic Card Demo](https://github.com/magicuidesign/magicui/blob/main/example/magic-card-demo.tsx): Example usage - [Magic Card Demo 2](https://github.com/magicuidesign/magicui/blob/main/example/magic-card-demo2.tsx): Example usage - [Android Demo](https://github.com/magicuidesign/magicui/blob/main/example/android-demo.tsx): Example usage diff --git a/apps/www/public/r/click-spark-demo.json b/apps/www/public/r/click-spark-demo.json new file mode 100644 index 000000000..69f8dec69 --- /dev/null +++ b/apps/www/public/r/click-spark-demo.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "click-spark-demo", + "type": "registry:example", + "title": "Click Spark Demo", + "description": "Example showing a click spark component.", + "registryDependencies": [ + "@magicui/click-spark", + "button" + ], + "files": [ + { + "path": "registry/example/click-spark-demo.tsx", + "content": "import ClickSpark from \"@/registry/magicui/click-spark\";\nimport { Button } from \"@/components/ui/button\";\n\nexport default function ClickSparkDemo() {\n return (\n
\n \n \n \n
\n );\n}\n", + "type": "registry:example" + } + ] +} \ No newline at end of file diff --git a/apps/www/public/r/click-spark.json b/apps/www/public/r/click-spark.json new file mode 100644 index 000000000..0796cda47 --- /dev/null +++ b/apps/www/public/r/click-spark.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "click-spark", + "type": "registry:ui", + "title": "Click Spark", + "description": "A component that renders sparks when clicked.", + "files": [ + { + "path": "registry/magicui/click-spark.tsx", + "content": "import React, { useRef, useEffect, useCallback } from 'react';\n\ninterface ClickSparkProps {\n sparkColor?: string;\n sparkSize?: number;\n sparkRadius?: number;\n sparkCount?: number;\n duration?: number;\n easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';\n extraScale?: number;\n children?: React.ReactNode;\n}\n\ninterface Spark {\n x: number;\n y: number;\n angle: number;\n startTime: number;\n}\n\nconst ClickSpark: React.FC = ({\n sparkColor = '#fff',\n sparkSize = 10,\n sparkRadius = 15,\n sparkCount = 8,\n duration = 400,\n easing = 'ease-out',\n extraScale = 1.0,\n children\n}) => {\n const canvasRef = useRef(null);\n const sparksRef = useRef([]);\n const startTimeRef = useRef(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const parent = canvas.parentElement;\n if (!parent) return;\n\n let resizeTimeout: ReturnType;\n\n const resizeCanvas = () => {\n const { width, height } = parent.getBoundingClientRect();\n if (canvas.width !== width || canvas.height !== height) {\n canvas.width = width;\n canvas.height = height;\n }\n };\n\n const handleResize = () => {\n clearTimeout(resizeTimeout);\n resizeTimeout = setTimeout(resizeCanvas, 100);\n };\n\n const ro = new ResizeObserver(handleResize);\n ro.observe(parent);\n\n resizeCanvas();\n\n return () => {\n ro.disconnect();\n clearTimeout(resizeTimeout);\n };\n }, []);\n\n const easeFunc = useCallback(\n (t: number) => {\n switch (easing) {\n case 'linear':\n return t;\n case 'ease-in':\n return t * t;\n case 'ease-in-out':\n return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;\n default:\n return t * (2 - t);\n }\n },\n [easing]\n );\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n let animationId: number;\n\n const draw = (timestamp: number) => {\n if (!startTimeRef.current) {\n startTimeRef.current = timestamp;\n }\n ctx?.clearRect(0, 0, canvas.width, canvas.height);\n\n sparksRef.current = sparksRef.current.filter((spark: Spark) => {\n const elapsed = timestamp - spark.startTime;\n if (elapsed >= duration) {\n return false;\n }\n\n const progress = elapsed / duration;\n const eased = easeFunc(progress);\n\n const distance = eased * sparkRadius * extraScale;\n const lineLength = sparkSize * (1 - eased);\n\n const x1 = spark.x + distance * Math.cos(spark.angle);\n const y1 = spark.y + distance * Math.sin(spark.angle);\n const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle);\n const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle);\n\n ctx.strokeStyle = sparkColor;\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.moveTo(x1, y1);\n ctx.lineTo(x2, y2);\n ctx.stroke();\n\n return true;\n });\n\n animationId = requestAnimationFrame(draw);\n };\n\n animationId = requestAnimationFrame(draw);\n\n return () => {\n cancelAnimationFrame(animationId);\n };\n }, [sparkColor, sparkSize, sparkRadius, sparkCount, duration, easeFunc, extraScale]);\n\n const handleClick = (e: React.MouseEvent): void => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const rect = canvas.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n\n const now = performance.now();\n const newSparks: Spark[] = Array.from({ length: sparkCount }, (_, i) => ({\n x,\n y,\n angle: (2 * Math.PI * i) / sparkCount,\n startTime: now\n }));\n\n sparksRef.current.push(...newSparks);\n };\n\n return (\n
\n \n {children}\n
\n );\n};\n\nexport default ClickSpark;\n", + "type": "registry:ui" + } + ] +} \ No newline at end of file diff --git a/apps/www/public/r/registry.json b/apps/www/public/r/registry.json index 94bdd8056..9a9a2ada0 100644 --- a/apps/www/public/r/registry.json +++ b/apps/www/public/r/registry.json @@ -18,6 +18,18 @@ "files": [], "cssVars": {} }, + { + "name": "click-spark", + "type": "registry:ui", + "title": "Click Spark", + "description": "A component that renders sparks when clicked.", + "files": [ + { + "path": "registry/magicui/click-spark.tsx", + "type": "registry:ui" + } + ] + }, { "name": "magic-card", "type": "registry:ui", @@ -1335,6 +1347,22 @@ } ] }, + { + "name": "click-spark-demo", + "type": "registry:example", + "title": "Click Spark Demo", + "description": "Example showing a click spark component.", + "registryDependencies": [ + "@magicui/click-spark", + "button" + ], + "files": [ + { + "path": "registry/example/click-spark-demo.tsx", + "type": "registry:example" + } + ] + }, { "name": "magic-card-demo", "type": "registry:example", diff --git a/apps/www/public/registry.json b/apps/www/public/registry.json index 94bdd8056..9a9a2ada0 100644 --- a/apps/www/public/registry.json +++ b/apps/www/public/registry.json @@ -18,6 +18,18 @@ "files": [], "cssVars": {} }, + { + "name": "click-spark", + "type": "registry:ui", + "title": "Click Spark", + "description": "A component that renders sparks when clicked.", + "files": [ + { + "path": "registry/magicui/click-spark.tsx", + "type": "registry:ui" + } + ] + }, { "name": "magic-card", "type": "registry:ui", @@ -1335,6 +1347,22 @@ } ] }, + { + "name": "click-spark-demo", + "type": "registry:example", + "title": "Click Spark Demo", + "description": "Example showing a click spark component.", + "registryDependencies": [ + "@magicui/click-spark", + "button" + ], + "files": [ + { + "path": "registry/example/click-spark-demo.tsx", + "type": "registry:example" + } + ] + }, { "name": "magic-card-demo", "type": "registry:example", diff --git a/apps/www/registry.json b/apps/www/registry.json index 94bdd8056..9a9a2ada0 100644 --- a/apps/www/registry.json +++ b/apps/www/registry.json @@ -18,6 +18,18 @@ "files": [], "cssVars": {} }, + { + "name": "click-spark", + "type": "registry:ui", + "title": "Click Spark", + "description": "A component that renders sparks when clicked.", + "files": [ + { + "path": "registry/magicui/click-spark.tsx", + "type": "registry:ui" + } + ] + }, { "name": "magic-card", "type": "registry:ui", @@ -1335,6 +1347,22 @@ } ] }, + { + "name": "click-spark-demo", + "type": "registry:example", + "title": "Click Spark Demo", + "description": "Example showing a click spark component.", + "registryDependencies": [ + "@magicui/click-spark", + "button" + ], + "files": [ + { + "path": "registry/example/click-spark-demo.tsx", + "type": "registry:example" + } + ] + }, { "name": "magic-card-demo", "type": "registry:example", diff --git a/apps/www/registry/__index__.tsx b/apps/www/registry/__index__.tsx index f84b4092d..0b8cd53d6 100644 --- a/apps/www/registry/__index__.tsx +++ b/apps/www/registry/__index__.tsx @@ -15,6 +15,23 @@ export const Index: Record = { component: null, meta: undefined, }, + "click-spark": { + name: "click-spark", + description: "A component that renders sparks when clicked.", + type: "registry:ui", + registryDependencies: undefined, + files: [{ + path: "registry/magicui/click-spark.tsx", + type: "registry:ui", + target: "" + }], + component: React.lazy(async () => { + const mod = await import("@/registry/magicui/click-spark.tsx") + const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') ?? item.name + return { default: mod.default ?? mod[exportName] } + }), + meta: undefined, + }, "magic-card": { name: "magic-card", description: "A spotlight effect that follows your mouse cursor and highlights borders on hover.", @@ -1290,6 +1307,23 @@ export const Index: Record = { }), meta: undefined, }, + "click-spark-demo": { + name: "click-spark-demo", + description: "Example showing a click spark component.", + type: "registry:example", + registryDependencies: ["@magicui/click-spark","button"], + files: [{ + path: "registry/example/click-spark-demo.tsx", + type: "registry:example", + target: "" + }], + component: React.lazy(async () => { + const mod = await import("@/registry/example/click-spark-demo.tsx") + const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') ?? item.name + return { default: mod.default ?? mod[exportName] } + }), + meta: undefined, + }, "magic-card-demo": { name: "magic-card-demo", description: "Example showing a spotlight effect that follows your mouse cursor and highlights borders on hover.", diff --git a/apps/www/registry/example/click-spark-demo.tsx b/apps/www/registry/example/click-spark-demo.tsx new file mode 100644 index 000000000..9835b8906 --- /dev/null +++ b/apps/www/registry/example/click-spark-demo.tsx @@ -0,0 +1,20 @@ +import { Button } from "@/components/ui/button" +import ClickSpark from "@/registry/magicui/click-spark" + +export default function ClickSparkDemo() { + return ( +
+ + + +
+ ) +} diff --git a/apps/www/registry/magicui/click-spark.tsx b/apps/www/registry/magicui/click-spark.tsx new file mode 100644 index 000000000..cac0917bb --- /dev/null +++ b/apps/www/registry/magicui/click-spark.tsx @@ -0,0 +1,242 @@ +"use client" + +import React, { useCallback, useEffect, useRef } from "react" + +import { cn } from "@/lib/utils" + +interface Spark { + x: number + y: number + angle: number + startTime: number +} + +interface ClickSparkProps { + /** + * The color of the sparks. + * @default "#fff" + */ + sparkColor?: string + /** + * The size of the sparks. + * @default 10 + */ + sparkSize?: number + /** + * The radius of the spark explosion. + * @default 15 + */ + sparkRadius?: number + /** + * The number of sparks per click. + * @default 8 + */ + sparkCount?: number + /** + * The duration of the spark animation in milliseconds. + * @default 400 + */ + duration?: number + /** + * The easing function for the spark animation. + * @default "ease-out" + */ + easing?: "linear" | "ease-in" | "ease-out" | "ease-in-out" + /** + * Extra scale factor for the spark distance. + * @default 1.0 + */ + extraScale?: number + /** + * The content to wrap. + */ + children?: React.ReactNode + /** + * Additional class names for the wrapper. + */ + className?: string +} + +export function ClickSpark({ + sparkColor = "#fff", + sparkSize = 10, + sparkRadius = 15, + sparkCount = 8, + duration = 400, + easing = "ease-out", + extraScale = 1.0, + children, + className, +}: ClickSparkProps) { + const canvasRef = useRef(null) + const sparksRef = useRef([]) + const animationIdRef = useRef(null) + + // Store animation settings in a ref to keep the draw loop stable + // and avoid exhaustive-deps warnings. + const settingsRef = useRef({ + sparkColor, + sparkSize, + sparkRadius, + duration, + extraScale, + }) + + // Sync refs when props change + useEffect(() => { + settingsRef.current = { + sparkColor, + sparkSize, + sparkRadius, + duration, + extraScale, + } + }, [sparkColor, sparkSize, sparkRadius, duration, extraScale]) + + useEffect(() => { + const canvas = canvasRef.current + if (!canvas) return + + const parent = canvas.parentElement + if (!parent) return + + let resizeTimeout: ReturnType + + const resizeCanvas = () => { + const { width, height } = parent.getBoundingClientRect() + if (canvas.width !== width || canvas.height !== height) { + canvas.width = width + canvas.height = height + } + } + + const handleResize = () => { + clearTimeout(resizeTimeout) + resizeTimeout = setTimeout(resizeCanvas, 100) + } + + const ro = new ResizeObserver(handleResize) + ro.observe(parent) + + resizeCanvas() + + return () => { + ro.disconnect() + clearTimeout(resizeTimeout) + } + }, []) + + const easeFunc = useCallback( + (t: number) => { + switch (easing) { + case "linear": + return t + case "ease-in": + return t * t + case "ease-in-out": + return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t + default: + return t * (2 - t) + } + }, + [easing] + ) + + useEffect(() => { + return () => { + if (animationIdRef.current) { + cancelAnimationFrame(animationIdRef.current) + } + } + }, []) + + const draw = useCallback( + (timestamp: number) => { + const canvas = canvasRef.current + if (!canvas) return + const ctx = canvas.getContext("2d") + if (!ctx) return + + ctx.clearRect(0, 0, canvas.width, canvas.height) + + // Use settings from Ref to avoid dependency loop + const { + sparkColor: color, + sparkSize: size, + sparkRadius: radius, + duration: drn, + extraScale: scale, + } = settingsRef.current + + sparksRef.current = sparksRef.current.filter((spark: Spark) => { + const elapsed = timestamp - spark.startTime + if (elapsed >= drn) { + return false + } + + const progress = elapsed / drn + const eased = easeFunc(progress) + + const distance = eased * radius * scale + const lineLength = size * (1 - eased) + + const x1 = spark.x + distance * Math.cos(spark.angle) + const y1 = spark.y + distance * Math.sin(spark.angle) + const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle) + const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle) + + ctx.strokeStyle = color + ctx.lineWidth = 2 + ctx.beginPath() + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + ctx.stroke() + + return true + }) + + if (sparksRef.current.length > 0) { + animationIdRef.current = requestAnimationFrame(draw) + } else { + animationIdRef.current = null + } + }, + [easeFunc] + ) + + const handleClick = (e: React.MouseEvent): void => { + const canvas = canvasRef.current + if (!canvas) return + const rect = canvas.getBoundingClientRect() + const x = e.clientX - rect.left + const y = e.clientY - rect.top + + const now = performance.now() + // sparkCount is only used here to initialize the objects + const newSparks: Spark[] = Array.from({ length: sparkCount }, (_, i) => ({ + x, + y, + angle: (2 * Math.PI * i) / sparkCount, + startTime: now, + })) + + sparksRef.current.push(...newSparks) + + if (animationIdRef.current === null) { + animationIdRef.current = requestAnimationFrame(draw) + } + } + + return ( +
+ + {children} +
+ ) +} diff --git a/apps/www/registry/registry-examples.ts b/apps/www/registry/registry-examples.ts index d3a5b3a21..0fafd95ac 100644 --- a/apps/www/registry/registry-examples.ts +++ b/apps/www/registry/registry-examples.ts @@ -1,6 +1,19 @@ import { type Registry } from "shadcn/schema" export const examples: Registry["items"] = [ + { + name: "click-spark-demo", + type: "registry:example", + title: "Click Spark Demo", + description: "Example showing a click spark component.", + registryDependencies: ["@magicui/click-spark", "button"], + files: [ + { + path: "example/click-spark-demo.tsx", + type: "registry:example", + }, + ], + }, { name: "magic-card-demo", type: "registry:example", diff --git a/apps/www/registry/registry-ui.ts b/apps/www/registry/registry-ui.ts index 2caf25133..af5c1a45a 100644 --- a/apps/www/registry/registry-ui.ts +++ b/apps/www/registry/registry-ui.ts @@ -1,6 +1,18 @@ import { type Registry } from "shadcn/schema" export const ui: Registry["items"] = [ + { + name: "click-spark", + type: "registry:ui", + title: "Click Spark", + description: "A component that renders sparks when clicked.", + files: [ + { + path: "magicui/click-spark.tsx", + type: "registry:ui", + }, + ], + }, { name: "magic-card", type: "registry:ui", From 3921180fdaec3fb8e8d845bb981b3d45c915a892 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Sun, 3 May 2026 12:58:12 +0530 Subject: [PATCH 02/11] feat: add ClickSpark demo component to registry --- .../www/registry/example/click-spark-demo.tsx | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/apps/www/registry/example/click-spark-demo.tsx b/apps/www/registry/example/click-spark-demo.tsx index 9835b8906..9df205287 100644 --- a/apps/www/registry/example/click-spark-demo.tsx +++ b/apps/www/registry/example/click-spark-demo.tsx @@ -1,19 +1,24 @@ -import { Button } from "@/components/ui/button" -import ClickSpark from "@/registry/magicui/click-spark" +import { ClickSpark } from "@/registry/magicui/click-spark" export default function ClickSparkDemo() { return ( -
+
- +
+

+ Interactive Spark +

+

+ Click anywhere to experience the effect +

+
) From 44324690961f63000386a8352c95eea8bcfb40c5 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Sun, 3 May 2026 13:07:57 +0530 Subject: [PATCH 03/11] docs: add documentation for Click Spark component --- apps/www/content/docs/components/click-spark.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/content/docs/components/click-spark.mdx b/apps/www/content/docs/components/click-spark.mdx index 5432dd966..75666ba9c 100644 --- a/apps/www/content/docs/components/click-spark.mdx +++ b/apps/www/content/docs/components/click-spark.mdx @@ -2,7 +2,7 @@ title: Click Spark date: 2024-06-01 description: A component that renders sparks when clicked. -author: magicui +author: rishabhmishra published: true --- From 8c06a56d203b76cdac331fbb376219d135efd328 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Sun, 3 May 2026 13:11:13 +0530 Subject: [PATCH 04/11] feat: add click-spark-demo registry example --- apps/www/public/r/click-spark-demo.json | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/www/public/r/click-spark-demo.json b/apps/www/public/r/click-spark-demo.json index 69f8dec69..4d208504e 100644 --- a/apps/www/public/r/click-spark-demo.json +++ b/apps/www/public/r/click-spark-demo.json @@ -6,7 +6,6 @@ "description": "Example showing a click spark component.", "registryDependencies": [ "@magicui/click-spark", - "button" ], "files": [ { From 903c3a95b5f8890c9936c815042d8e544143992d Mon Sep 17 00:00:00 2001 From: Rishabh Date: Sun, 3 May 2026 13:12:59 +0530 Subject: [PATCH 05/11] feat: initialize registry index and configuration files with Magic UI components --- apps/www/public/r/click-spark-demo.json | 2 +- apps/www/registry.json | 3 +-- apps/www/registry/__index__.tsx | 36 ++++++++++++------------- apps/www/registry/registry-examples.ts | 2 +- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/apps/www/public/r/click-spark-demo.json b/apps/www/public/r/click-spark-demo.json index 4d208504e..e6917f873 100644 --- a/apps/www/public/r/click-spark-demo.json +++ b/apps/www/public/r/click-spark-demo.json @@ -5,7 +5,7 @@ "title": "Click Spark Demo", "description": "Example showing a click spark component.", "registryDependencies": [ - "@magicui/click-spark", + "@magicui/click-spark" ], "files": [ { diff --git a/apps/www/registry.json b/apps/www/registry.json index 9a9a2ada0..f80ee66c2 100644 --- a/apps/www/registry.json +++ b/apps/www/registry.json @@ -1353,8 +1353,7 @@ "title": "Click Spark Demo", "description": "Example showing a click spark component.", "registryDependencies": [ - "@magicui/click-spark", - "button" + "@magicui/click-spark" ], "files": [ { diff --git a/apps/www/registry/__index__.tsx b/apps/www/registry/__index__.tsx index 0b8cd53d6..4089b813a 100644 --- a/apps/www/registry/__index__.tsx +++ b/apps/www/registry/__index__.tsx @@ -1311,7 +1311,7 @@ export const Index: Record = { name: "click-spark-demo", description: "Example showing a click spark component.", type: "registry:example", - registryDependencies: ["@magicui/click-spark","button"], + registryDependencies: ["@magicui/click-spark"], files: [{ path: "registry/example/click-spark-demo.tsx", type: "registry:example", @@ -1345,7 +1345,7 @@ export const Index: Record = { name: "magic-card-demo-2", description: "Example showing a magic card with an orb effect.", type: "registry:example", - registryDependencies: ["@magicui/magic-card","@magicui/avatar-circles"], + registryDependencies: ["@magicui/magic-card", "@magicui/avatar-circles"], files: [{ path: "registry/example/magic-card-demo2.tsx", type: "registry:example", @@ -1498,7 +1498,7 @@ export const Index: Record = { name: "lens-demo", description: "Example showing a lens effect component", type: "registry:example", - registryDependencies: ["button","card","@magicui/lens"], + registryDependencies: ["button", "card", "@magicui/lens"], files: [{ path: "registry/example/lens-demo.tsx", type: "registry:example", @@ -1515,7 +1515,7 @@ export const Index: Record = { name: "lens-demo-2", description: "Second example showing a lens effect component", type: "registry:example", - registryDependencies: ["button","card","@magicui/lens"], + registryDependencies: ["button", "card", "@magicui/lens"], files: [{ path: "registry/example/lens-demo-2.tsx", type: "registry:example", @@ -1532,7 +1532,7 @@ export const Index: Record = { name: "lens-demo-3", description: "Third example showing a lens effect component", type: "registry:example", - registryDependencies: ["button","card","@magicui/lens"], + registryDependencies: ["button", "card", "@magicui/lens"], files: [{ path: "registry/example/lens-demo-3.tsx", type: "registry:example", @@ -1634,7 +1634,7 @@ export const Index: Record = { name: "noise-texture-demo-2", description: "Example showing a newsletter signup card with email input over a noise texture.", type: "registry:example", - registryDependencies: ["@magicui/noise-texture","button","card","input","label"], + registryDependencies: ["@magicui/noise-texture", "button", "card", "input", "label"], files: [{ path: "registry/example/noise-texture-demo-2.tsx", type: "registry:example", @@ -1651,7 +1651,7 @@ export const Index: Record = { name: "noise-texture-demo-3", description: "Example showing NoiseTexture behind a button label inside a relative button.", type: "registry:example", - registryDependencies: ["@magicui/noise-texture","button"], + registryDependencies: ["@magicui/noise-texture", "button"], files: [{ path: "registry/example/noise-texture-demo-3.tsx", type: "registry:example", @@ -1668,7 +1668,7 @@ export const Index: Record = { name: "noise-texture-demo-4", description: "Example showing a labeled input field with NoiseTexture filling the input container.", type: "registry:example", - registryDependencies: ["@magicui/noise-texture","input","label"], + registryDependencies: ["@magicui/noise-texture", "input", "label"], files: [{ path: "registry/example/noise-texture-demo-4.tsx", type: "registry:example", @@ -2144,7 +2144,7 @@ export const Index: Record = { name: "tweet-card-demo", description: "Example showing a tweet card with author info.", type: "registry:example", - registryDependencies: ["@magicui/client-tweet-card","@magicui/tweet-card"], + registryDependencies: ["@magicui/client-tweet-card", "@magicui/tweet-card"], files: [{ path: "registry/example/tweet-card-demo.tsx", type: "registry:example", @@ -2161,7 +2161,7 @@ export const Index: Record = { name: "tweet-card-images", description: "Example showing a tweet card with images.", type: "registry:example", - registryDependencies: ["@magicui/client-tweet-card","@magicui/tweet-card"], + registryDependencies: ["@magicui/client-tweet-card", "@magicui/tweet-card"], files: [{ path: "registry/example/tweet-card-images.tsx", type: "registry:example", @@ -2178,7 +2178,7 @@ export const Index: Record = { name: "tweet-card-meta-preview", description: "Example showing a tweet card with meta preview.", type: "registry:example", - registryDependencies: ["@magicui/client-tweet-card","@magicui/tweet-card"], + registryDependencies: ["@magicui/client-tweet-card", "@magicui/tweet-card"], files: [{ path: "registry/example/tweet-card-meta-preview.tsx", type: "registry:example", @@ -2212,7 +2212,7 @@ export const Index: Record = { name: "bento-demo", description: "Example showing a bento grid layout for showcasing features.", type: "registry:example", - registryDependencies: ["button","calendar","@magicui/marquee","@magicui/bento-grid","@magicui/animated-beam","@magicui/animated-list","@magicui/animated-beam-multiple-outputs","@magicui/animated-list-demo"], + registryDependencies: ["button", "calendar", "@magicui/marquee", "@magicui/bento-grid", "@magicui/animated-beam", "@magicui/animated-list", "@magicui/animated-beam-multiple-outputs", "@magicui/animated-list-demo"], files: [{ path: "registry/example/bento-demo.tsx", type: "registry:example", @@ -2450,7 +2450,7 @@ export const Index: Record = { name: "border-beam-demo-2", description: "Example showing an animated border beam effect.", type: "registry:example", - registryDependencies: ["button","card","@magicui/border-beam"], + registryDependencies: ["button", "card", "@magicui/border-beam"], files: [{ path: "registry/example/border-beam-demo-2.tsx", type: "registry:example", @@ -2467,7 +2467,7 @@ export const Index: Record = { name: "border-beam-demo-3", description: "Example showing an animated border beam effect.", type: "registry:example", - registryDependencies: ["button","card","input","label","@magicui/border-beam"], + registryDependencies: ["button", "card", "input", "label", "@magicui/border-beam"], files: [{ path: "registry/example/border-beam-demo-3.tsx", type: "registry:example", @@ -2484,7 +2484,7 @@ export const Index: Record = { name: "border-beam-demo-4", description: "Example showing an animated border beam effect.", type: "registry:example", - registryDependencies: ["button","@magicui/border-beam"], + registryDependencies: ["button", "@magicui/border-beam"], files: [{ path: "registry/example/border-beam-demo-4.tsx", type: "registry:example", @@ -2722,7 +2722,7 @@ export const Index: Record = { name: "dock-demo", description: "Example showing a MacOS-style dock implementation.", type: "registry:example", - registryDependencies: ["button","@magicui/dock"], + registryDependencies: ["button", "@magicui/dock"], files: [{ path: "registry/example/dock-demo.tsx", type: "registry:example", @@ -3849,7 +3849,7 @@ export const Index: Record = { path: "registry/example/highlighter-demo.tsx", type: "registry:example", target: "" - },{ + }, { path: "registry/magicui/highlighter.tsx", type: "registry:ui", target: "" @@ -4150,4 +4150,4 @@ export const Index: Record = { }), meta: undefined, }, - } \ No newline at end of file +} \ No newline at end of file diff --git a/apps/www/registry/registry-examples.ts b/apps/www/registry/registry-examples.ts index 0fafd95ac..fe8515930 100644 --- a/apps/www/registry/registry-examples.ts +++ b/apps/www/registry/registry-examples.ts @@ -6,7 +6,7 @@ export const examples: Registry["items"] = [ type: "registry:example", title: "Click Spark Demo", description: "Example showing a click spark component.", - registryDependencies: ["@magicui/click-spark", "button"], + registryDependencies: ["@magicui/click-spark"], files: [ { path: "example/click-spark-demo.tsx", From 6206131e88190d6ae6c3671b63fea87fbc4e472b Mon Sep 17 00:00:00 2001 From: Rishabh Date: Sun, 3 May 2026 12:37:55 +0530 Subject: [PATCH 06/11] feat: add ClickSpark component and corresponding documentation --- apps/www/config/docs.ts | 6 + .../content/docs/components/click-spark.mdx | 53 ++++ apps/www/public/llms-full.txt | 195 ++++++++++++++ apps/www/public/llms.txt | 2 + apps/www/public/r/click-spark-demo.json | 18 ++ apps/www/public/r/click-spark.json | 14 + apps/www/public/r/registry.json | 28 ++ apps/www/public/registry.json | 28 ++ apps/www/registry.json | 28 ++ apps/www/registry/__index__.tsx | 34 +++ .../www/registry/example/click-spark-demo.tsx | 20 ++ apps/www/registry/magicui/click-spark.tsx | 242 ++++++++++++++++++ apps/www/registry/registry-examples.ts | 13 + apps/www/registry/registry-ui.ts | 12 + 14 files changed, 693 insertions(+) create mode 100644 apps/www/content/docs/components/click-spark.mdx create mode 100644 apps/www/public/r/click-spark-demo.json create mode 100644 apps/www/public/r/click-spark.json create mode 100644 apps/www/registry/example/click-spark-demo.tsx create mode 100644 apps/www/registry/magicui/click-spark.tsx diff --git a/apps/www/config/docs.ts b/apps/www/config/docs.ts index 92df27973..cb049d1e2 100644 --- a/apps/www/config/docs.ts +++ b/apps/www/config/docs.ts @@ -333,6 +333,12 @@ export const docsConfig: DocsConfig = { { title: "Animations", items: [ + { + title: "Click Spark", + href: `/docs/components/click-spark`, + items: [], + label: "New", + }, { title: "Blur Fade", href: `/docs/components/blur-fade`, diff --git a/apps/www/content/docs/components/click-spark.mdx b/apps/www/content/docs/components/click-spark.mdx new file mode 100644 index 000000000..5432dd966 --- /dev/null +++ b/apps/www/content/docs/components/click-spark.mdx @@ -0,0 +1,53 @@ +--- +title: Click Spark +date: 2024-06-01 +description: A component that renders sparks when clicked. +author: magicui +published: true +--- + + + +## Installation + + + + + CLI + Manual + + + +```bash +npx shadcn@latest add @magicui/click-spark +``` + + + + + + + +Copy and paste the following code into your project. + + + +Update the import paths to match your project setup. + + + + + + + +## Props + +| Prop | Type | Default | Description | +| ------------- | -------- | ---------- | --------------------------------- | +| `sparkColor` | `string` | `#fff` | Color of the sparks. | +| `sparkSize` | `number` | `10` | Size of the sparks. | +| `sparkRadius` | `number` | `15` | Radius of the sparks. | +| `sparkCount` | `number` | `8` | Number of sparks. | +| `duration` | `number` | `400` | Duration of the animation. | +| `easing` | `string` | `ease-out` | Easing function of the animation. | +| `extraScale` | `number` | `1.0` | Extra scale of the sparks. | diff --git a/apps/www/public/llms-full.txt b/apps/www/public/llms-full.txt index 39bd644c8..a66aa12ee 100644 --- a/apps/www/public/llms-full.txt +++ b/apps/www/public/llms-full.txt @@ -4661,6 +4661,201 @@ export default function Component() { +===== COMPONENT: click-spark ===== +Title: Click Spark +Description: A component that renders sparks when clicked. + +--- file: magicui/click-spark.tsx --- +import React, { useRef, useEffect, useCallback } from 'react'; + +interface ClickSparkProps { + sparkColor?: string; + sparkSize?: number; + sparkRadius?: number; + sparkCount?: number; + duration?: number; + easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out'; + extraScale?: number; + children?: React.ReactNode; +} + +interface Spark { + x: number; + y: number; + angle: number; + startTime: number; +} + +const ClickSpark: React.FC = ({ + sparkColor = '#fff', + sparkSize = 10, + sparkRadius = 15, + sparkCount = 8, + duration = 400, + easing = 'ease-out', + extraScale = 1.0, + children +}) => { + const canvasRef = useRef(null); + const sparksRef = useRef([]); + const startTimeRef = useRef(null); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + + const parent = canvas.parentElement; + if (!parent) return; + + let resizeTimeout: ReturnType; + + const resizeCanvas = () => { + const { width, height } = parent.getBoundingClientRect(); + if (canvas.width !== width || canvas.height !== height) { + canvas.width = width; + canvas.height = height; + } + }; + + const handleResize = () => { + clearTimeout(resizeTimeout); + resizeTimeout = setTimeout(resizeCanvas, 100); + }; + + const ro = new ResizeObserver(handleResize); + ro.observe(parent); + + resizeCanvas(); + + return () => { + ro.disconnect(); + clearTimeout(resizeTimeout); + }; + }, []); + + const easeFunc = useCallback( + (t: number) => { + switch (easing) { + case 'linear': + return t; + case 'ease-in': + return t * t; + case 'ease-in-out': + return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; + default: + return t * (2 - t); + } + }, + [easing] + ); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + let animationId: number; + + const draw = (timestamp: number) => { + if (!startTimeRef.current) { + startTimeRef.current = timestamp; + } + ctx?.clearRect(0, 0, canvas.width, canvas.height); + + sparksRef.current = sparksRef.current.filter((spark: Spark) => { + const elapsed = timestamp - spark.startTime; + if (elapsed >= duration) { + return false; + } + + const progress = elapsed / duration; + const eased = easeFunc(progress); + + const distance = eased * sparkRadius * extraScale; + const lineLength = sparkSize * (1 - eased); + + const x1 = spark.x + distance * Math.cos(spark.angle); + const y1 = spark.y + distance * Math.sin(spark.angle); + const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle); + const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle); + + ctx.strokeStyle = sparkColor; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + + return true; + }); + + animationId = requestAnimationFrame(draw); + }; + + animationId = requestAnimationFrame(draw); + + return () => { + cancelAnimationFrame(animationId); + }; + }, [sparkColor, sparkSize, sparkRadius, sparkCount, duration, easeFunc, extraScale]); + + const handleClick = (e: React.MouseEvent): void => { + const canvas = canvasRef.current; + if (!canvas) return; + const rect = canvas.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + + const now = performance.now(); + const newSparks: Spark[] = Array.from({ length: sparkCount }, (_, i) => ({ + x, + y, + angle: (2 * Math.PI * i) / sparkCount, + startTime: now + })); + + sparksRef.current.push(...newSparks); + }; + + return ( +
+ + {children} +
+ ); +}; + +export default ClickSpark; + + +===== EXAMPLE: click-spark-demo ===== +Title: Click Spark Demo + +--- file: example/click-spark-demo.tsx --- +import ClickSpark from "@/registry/magicui/click-spark"; +import { Button } from "@/components/ui/button"; + +export default function ClickSparkDemo() { + return ( +
+ + + +
+ ); +} + + + ===== COMPONENT: client-tweet-card ===== Title: Client Tweet Card Description: A client-side version of the tweet card that displays a tweet with the author's name, handle, and profile picture. diff --git a/apps/www/public/llms.txt b/apps/www/public/llms.txt index 034ca66e5..b9efda24d 100644 --- a/apps/www/public/llms.txt +++ b/apps/www/public/llms.txt @@ -20,6 +20,7 @@ This file provides LLM-friendly entry points to documentation and examples. - [Bento Grid](https://magicui.design/docs/components/bento-grid): Bento grid is a layout used to showcase the features of a product in a simple and elegant way. - [Blur Fade](https://magicui.design/docs/components/blur-fade): Blur fade in and out animation. Used to smoothly fade in and out content. - [Border Beam](https://magicui.design/docs/components/border-beam): An animated beam of light which travels along the border of its container. +- [Click Spark](https://magicui.design/docs/components/click-spark): A component that renders sparks when clicked. - [Client Tweet Card](https://magicui.design/docs/components/client-tweet-card): A client-side version of the tweet card that displays a tweet with the author's name, handle, and profile picture. - [Code Comparison](https://magicui.design/docs/components/code-comparison): A component which compares two code snippets. - [Comic Text](https://magicui.design/docs/components/comic-text): Comic text animation @@ -85,6 +86,7 @@ This file provides LLM-friendly entry points to documentation and examples. ## Examples +- [Click Spark Demo](https://github.com/magicuidesign/magicui/blob/main/example/click-spark-demo.tsx): Example usage - [Magic Card Demo](https://github.com/magicuidesign/magicui/blob/main/example/magic-card-demo.tsx): Example usage - [Magic Card Demo 2](https://github.com/magicuidesign/magicui/blob/main/example/magic-card-demo2.tsx): Example usage - [Android Demo](https://github.com/magicuidesign/magicui/blob/main/example/android-demo.tsx): Example usage diff --git a/apps/www/public/r/click-spark-demo.json b/apps/www/public/r/click-spark-demo.json new file mode 100644 index 000000000..69f8dec69 --- /dev/null +++ b/apps/www/public/r/click-spark-demo.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "click-spark-demo", + "type": "registry:example", + "title": "Click Spark Demo", + "description": "Example showing a click spark component.", + "registryDependencies": [ + "@magicui/click-spark", + "button" + ], + "files": [ + { + "path": "registry/example/click-spark-demo.tsx", + "content": "import ClickSpark from \"@/registry/magicui/click-spark\";\nimport { Button } from \"@/components/ui/button\";\n\nexport default function ClickSparkDemo() {\n return (\n
\n \n \n \n
\n );\n}\n", + "type": "registry:example" + } + ] +} \ No newline at end of file diff --git a/apps/www/public/r/click-spark.json b/apps/www/public/r/click-spark.json new file mode 100644 index 000000000..0796cda47 --- /dev/null +++ b/apps/www/public/r/click-spark.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "click-spark", + "type": "registry:ui", + "title": "Click Spark", + "description": "A component that renders sparks when clicked.", + "files": [ + { + "path": "registry/magicui/click-spark.tsx", + "content": "import React, { useRef, useEffect, useCallback } from 'react';\n\ninterface ClickSparkProps {\n sparkColor?: string;\n sparkSize?: number;\n sparkRadius?: number;\n sparkCount?: number;\n duration?: number;\n easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';\n extraScale?: number;\n children?: React.ReactNode;\n}\n\ninterface Spark {\n x: number;\n y: number;\n angle: number;\n startTime: number;\n}\n\nconst ClickSpark: React.FC = ({\n sparkColor = '#fff',\n sparkSize = 10,\n sparkRadius = 15,\n sparkCount = 8,\n duration = 400,\n easing = 'ease-out',\n extraScale = 1.0,\n children\n}) => {\n const canvasRef = useRef(null);\n const sparksRef = useRef([]);\n const startTimeRef = useRef(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const parent = canvas.parentElement;\n if (!parent) return;\n\n let resizeTimeout: ReturnType;\n\n const resizeCanvas = () => {\n const { width, height } = parent.getBoundingClientRect();\n if (canvas.width !== width || canvas.height !== height) {\n canvas.width = width;\n canvas.height = height;\n }\n };\n\n const handleResize = () => {\n clearTimeout(resizeTimeout);\n resizeTimeout = setTimeout(resizeCanvas, 100);\n };\n\n const ro = new ResizeObserver(handleResize);\n ro.observe(parent);\n\n resizeCanvas();\n\n return () => {\n ro.disconnect();\n clearTimeout(resizeTimeout);\n };\n }, []);\n\n const easeFunc = useCallback(\n (t: number) => {\n switch (easing) {\n case 'linear':\n return t;\n case 'ease-in':\n return t * t;\n case 'ease-in-out':\n return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;\n default:\n return t * (2 - t);\n }\n },\n [easing]\n );\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n let animationId: number;\n\n const draw = (timestamp: number) => {\n if (!startTimeRef.current) {\n startTimeRef.current = timestamp;\n }\n ctx?.clearRect(0, 0, canvas.width, canvas.height);\n\n sparksRef.current = sparksRef.current.filter((spark: Spark) => {\n const elapsed = timestamp - spark.startTime;\n if (elapsed >= duration) {\n return false;\n }\n\n const progress = elapsed / duration;\n const eased = easeFunc(progress);\n\n const distance = eased * sparkRadius * extraScale;\n const lineLength = sparkSize * (1 - eased);\n\n const x1 = spark.x + distance * Math.cos(spark.angle);\n const y1 = spark.y + distance * Math.sin(spark.angle);\n const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle);\n const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle);\n\n ctx.strokeStyle = sparkColor;\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.moveTo(x1, y1);\n ctx.lineTo(x2, y2);\n ctx.stroke();\n\n return true;\n });\n\n animationId = requestAnimationFrame(draw);\n };\n\n animationId = requestAnimationFrame(draw);\n\n return () => {\n cancelAnimationFrame(animationId);\n };\n }, [sparkColor, sparkSize, sparkRadius, sparkCount, duration, easeFunc, extraScale]);\n\n const handleClick = (e: React.MouseEvent): void => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const rect = canvas.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n\n const now = performance.now();\n const newSparks: Spark[] = Array.from({ length: sparkCount }, (_, i) => ({\n x,\n y,\n angle: (2 * Math.PI * i) / sparkCount,\n startTime: now\n }));\n\n sparksRef.current.push(...newSparks);\n };\n\n return (\n
\n \n {children}\n
\n );\n};\n\nexport default ClickSpark;\n", + "type": "registry:ui" + } + ] +} \ No newline at end of file diff --git a/apps/www/public/r/registry.json b/apps/www/public/r/registry.json index fa8ff0eb1..2915f19ad 100644 --- a/apps/www/public/r/registry.json +++ b/apps/www/public/r/registry.json @@ -18,6 +18,18 @@ "files": [], "cssVars": {} }, + { + "name": "click-spark", + "type": "registry:ui", + "title": "Click Spark", + "description": "A component that renders sparks when clicked.", + "files": [ + { + "path": "registry/magicui/click-spark.tsx", + "type": "registry:ui" + } + ] + }, { "name": "magic-card", "type": "registry:ui", @@ -1348,6 +1360,22 @@ } ] }, + { + "name": "click-spark-demo", + "type": "registry:example", + "title": "Click Spark Demo", + "description": "Example showing a click spark component.", + "registryDependencies": [ + "@magicui/click-spark", + "button" + ], + "files": [ + { + "path": "registry/example/click-spark-demo.tsx", + "type": "registry:example" + } + ] + }, { "name": "magic-card-demo", "type": "registry:example", diff --git a/apps/www/public/registry.json b/apps/www/public/registry.json index fa8ff0eb1..2915f19ad 100644 --- a/apps/www/public/registry.json +++ b/apps/www/public/registry.json @@ -18,6 +18,18 @@ "files": [], "cssVars": {} }, + { + "name": "click-spark", + "type": "registry:ui", + "title": "Click Spark", + "description": "A component that renders sparks when clicked.", + "files": [ + { + "path": "registry/magicui/click-spark.tsx", + "type": "registry:ui" + } + ] + }, { "name": "magic-card", "type": "registry:ui", @@ -1348,6 +1360,22 @@ } ] }, + { + "name": "click-spark-demo", + "type": "registry:example", + "title": "Click Spark Demo", + "description": "Example showing a click spark component.", + "registryDependencies": [ + "@magicui/click-spark", + "button" + ], + "files": [ + { + "path": "registry/example/click-spark-demo.tsx", + "type": "registry:example" + } + ] + }, { "name": "magic-card-demo", "type": "registry:example", diff --git a/apps/www/registry.json b/apps/www/registry.json index fa8ff0eb1..2915f19ad 100644 --- a/apps/www/registry.json +++ b/apps/www/registry.json @@ -18,6 +18,18 @@ "files": [], "cssVars": {} }, + { + "name": "click-spark", + "type": "registry:ui", + "title": "Click Spark", + "description": "A component that renders sparks when clicked.", + "files": [ + { + "path": "registry/magicui/click-spark.tsx", + "type": "registry:ui" + } + ] + }, { "name": "magic-card", "type": "registry:ui", @@ -1348,6 +1360,22 @@ } ] }, + { + "name": "click-spark-demo", + "type": "registry:example", + "title": "Click Spark Demo", + "description": "Example showing a click spark component.", + "registryDependencies": [ + "@magicui/click-spark", + "button" + ], + "files": [ + { + "path": "registry/example/click-spark-demo.tsx", + "type": "registry:example" + } + ] + }, { "name": "magic-card-demo", "type": "registry:example", diff --git a/apps/www/registry/__index__.tsx b/apps/www/registry/__index__.tsx index c660e626b..67ed315cd 100644 --- a/apps/www/registry/__index__.tsx +++ b/apps/www/registry/__index__.tsx @@ -15,6 +15,23 @@ export const Index: Record = { component: null, meta: undefined, }, + "click-spark": { + name: "click-spark", + description: "A component that renders sparks when clicked.", + type: "registry:ui", + registryDependencies: undefined, + files: [{ + path: "registry/magicui/click-spark.tsx", + type: "registry:ui", + target: "" + }], + component: React.lazy(async () => { + const mod = await import("@/registry/magicui/click-spark.tsx") + const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') ?? item.name + return { default: mod.default ?? mod[exportName] } + }), + meta: undefined, + }, "magic-card": { name: "magic-card", description: "A spotlight effect that follows your mouse cursor and highlights borders on hover.", @@ -1307,6 +1324,23 @@ export const Index: Record = { }), meta: undefined, }, + "click-spark-demo": { + name: "click-spark-demo", + description: "Example showing a click spark component.", + type: "registry:example", + registryDependencies: ["@magicui/click-spark","button"], + files: [{ + path: "registry/example/click-spark-demo.tsx", + type: "registry:example", + target: "" + }], + component: React.lazy(async () => { + const mod = await import("@/registry/example/click-spark-demo.tsx") + const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') ?? item.name + return { default: mod.default ?? mod[exportName] } + }), + meta: undefined, + }, "magic-card-demo": { name: "magic-card-demo", description: "Example showing a spotlight effect that follows your mouse cursor and highlights borders on hover.", diff --git a/apps/www/registry/example/click-spark-demo.tsx b/apps/www/registry/example/click-spark-demo.tsx new file mode 100644 index 000000000..9835b8906 --- /dev/null +++ b/apps/www/registry/example/click-spark-demo.tsx @@ -0,0 +1,20 @@ +import { Button } from "@/components/ui/button" +import ClickSpark from "@/registry/magicui/click-spark" + +export default function ClickSparkDemo() { + return ( +
+ + + +
+ ) +} diff --git a/apps/www/registry/magicui/click-spark.tsx b/apps/www/registry/magicui/click-spark.tsx new file mode 100644 index 000000000..cac0917bb --- /dev/null +++ b/apps/www/registry/magicui/click-spark.tsx @@ -0,0 +1,242 @@ +"use client" + +import React, { useCallback, useEffect, useRef } from "react" + +import { cn } from "@/lib/utils" + +interface Spark { + x: number + y: number + angle: number + startTime: number +} + +interface ClickSparkProps { + /** + * The color of the sparks. + * @default "#fff" + */ + sparkColor?: string + /** + * The size of the sparks. + * @default 10 + */ + sparkSize?: number + /** + * The radius of the spark explosion. + * @default 15 + */ + sparkRadius?: number + /** + * The number of sparks per click. + * @default 8 + */ + sparkCount?: number + /** + * The duration of the spark animation in milliseconds. + * @default 400 + */ + duration?: number + /** + * The easing function for the spark animation. + * @default "ease-out" + */ + easing?: "linear" | "ease-in" | "ease-out" | "ease-in-out" + /** + * Extra scale factor for the spark distance. + * @default 1.0 + */ + extraScale?: number + /** + * The content to wrap. + */ + children?: React.ReactNode + /** + * Additional class names for the wrapper. + */ + className?: string +} + +export function ClickSpark({ + sparkColor = "#fff", + sparkSize = 10, + sparkRadius = 15, + sparkCount = 8, + duration = 400, + easing = "ease-out", + extraScale = 1.0, + children, + className, +}: ClickSparkProps) { + const canvasRef = useRef(null) + const sparksRef = useRef([]) + const animationIdRef = useRef(null) + + // Store animation settings in a ref to keep the draw loop stable + // and avoid exhaustive-deps warnings. + const settingsRef = useRef({ + sparkColor, + sparkSize, + sparkRadius, + duration, + extraScale, + }) + + // Sync refs when props change + useEffect(() => { + settingsRef.current = { + sparkColor, + sparkSize, + sparkRadius, + duration, + extraScale, + } + }, [sparkColor, sparkSize, sparkRadius, duration, extraScale]) + + useEffect(() => { + const canvas = canvasRef.current + if (!canvas) return + + const parent = canvas.parentElement + if (!parent) return + + let resizeTimeout: ReturnType + + const resizeCanvas = () => { + const { width, height } = parent.getBoundingClientRect() + if (canvas.width !== width || canvas.height !== height) { + canvas.width = width + canvas.height = height + } + } + + const handleResize = () => { + clearTimeout(resizeTimeout) + resizeTimeout = setTimeout(resizeCanvas, 100) + } + + const ro = new ResizeObserver(handleResize) + ro.observe(parent) + + resizeCanvas() + + return () => { + ro.disconnect() + clearTimeout(resizeTimeout) + } + }, []) + + const easeFunc = useCallback( + (t: number) => { + switch (easing) { + case "linear": + return t + case "ease-in": + return t * t + case "ease-in-out": + return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t + default: + return t * (2 - t) + } + }, + [easing] + ) + + useEffect(() => { + return () => { + if (animationIdRef.current) { + cancelAnimationFrame(animationIdRef.current) + } + } + }, []) + + const draw = useCallback( + (timestamp: number) => { + const canvas = canvasRef.current + if (!canvas) return + const ctx = canvas.getContext("2d") + if (!ctx) return + + ctx.clearRect(0, 0, canvas.width, canvas.height) + + // Use settings from Ref to avoid dependency loop + const { + sparkColor: color, + sparkSize: size, + sparkRadius: radius, + duration: drn, + extraScale: scale, + } = settingsRef.current + + sparksRef.current = sparksRef.current.filter((spark: Spark) => { + const elapsed = timestamp - spark.startTime + if (elapsed >= drn) { + return false + } + + const progress = elapsed / drn + const eased = easeFunc(progress) + + const distance = eased * radius * scale + const lineLength = size * (1 - eased) + + const x1 = spark.x + distance * Math.cos(spark.angle) + const y1 = spark.y + distance * Math.sin(spark.angle) + const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle) + const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle) + + ctx.strokeStyle = color + ctx.lineWidth = 2 + ctx.beginPath() + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + ctx.stroke() + + return true + }) + + if (sparksRef.current.length > 0) { + animationIdRef.current = requestAnimationFrame(draw) + } else { + animationIdRef.current = null + } + }, + [easeFunc] + ) + + const handleClick = (e: React.MouseEvent): void => { + const canvas = canvasRef.current + if (!canvas) return + const rect = canvas.getBoundingClientRect() + const x = e.clientX - rect.left + const y = e.clientY - rect.top + + const now = performance.now() + // sparkCount is only used here to initialize the objects + const newSparks: Spark[] = Array.from({ length: sparkCount }, (_, i) => ({ + x, + y, + angle: (2 * Math.PI * i) / sparkCount, + startTime: now, + })) + + sparksRef.current.push(...newSparks) + + if (animationIdRef.current === null) { + animationIdRef.current = requestAnimationFrame(draw) + } + } + + return ( +
+ + {children} +
+ ) +} diff --git a/apps/www/registry/registry-examples.ts b/apps/www/registry/registry-examples.ts index 9b88a2740..3efae8cda 100644 --- a/apps/www/registry/registry-examples.ts +++ b/apps/www/registry/registry-examples.ts @@ -1,6 +1,19 @@ import { type Registry } from "shadcn/schema" export const examples: Registry["items"] = [ + { + name: "click-spark-demo", + type: "registry:example", + title: "Click Spark Demo", + description: "Example showing a click spark component.", + registryDependencies: ["@magicui/click-spark", "button"], + files: [ + { + path: "example/click-spark-demo.tsx", + type: "registry:example", + }, + ], + }, { name: "magic-card-demo", type: "registry:example", diff --git a/apps/www/registry/registry-ui.ts b/apps/www/registry/registry-ui.ts index 873cf43e5..2c1c02960 100644 --- a/apps/www/registry/registry-ui.ts +++ b/apps/www/registry/registry-ui.ts @@ -1,6 +1,18 @@ import { type Registry } from "shadcn/schema" export const ui: Registry["items"] = [ + { + name: "click-spark", + type: "registry:ui", + title: "Click Spark", + description: "A component that renders sparks when clicked.", + files: [ + { + path: "magicui/click-spark.tsx", + type: "registry:ui", + }, + ], + }, { name: "magic-card", type: "registry:ui", From 0be61d6b90369595f0b6df723a8e2c8c2ea37379 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Sun, 3 May 2026 12:58:12 +0530 Subject: [PATCH 07/11] feat: add ClickSpark demo component to registry --- .../www/registry/example/click-spark-demo.tsx | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/apps/www/registry/example/click-spark-demo.tsx b/apps/www/registry/example/click-spark-demo.tsx index 9835b8906..9df205287 100644 --- a/apps/www/registry/example/click-spark-demo.tsx +++ b/apps/www/registry/example/click-spark-demo.tsx @@ -1,19 +1,24 @@ -import { Button } from "@/components/ui/button" -import ClickSpark from "@/registry/magicui/click-spark" +import { ClickSpark } from "@/registry/magicui/click-spark" export default function ClickSparkDemo() { return ( -
+
- +
+

+ Interactive Spark +

+

+ Click anywhere to experience the effect +

+
) From b3bf66b2d3f136874c1e598539228d1ba7a54e0f Mon Sep 17 00:00:00 2001 From: Rishabh Date: Sun, 3 May 2026 13:07:57 +0530 Subject: [PATCH 08/11] docs: add documentation for Click Spark component --- apps/www/content/docs/components/click-spark.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/www/content/docs/components/click-spark.mdx b/apps/www/content/docs/components/click-spark.mdx index 5432dd966..75666ba9c 100644 --- a/apps/www/content/docs/components/click-spark.mdx +++ b/apps/www/content/docs/components/click-spark.mdx @@ -2,7 +2,7 @@ title: Click Spark date: 2024-06-01 description: A component that renders sparks when clicked. -author: magicui +author: rishabhmishra published: true --- From 98cbbc799592add1aca5ee444cc76a57009ed706 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Sun, 3 May 2026 13:11:13 +0530 Subject: [PATCH 09/11] feat: add click-spark-demo registry example --- apps/www/public/r/click-spark-demo.json | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/www/public/r/click-spark-demo.json b/apps/www/public/r/click-spark-demo.json index 69f8dec69..4d208504e 100644 --- a/apps/www/public/r/click-spark-demo.json +++ b/apps/www/public/r/click-spark-demo.json @@ -6,7 +6,6 @@ "description": "Example showing a click spark component.", "registryDependencies": [ "@magicui/click-spark", - "button" ], "files": [ { From 505561bb65133a6c05f5fbb215a20ba138391d42 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Sun, 3 May 2026 13:12:59 +0530 Subject: [PATCH 10/11] feat: initialize registry index and configuration files with Magic UI components --- apps/www/public/r/click-spark-demo.json | 2 +- apps/www/registry.json | 3 +-- apps/www/registry/__index__.tsx | 36 ++++++++++++------------- apps/www/registry/registry-examples.ts | 2 +- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/apps/www/public/r/click-spark-demo.json b/apps/www/public/r/click-spark-demo.json index 4d208504e..e6917f873 100644 --- a/apps/www/public/r/click-spark-demo.json +++ b/apps/www/public/r/click-spark-demo.json @@ -5,7 +5,7 @@ "title": "Click Spark Demo", "description": "Example showing a click spark component.", "registryDependencies": [ - "@magicui/click-spark", + "@magicui/click-spark" ], "files": [ { diff --git a/apps/www/registry.json b/apps/www/registry.json index 2915f19ad..eaeb79f5f 100644 --- a/apps/www/registry.json +++ b/apps/www/registry.json @@ -1366,8 +1366,7 @@ "title": "Click Spark Demo", "description": "Example showing a click spark component.", "registryDependencies": [ - "@magicui/click-spark", - "button" + "@magicui/click-spark" ], "files": [ { diff --git a/apps/www/registry/__index__.tsx b/apps/www/registry/__index__.tsx index 67ed315cd..6f4c71252 100644 --- a/apps/www/registry/__index__.tsx +++ b/apps/www/registry/__index__.tsx @@ -1328,7 +1328,7 @@ export const Index: Record = { name: "click-spark-demo", description: "Example showing a click spark component.", type: "registry:example", - registryDependencies: ["@magicui/click-spark","button"], + registryDependencies: ["@magicui/click-spark"], files: [{ path: "registry/example/click-spark-demo.tsx", type: "registry:example", @@ -1362,7 +1362,7 @@ export const Index: Record = { name: "magic-card-demo-2", description: "Example showing a magic card with an orb effect.", type: "registry:example", - registryDependencies: ["@magicui/magic-card","@magicui/avatar-circles"], + registryDependencies: ["@magicui/magic-card", "@magicui/avatar-circles"], files: [{ path: "registry/example/magic-card-demo2.tsx", type: "registry:example", @@ -1515,7 +1515,7 @@ export const Index: Record = { name: "lens-demo", description: "Example showing a lens effect component", type: "registry:example", - registryDependencies: ["button","card","@magicui/lens"], + registryDependencies: ["button", "card", "@magicui/lens"], files: [{ path: "registry/example/lens-demo.tsx", type: "registry:example", @@ -1532,7 +1532,7 @@ export const Index: Record = { name: "lens-demo-2", description: "Second example showing a lens effect component", type: "registry:example", - registryDependencies: ["button","card","@magicui/lens"], + registryDependencies: ["button", "card", "@magicui/lens"], files: [{ path: "registry/example/lens-demo-2.tsx", type: "registry:example", @@ -1549,7 +1549,7 @@ export const Index: Record = { name: "lens-demo-3", description: "Third example showing a lens effect component", type: "registry:example", - registryDependencies: ["button","card","@magicui/lens"], + registryDependencies: ["button", "card", "@magicui/lens"], files: [{ path: "registry/example/lens-demo-3.tsx", type: "registry:example", @@ -1651,7 +1651,7 @@ export const Index: Record = { name: "noise-texture-demo-2", description: "Example showing a newsletter signup card with email input over a noise texture.", type: "registry:example", - registryDependencies: ["@magicui/noise-texture","button","card","input","label"], + registryDependencies: ["@magicui/noise-texture", "button", "card", "input", "label"], files: [{ path: "registry/example/noise-texture-demo-2.tsx", type: "registry:example", @@ -1668,7 +1668,7 @@ export const Index: Record = { name: "noise-texture-demo-3", description: "Example showing NoiseTexture behind a button label inside a relative button.", type: "registry:example", - registryDependencies: ["@magicui/noise-texture","button"], + registryDependencies: ["@magicui/noise-texture", "button"], files: [{ path: "registry/example/noise-texture-demo-3.tsx", type: "registry:example", @@ -1685,7 +1685,7 @@ export const Index: Record = { name: "noise-texture-demo-4", description: "Example showing a labeled input field with NoiseTexture filling the input container.", type: "registry:example", - registryDependencies: ["@magicui/noise-texture","input","label"], + registryDependencies: ["@magicui/noise-texture", "input", "label"], files: [{ path: "registry/example/noise-texture-demo-4.tsx", type: "registry:example", @@ -2161,7 +2161,7 @@ export const Index: Record = { name: "tweet-card-demo", description: "Example showing a tweet card with author info.", type: "registry:example", - registryDependencies: ["@magicui/client-tweet-card","@magicui/tweet-card"], + registryDependencies: ["@magicui/client-tweet-card", "@magicui/tweet-card"], files: [{ path: "registry/example/tweet-card-demo.tsx", type: "registry:example", @@ -2178,7 +2178,7 @@ export const Index: Record = { name: "tweet-card-images", description: "Example showing a tweet card with images.", type: "registry:example", - registryDependencies: ["@magicui/client-tweet-card","@magicui/tweet-card"], + registryDependencies: ["@magicui/client-tweet-card", "@magicui/tweet-card"], files: [{ path: "registry/example/tweet-card-images.tsx", type: "registry:example", @@ -2195,7 +2195,7 @@ export const Index: Record = { name: "tweet-card-meta-preview", description: "Example showing a tweet card with meta preview.", type: "registry:example", - registryDependencies: ["@magicui/client-tweet-card","@magicui/tweet-card"], + registryDependencies: ["@magicui/client-tweet-card", "@magicui/tweet-card"], files: [{ path: "registry/example/tweet-card-meta-preview.tsx", type: "registry:example", @@ -2229,7 +2229,7 @@ export const Index: Record = { name: "bento-demo", description: "Example showing a bento grid layout for showcasing features.", type: "registry:example", - registryDependencies: ["button","calendar","@magicui/marquee","@magicui/bento-grid","@magicui/animated-beam","@magicui/animated-list","@magicui/animated-beam-multiple-outputs","@magicui/animated-list-demo"], + registryDependencies: ["button", "calendar", "@magicui/marquee", "@magicui/bento-grid", "@magicui/animated-beam", "@magicui/animated-list", "@magicui/animated-beam-multiple-outputs", "@magicui/animated-list-demo"], files: [{ path: "registry/example/bento-demo.tsx", type: "registry:example", @@ -2467,7 +2467,7 @@ export const Index: Record = { name: "border-beam-demo-2", description: "Example showing an animated border beam effect.", type: "registry:example", - registryDependencies: ["button","card","@magicui/border-beam"], + registryDependencies: ["button", "card", "@magicui/border-beam"], files: [{ path: "registry/example/border-beam-demo-2.tsx", type: "registry:example", @@ -2484,7 +2484,7 @@ export const Index: Record = { name: "border-beam-demo-3", description: "Example showing an animated border beam effect.", type: "registry:example", - registryDependencies: ["button","card","input","label","@magicui/border-beam"], + registryDependencies: ["button", "card", "input", "label", "@magicui/border-beam"], files: [{ path: "registry/example/border-beam-demo-3.tsx", type: "registry:example", @@ -2501,7 +2501,7 @@ export const Index: Record = { name: "border-beam-demo-4", description: "Example showing an animated border beam effect.", type: "registry:example", - registryDependencies: ["button","@magicui/border-beam"], + registryDependencies: ["button", "@magicui/border-beam"], files: [{ path: "registry/example/border-beam-demo-4.tsx", type: "registry:example", @@ -2739,7 +2739,7 @@ export const Index: Record = { name: "dock-demo", description: "Example showing a MacOS-style dock implementation.", type: "registry:example", - registryDependencies: ["button","@magicui/dock"], + registryDependencies: ["button", "@magicui/dock"], files: [{ path: "registry/example/dock-demo.tsx", type: "registry:example", @@ -3866,7 +3866,7 @@ export const Index: Record = { path: "registry/example/highlighter-demo.tsx", type: "registry:example", target: "" - },{ + }, { path: "registry/magicui/highlighter.tsx", type: "registry:ui", target: "" @@ -4184,4 +4184,4 @@ export const Index: Record = { }), meta: undefined, }, - } \ No newline at end of file +} \ No newline at end of file diff --git a/apps/www/registry/registry-examples.ts b/apps/www/registry/registry-examples.ts index 3efae8cda..2287371c3 100644 --- a/apps/www/registry/registry-examples.ts +++ b/apps/www/registry/registry-examples.ts @@ -6,7 +6,7 @@ export const examples: Registry["items"] = [ type: "registry:example", title: "Click Spark Demo", description: "Example showing a click spark component.", - registryDependencies: ["@magicui/click-spark", "button"], + registryDependencies: ["@magicui/click-spark"], files: [ { path: "example/click-spark-demo.tsx", From 361e3827bb7afefac64b4df9fa8d4e96dce38789 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Sun, 17 May 2026 20:17:38 +0530 Subject: [PATCH 11/11] chore: regenerate registry build artifacts and sync MDX docs date for ClickSpark --- .../content/docs/components/click-spark.mdx | 2 +- apps/www/public/llms-full.txt | 314 +++++++++++------- apps/www/public/r/click-spark-demo.json | 2 +- apps/www/public/r/click-spark.json | 2 +- apps/www/public/r/registry.json | 3 +- apps/www/public/registry.json | 3 +- apps/www/registry/__index__.tsx | 34 +- 7 files changed, 222 insertions(+), 138 deletions(-) diff --git a/apps/www/content/docs/components/click-spark.mdx b/apps/www/content/docs/components/click-spark.mdx index 75666ba9c..4a1af615a 100644 --- a/apps/www/content/docs/components/click-spark.mdx +++ b/apps/www/content/docs/components/click-spark.mdx @@ -1,6 +1,6 @@ --- title: Click Spark -date: 2024-06-01 +date: 2026-05-17 description: A component that renders sparks when clicked. author: rishabhmishra published: true diff --git a/apps/www/public/llms-full.txt b/apps/www/public/llms-full.txt index a66aa12ee..68c0ef312 100644 --- a/apps/www/public/llms-full.txt +++ b/apps/www/public/llms-full.txt @@ -4666,192 +4666,278 @@ Title: Click Spark Description: A component that renders sparks when clicked. --- file: magicui/click-spark.tsx --- -import React, { useRef, useEffect, useCallback } from 'react'; +"use client" -interface ClickSparkProps { - sparkColor?: string; - sparkSize?: number; - sparkRadius?: number; - sparkCount?: number; - duration?: number; - easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out'; - extraScale?: number; - children?: React.ReactNode; -} +import React, { useCallback, useEffect, useRef } from "react" + +import { cn } from "@/lib/utils" interface Spark { - x: number; - y: number; - angle: number; - startTime: number; + x: number + y: number + angle: number + startTime: number } -const ClickSpark: React.FC = ({ - sparkColor = '#fff', +interface ClickSparkProps { + /** + * The color of the sparks. + * @default "#fff" + */ + sparkColor?: string + /** + * The size of the sparks. + * @default 10 + */ + sparkSize?: number + /** + * The radius of the spark explosion. + * @default 15 + */ + sparkRadius?: number + /** + * The number of sparks per click. + * @default 8 + */ + sparkCount?: number + /** + * The duration of the spark animation in milliseconds. + * @default 400 + */ + duration?: number + /** + * The easing function for the spark animation. + * @default "ease-out" + */ + easing?: "linear" | "ease-in" | "ease-out" | "ease-in-out" + /** + * Extra scale factor for the spark distance. + * @default 1.0 + */ + extraScale?: number + /** + * The content to wrap. + */ + children?: React.ReactNode + /** + * Additional class names for the wrapper. + */ + className?: string +} + +export function ClickSpark({ + sparkColor = "#fff", sparkSize = 10, sparkRadius = 15, sparkCount = 8, duration = 400, - easing = 'ease-out', + easing = "ease-out", extraScale = 1.0, - children -}) => { - const canvasRef = useRef(null); - const sparksRef = useRef([]); - const startTimeRef = useRef(null); + children, + className, +}: ClickSparkProps) { + const canvasRef = useRef(null) + const sparksRef = useRef([]) + const animationIdRef = useRef(null) + + // Store animation settings in a ref to keep the draw loop stable + // and avoid exhaustive-deps warnings. + const settingsRef = useRef({ + sparkColor, + sparkSize, + sparkRadius, + duration, + extraScale, + }) + // Sync refs when props change useEffect(() => { - const canvas = canvasRef.current; - if (!canvas) return; + settingsRef.current = { + sparkColor, + sparkSize, + sparkRadius, + duration, + extraScale, + } + }, [sparkColor, sparkSize, sparkRadius, duration, extraScale]) - const parent = canvas.parentElement; - if (!parent) return; + useEffect(() => { + const canvas = canvasRef.current + if (!canvas) return + + const parent = canvas.parentElement + if (!parent) return - let resizeTimeout: ReturnType; + let resizeTimeout: ReturnType const resizeCanvas = () => { - const { width, height } = parent.getBoundingClientRect(); + const { width, height } = parent.getBoundingClientRect() if (canvas.width !== width || canvas.height !== height) { - canvas.width = width; - canvas.height = height; + canvas.width = width + canvas.height = height } - }; + } const handleResize = () => { - clearTimeout(resizeTimeout); - resizeTimeout = setTimeout(resizeCanvas, 100); - }; + clearTimeout(resizeTimeout) + resizeTimeout = setTimeout(resizeCanvas, 100) + } - const ro = new ResizeObserver(handleResize); - ro.observe(parent); + const ro = new ResizeObserver(handleResize) + ro.observe(parent) - resizeCanvas(); + resizeCanvas() return () => { - ro.disconnect(); - clearTimeout(resizeTimeout); - }; - }, []); + ro.disconnect() + clearTimeout(resizeTimeout) + } + }, []) const easeFunc = useCallback( (t: number) => { switch (easing) { - case 'linear': - return t; - case 'ease-in': - return t * t; - case 'ease-in-out': - return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; + case "linear": + return t + case "ease-in": + return t * t + case "ease-in-out": + return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t default: - return t * (2 - t); + return t * (2 - t) } }, [easing] - ); + ) useEffect(() => { - const canvas = canvasRef.current; - if (!canvas) return; - const ctx = canvas.getContext('2d'); - if (!ctx) return; + return () => { + if (animationIdRef.current) { + cancelAnimationFrame(animationIdRef.current) + } + } + }, []) - let animationId: number; + const draw = useCallback( + (timestamp: number) => { + const canvas = canvasRef.current + if (!canvas) return + const ctx = canvas.getContext("2d") + if (!ctx) return - const draw = (timestamp: number) => { - if (!startTimeRef.current) { - startTimeRef.current = timestamp; - } - ctx?.clearRect(0, 0, canvas.width, canvas.height); + ctx.clearRect(0, 0, canvas.width, canvas.height) + + // Use settings from Ref to avoid dependency loop + const { + sparkColor: color, + sparkSize: size, + sparkRadius: radius, + duration: drn, + extraScale: scale, + } = settingsRef.current sparksRef.current = sparksRef.current.filter((spark: Spark) => { - const elapsed = timestamp - spark.startTime; - if (elapsed >= duration) { - return false; + const elapsed = timestamp - spark.startTime + if (elapsed >= drn) { + return false } - const progress = elapsed / duration; - const eased = easeFunc(progress); - - const distance = eased * sparkRadius * extraScale; - const lineLength = sparkSize * (1 - eased); + const progress = elapsed / drn + const eased = easeFunc(progress) - const x1 = spark.x + distance * Math.cos(spark.angle); - const y1 = spark.y + distance * Math.sin(spark.angle); - const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle); - const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle); + const distance = eased * radius * scale + const lineLength = size * (1 - eased) - ctx.strokeStyle = sparkColor; - ctx.lineWidth = 2; - ctx.beginPath(); - ctx.moveTo(x1, y1); - ctx.lineTo(x2, y2); - ctx.stroke(); + const x1 = spark.x + distance * Math.cos(spark.angle) + const y1 = spark.y + distance * Math.sin(spark.angle) + const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle) + const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle) - return true; - }); + ctx.strokeStyle = color + ctx.lineWidth = 2 + ctx.beginPath() + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + ctx.stroke() - animationId = requestAnimationFrame(draw); - }; - - animationId = requestAnimationFrame(draw); + return true + }) - return () => { - cancelAnimationFrame(animationId); - }; - }, [sparkColor, sparkSize, sparkRadius, sparkCount, duration, easeFunc, extraScale]); + if (sparksRef.current.length > 0) { + animationIdRef.current = requestAnimationFrame(draw) + } else { + animationIdRef.current = null + } + }, + [easeFunc] + ) const handleClick = (e: React.MouseEvent): void => { - const canvas = canvasRef.current; - if (!canvas) return; - const rect = canvas.getBoundingClientRect(); - const x = e.clientX - rect.left; - const y = e.clientY - rect.top; + const canvas = canvasRef.current + if (!canvas) return + const rect = canvas.getBoundingClientRect() + const x = e.clientX - rect.left + const y = e.clientY - rect.top - const now = performance.now(); + const now = performance.now() + // sparkCount is only used here to initialize the objects const newSparks: Spark[] = Array.from({ length: sparkCount }, (_, i) => ({ x, y, angle: (2 * Math.PI * i) / sparkCount, - startTime: now - })); + startTime: now, + })) - sparksRef.current.push(...newSparks); - }; + sparksRef.current.push(...newSparks) + + if (animationIdRef.current === null) { + animationIdRef.current = requestAnimationFrame(draw) + } + } return ( -
- +
+ {children}
- ); -}; - -export default ClickSpark; + ) +} ===== EXAMPLE: click-spark-demo ===== Title: Click Spark Demo --- file: example/click-spark-demo.tsx --- -import ClickSpark from "@/registry/magicui/click-spark"; -import { Button } from "@/components/ui/button"; +import { ClickSpark } from "@/registry/magicui/click-spark" export default function ClickSparkDemo() { return ( -
+
- +
+

+ Interactive Spark +

+

+ Click anywhere to experience the effect +

+
- ); + ) } diff --git a/apps/www/public/r/click-spark-demo.json b/apps/www/public/r/click-spark-demo.json index e6917f873..47d3307ae 100644 --- a/apps/www/public/r/click-spark-demo.json +++ b/apps/www/public/r/click-spark-demo.json @@ -10,7 +10,7 @@ "files": [ { "path": "registry/example/click-spark-demo.tsx", - "content": "import ClickSpark from \"@/registry/magicui/click-spark\";\nimport { Button } from \"@/components/ui/button\";\n\nexport default function ClickSparkDemo() {\n return (\n
\n \n \n \n
\n );\n}\n", + "content": "import { ClickSpark } from \"@/registry/magicui/click-spark\"\n\nexport default function ClickSparkDemo() {\n return (\n
\n \n
\n

\n Interactive Spark\n

\n

\n Click anywhere to experience the effect\n

\n
\n \n
\n )\n}\n", "type": "registry:example" } ] diff --git a/apps/www/public/r/click-spark.json b/apps/www/public/r/click-spark.json index 0796cda47..3ef0c02d5 100644 --- a/apps/www/public/r/click-spark.json +++ b/apps/www/public/r/click-spark.json @@ -7,7 +7,7 @@ "files": [ { "path": "registry/magicui/click-spark.tsx", - "content": "import React, { useRef, useEffect, useCallback } from 'react';\n\ninterface ClickSparkProps {\n sparkColor?: string;\n sparkSize?: number;\n sparkRadius?: number;\n sparkCount?: number;\n duration?: number;\n easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';\n extraScale?: number;\n children?: React.ReactNode;\n}\n\ninterface Spark {\n x: number;\n y: number;\n angle: number;\n startTime: number;\n}\n\nconst ClickSpark: React.FC = ({\n sparkColor = '#fff',\n sparkSize = 10,\n sparkRadius = 15,\n sparkCount = 8,\n duration = 400,\n easing = 'ease-out',\n extraScale = 1.0,\n children\n}) => {\n const canvasRef = useRef(null);\n const sparksRef = useRef([]);\n const startTimeRef = useRef(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const parent = canvas.parentElement;\n if (!parent) return;\n\n let resizeTimeout: ReturnType;\n\n const resizeCanvas = () => {\n const { width, height } = parent.getBoundingClientRect();\n if (canvas.width !== width || canvas.height !== height) {\n canvas.width = width;\n canvas.height = height;\n }\n };\n\n const handleResize = () => {\n clearTimeout(resizeTimeout);\n resizeTimeout = setTimeout(resizeCanvas, 100);\n };\n\n const ro = new ResizeObserver(handleResize);\n ro.observe(parent);\n\n resizeCanvas();\n\n return () => {\n ro.disconnect();\n clearTimeout(resizeTimeout);\n };\n }, []);\n\n const easeFunc = useCallback(\n (t: number) => {\n switch (easing) {\n case 'linear':\n return t;\n case 'ease-in':\n return t * t;\n case 'ease-in-out':\n return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;\n default:\n return t * (2 - t);\n }\n },\n [easing]\n );\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n let animationId: number;\n\n const draw = (timestamp: number) => {\n if (!startTimeRef.current) {\n startTimeRef.current = timestamp;\n }\n ctx?.clearRect(0, 0, canvas.width, canvas.height);\n\n sparksRef.current = sparksRef.current.filter((spark: Spark) => {\n const elapsed = timestamp - spark.startTime;\n if (elapsed >= duration) {\n return false;\n }\n\n const progress = elapsed / duration;\n const eased = easeFunc(progress);\n\n const distance = eased * sparkRadius * extraScale;\n const lineLength = sparkSize * (1 - eased);\n\n const x1 = spark.x + distance * Math.cos(spark.angle);\n const y1 = spark.y + distance * Math.sin(spark.angle);\n const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle);\n const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle);\n\n ctx.strokeStyle = sparkColor;\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.moveTo(x1, y1);\n ctx.lineTo(x2, y2);\n ctx.stroke();\n\n return true;\n });\n\n animationId = requestAnimationFrame(draw);\n };\n\n animationId = requestAnimationFrame(draw);\n\n return () => {\n cancelAnimationFrame(animationId);\n };\n }, [sparkColor, sparkSize, sparkRadius, sparkCount, duration, easeFunc, extraScale]);\n\n const handleClick = (e: React.MouseEvent): void => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const rect = canvas.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n\n const now = performance.now();\n const newSparks: Spark[] = Array.from({ length: sparkCount }, (_, i) => ({\n x,\n y,\n angle: (2 * Math.PI * i) / sparkCount,\n startTime: now\n }));\n\n sparksRef.current.push(...newSparks);\n };\n\n return (\n
\n \n {children}\n
\n );\n};\n\nexport default ClickSpark;\n", + "content": "\"use client\"\n\nimport React, { useCallback, useEffect, useRef } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface Spark {\n x: number\n y: number\n angle: number\n startTime: number\n}\n\ninterface ClickSparkProps {\n /**\n * The color of the sparks.\n * @default \"#fff\"\n */\n sparkColor?: string\n /**\n * The size of the sparks.\n * @default 10\n */\n sparkSize?: number\n /**\n * The radius of the spark explosion.\n * @default 15\n */\n sparkRadius?: number\n /**\n * The number of sparks per click.\n * @default 8\n */\n sparkCount?: number\n /**\n * The duration of the spark animation in milliseconds.\n * @default 400\n */\n duration?: number\n /**\n * The easing function for the spark animation.\n * @default \"ease-out\"\n */\n easing?: \"linear\" | \"ease-in\" | \"ease-out\" | \"ease-in-out\"\n /**\n * Extra scale factor for the spark distance.\n * @default 1.0\n */\n extraScale?: number\n /**\n * The content to wrap.\n */\n children?: React.ReactNode\n /**\n * Additional class names for the wrapper.\n */\n className?: string\n}\n\nexport function ClickSpark({\n sparkColor = \"#fff\",\n sparkSize = 10,\n sparkRadius = 15,\n sparkCount = 8,\n duration = 400,\n easing = \"ease-out\",\n extraScale = 1.0,\n children,\n className,\n}: ClickSparkProps) {\n const canvasRef = useRef(null)\n const sparksRef = useRef([])\n const animationIdRef = useRef(null)\n\n // Store animation settings in a ref to keep the draw loop stable\n // and avoid exhaustive-deps warnings.\n const settingsRef = useRef({\n sparkColor,\n sparkSize,\n sparkRadius,\n duration,\n extraScale,\n })\n\n // Sync refs when props change\n useEffect(() => {\n settingsRef.current = {\n sparkColor,\n sparkSize,\n sparkRadius,\n duration,\n extraScale,\n }\n }, [sparkColor, sparkSize, sparkRadius, duration, extraScale])\n\n useEffect(() => {\n const canvas = canvasRef.current\n if (!canvas) return\n\n const parent = canvas.parentElement\n if (!parent) return\n\n let resizeTimeout: ReturnType\n\n const resizeCanvas = () => {\n const { width, height } = parent.getBoundingClientRect()\n if (canvas.width !== width || canvas.height !== height) {\n canvas.width = width\n canvas.height = height\n }\n }\n\n const handleResize = () => {\n clearTimeout(resizeTimeout)\n resizeTimeout = setTimeout(resizeCanvas, 100)\n }\n\n const ro = new ResizeObserver(handleResize)\n ro.observe(parent)\n\n resizeCanvas()\n\n return () => {\n ro.disconnect()\n clearTimeout(resizeTimeout)\n }\n }, [])\n\n const easeFunc = useCallback(\n (t: number) => {\n switch (easing) {\n case \"linear\":\n return t\n case \"ease-in\":\n return t * t\n case \"ease-in-out\":\n return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t\n default:\n return t * (2 - t)\n }\n },\n [easing]\n )\n\n useEffect(() => {\n return () => {\n if (animationIdRef.current) {\n cancelAnimationFrame(animationIdRef.current)\n }\n }\n }, [])\n\n const draw = useCallback(\n (timestamp: number) => {\n const canvas = canvasRef.current\n if (!canvas) return\n const ctx = canvas.getContext(\"2d\")\n if (!ctx) return\n\n ctx.clearRect(0, 0, canvas.width, canvas.height)\n\n // Use settings from Ref to avoid dependency loop\n const {\n sparkColor: color,\n sparkSize: size,\n sparkRadius: radius,\n duration: drn,\n extraScale: scale,\n } = settingsRef.current\n\n sparksRef.current = sparksRef.current.filter((spark: Spark) => {\n const elapsed = timestamp - spark.startTime\n if (elapsed >= drn) {\n return false\n }\n\n const progress = elapsed / drn\n const eased = easeFunc(progress)\n\n const distance = eased * radius * scale\n const lineLength = size * (1 - eased)\n\n const x1 = spark.x + distance * Math.cos(spark.angle)\n const y1 = spark.y + distance * Math.sin(spark.angle)\n const x2 = spark.x + (distance + lineLength) * Math.cos(spark.angle)\n const y2 = spark.y + (distance + lineLength) * Math.sin(spark.angle)\n\n ctx.strokeStyle = color\n ctx.lineWidth = 2\n ctx.beginPath()\n ctx.moveTo(x1, y1)\n ctx.lineTo(x2, y2)\n ctx.stroke()\n\n return true\n })\n\n if (sparksRef.current.length > 0) {\n animationIdRef.current = requestAnimationFrame(draw)\n } else {\n animationIdRef.current = null\n }\n },\n [easeFunc]\n )\n\n const handleClick = (e: React.MouseEvent): void => {\n const canvas = canvasRef.current\n if (!canvas) return\n const rect = canvas.getBoundingClientRect()\n const x = e.clientX - rect.left\n const y = e.clientY - rect.top\n\n const now = performance.now()\n // sparkCount is only used here to initialize the objects\n const newSparks: Spark[] = Array.from({ length: sparkCount }, (_, i) => ({\n x,\n y,\n angle: (2 * Math.PI * i) / sparkCount,\n startTime: now,\n }))\n\n sparksRef.current.push(...newSparks)\n\n if (animationIdRef.current === null) {\n animationIdRef.current = requestAnimationFrame(draw)\n }\n }\n\n return (\n \n \n {children}\n
\n )\n}\n", "type": "registry:ui" } ] diff --git a/apps/www/public/r/registry.json b/apps/www/public/r/registry.json index 2915f19ad..eaeb79f5f 100644 --- a/apps/www/public/r/registry.json +++ b/apps/www/public/r/registry.json @@ -1366,8 +1366,7 @@ "title": "Click Spark Demo", "description": "Example showing a click spark component.", "registryDependencies": [ - "@magicui/click-spark", - "button" + "@magicui/click-spark" ], "files": [ { diff --git a/apps/www/public/registry.json b/apps/www/public/registry.json index 2915f19ad..eaeb79f5f 100644 --- a/apps/www/public/registry.json +++ b/apps/www/public/registry.json @@ -1366,8 +1366,7 @@ "title": "Click Spark Demo", "description": "Example showing a click spark component.", "registryDependencies": [ - "@magicui/click-spark", - "button" + "@magicui/click-spark" ], "files": [ { diff --git a/apps/www/registry/__index__.tsx b/apps/www/registry/__index__.tsx index 6f4c71252..605cd7b8f 100644 --- a/apps/www/registry/__index__.tsx +++ b/apps/www/registry/__index__.tsx @@ -1362,7 +1362,7 @@ export const Index: Record = { name: "magic-card-demo-2", description: "Example showing a magic card with an orb effect.", type: "registry:example", - registryDependencies: ["@magicui/magic-card", "@magicui/avatar-circles"], + registryDependencies: ["@magicui/magic-card","@magicui/avatar-circles"], files: [{ path: "registry/example/magic-card-demo2.tsx", type: "registry:example", @@ -1515,7 +1515,7 @@ export const Index: Record = { name: "lens-demo", description: "Example showing a lens effect component", type: "registry:example", - registryDependencies: ["button", "card", "@magicui/lens"], + registryDependencies: ["button","card","@magicui/lens"], files: [{ path: "registry/example/lens-demo.tsx", type: "registry:example", @@ -1532,7 +1532,7 @@ export const Index: Record = { name: "lens-demo-2", description: "Second example showing a lens effect component", type: "registry:example", - registryDependencies: ["button", "card", "@magicui/lens"], + registryDependencies: ["button","card","@magicui/lens"], files: [{ path: "registry/example/lens-demo-2.tsx", type: "registry:example", @@ -1549,7 +1549,7 @@ export const Index: Record = { name: "lens-demo-3", description: "Third example showing a lens effect component", type: "registry:example", - registryDependencies: ["button", "card", "@magicui/lens"], + registryDependencies: ["button","card","@magicui/lens"], files: [{ path: "registry/example/lens-demo-3.tsx", type: "registry:example", @@ -1651,7 +1651,7 @@ export const Index: Record = { name: "noise-texture-demo-2", description: "Example showing a newsletter signup card with email input over a noise texture.", type: "registry:example", - registryDependencies: ["@magicui/noise-texture", "button", "card", "input", "label"], + registryDependencies: ["@magicui/noise-texture","button","card","input","label"], files: [{ path: "registry/example/noise-texture-demo-2.tsx", type: "registry:example", @@ -1668,7 +1668,7 @@ export const Index: Record = { name: "noise-texture-demo-3", description: "Example showing NoiseTexture behind a button label inside a relative button.", type: "registry:example", - registryDependencies: ["@magicui/noise-texture", "button"], + registryDependencies: ["@magicui/noise-texture","button"], files: [{ path: "registry/example/noise-texture-demo-3.tsx", type: "registry:example", @@ -1685,7 +1685,7 @@ export const Index: Record = { name: "noise-texture-demo-4", description: "Example showing a labeled input field with NoiseTexture filling the input container.", type: "registry:example", - registryDependencies: ["@magicui/noise-texture", "input", "label"], + registryDependencies: ["@magicui/noise-texture","input","label"], files: [{ path: "registry/example/noise-texture-demo-4.tsx", type: "registry:example", @@ -2161,7 +2161,7 @@ export const Index: Record = { name: "tweet-card-demo", description: "Example showing a tweet card with author info.", type: "registry:example", - registryDependencies: ["@magicui/client-tweet-card", "@magicui/tweet-card"], + registryDependencies: ["@magicui/client-tweet-card","@magicui/tweet-card"], files: [{ path: "registry/example/tweet-card-demo.tsx", type: "registry:example", @@ -2178,7 +2178,7 @@ export const Index: Record = { name: "tweet-card-images", description: "Example showing a tweet card with images.", type: "registry:example", - registryDependencies: ["@magicui/client-tweet-card", "@magicui/tweet-card"], + registryDependencies: ["@magicui/client-tweet-card","@magicui/tweet-card"], files: [{ path: "registry/example/tweet-card-images.tsx", type: "registry:example", @@ -2195,7 +2195,7 @@ export const Index: Record = { name: "tweet-card-meta-preview", description: "Example showing a tweet card with meta preview.", type: "registry:example", - registryDependencies: ["@magicui/client-tweet-card", "@magicui/tweet-card"], + registryDependencies: ["@magicui/client-tweet-card","@magicui/tweet-card"], files: [{ path: "registry/example/tweet-card-meta-preview.tsx", type: "registry:example", @@ -2229,7 +2229,7 @@ export const Index: Record = { name: "bento-demo", description: "Example showing a bento grid layout for showcasing features.", type: "registry:example", - registryDependencies: ["button", "calendar", "@magicui/marquee", "@magicui/bento-grid", "@magicui/animated-beam", "@magicui/animated-list", "@magicui/animated-beam-multiple-outputs", "@magicui/animated-list-demo"], + registryDependencies: ["button","calendar","@magicui/marquee","@magicui/bento-grid","@magicui/animated-beam","@magicui/animated-list","@magicui/animated-beam-multiple-outputs","@magicui/animated-list-demo"], files: [{ path: "registry/example/bento-demo.tsx", type: "registry:example", @@ -2467,7 +2467,7 @@ export const Index: Record = { name: "border-beam-demo-2", description: "Example showing an animated border beam effect.", type: "registry:example", - registryDependencies: ["button", "card", "@magicui/border-beam"], + registryDependencies: ["button","card","@magicui/border-beam"], files: [{ path: "registry/example/border-beam-demo-2.tsx", type: "registry:example", @@ -2484,7 +2484,7 @@ export const Index: Record = { name: "border-beam-demo-3", description: "Example showing an animated border beam effect.", type: "registry:example", - registryDependencies: ["button", "card", "input", "label", "@magicui/border-beam"], + registryDependencies: ["button","card","input","label","@magicui/border-beam"], files: [{ path: "registry/example/border-beam-demo-3.tsx", type: "registry:example", @@ -2501,7 +2501,7 @@ export const Index: Record = { name: "border-beam-demo-4", description: "Example showing an animated border beam effect.", type: "registry:example", - registryDependencies: ["button", "@magicui/border-beam"], + registryDependencies: ["button","@magicui/border-beam"], files: [{ path: "registry/example/border-beam-demo-4.tsx", type: "registry:example", @@ -2739,7 +2739,7 @@ export const Index: Record = { name: "dock-demo", description: "Example showing a MacOS-style dock implementation.", type: "registry:example", - registryDependencies: ["button", "@magicui/dock"], + registryDependencies: ["button","@magicui/dock"], files: [{ path: "registry/example/dock-demo.tsx", type: "registry:example", @@ -3866,7 +3866,7 @@ export const Index: Record = { path: "registry/example/highlighter-demo.tsx", type: "registry:example", target: "" - }, { + },{ path: "registry/magicui/highlighter.tsx", type: "registry:ui", target: "" @@ -4184,4 +4184,4 @@ export const Index: Record = { }), meta: undefined, }, -} \ No newline at end of file + } \ No newline at end of file