diff --git a/public/r/animated-list.json b/public/r/animated-list.json index 20ac1e1..64e3a47 100644 --- a/public/r/animated-list.json +++ b/public/r/animated-list.json @@ -13,17 +13,17 @@ "files": [ { "path": "./src/components/nurui/animated-list-demo.tsx", - "content": "import { cn } from \"@/lib/utils\";\nimport { Notification } from \"@/components/nurui/notification\";\nimport { AnimatedList } from \"@/components/nurui/animated-list-items\";\n\nlet notifications = [\n {\n name: \"New follower\",\n description: \"Alex started following you\",\n time: \"2m ago\",\n icon: \"๐Ÿ‘ฅ\",\n color: \"#6C63FF\",\n },\n {\n name: \"Comment received\",\n description: \"John commented on your post\",\n time: \"5m ago\",\n icon: \"๐Ÿ’ฌ\",\n color: \"#FF6584\",\n },\n {\n name: \"File uploaded\",\n description: \"You uploaded 'design.png'\",\n time: \"8m ago\",\n icon: \"๐Ÿ“\",\n color: \"#4CAF50\",\n },\n {\n name: \"Payment processed\",\n description: \"$120 sent to your account\",\n time: \"12m ago\",\n icon: \"๐Ÿ’ณ\",\n color: \"#00BFA6\",\n },\n {\n name: \"New like\",\n description: \"Sophia liked your photo\",\n time: \"18m ago\",\n icon: \"โค๏ธ\",\n color: \"#FF1744\",\n },\n {\n name: \"System alert\",\n description: \"Server downtime detected\",\n time: \"25m ago\",\n icon: \"โš ๏ธ\",\n color: \"#FFC107\",\n },\n {\n name: \"Message received\",\n description: \"Daniel sent you a message\",\n time: \"45m ago\",\n icon: \"๐Ÿ“จ\",\n color: \"#2196F3\",\n },\n {\n name: \"Password updated\",\n description: \"Your account password has been changed\",\n time: \"1h ago\",\n icon: \"๐Ÿ”‘\",\n color: \"#9C27B0\",\n },\n];\nnotifications = Array.from({ length: 10 }, () => notifications).flat();\n\nexport function AnimatedListDemo({ className }: { className?: string }) {\n return (\n \n \n {notifications.map((item, idx) => (\n \n ))}\n \n\n
\n \n );\n}\n", + "content": "import { cn } from \"@/lib/utils\";\r\nimport { Notification } from \"@/components/nurui/notification\";\r\nimport { AnimatedList } from \"@/components/nurui/animated-list-items\";\r\n\r\nlet notifications = [\r\n {\r\n name: \"New follower\",\r\n description: \"Alex started following you\",\r\n time: \"2m ago\",\r\n icon: \"๐Ÿ‘ฅ\",\r\n color: \"#6C63FF\",\r\n },\r\n {\r\n name: \"Comment received\",\r\n description: \"John commented on your post\",\r\n time: \"5m ago\",\r\n icon: \"๐Ÿ’ฌ\",\r\n color: \"#FF6584\",\r\n },\r\n {\r\n name: \"File uploaded\",\r\n description: \"You uploaded 'design.png'\",\r\n time: \"8m ago\",\r\n icon: \"๐Ÿ“\",\r\n color: \"#4CAF50\",\r\n },\r\n {\r\n name: \"Payment processed\",\r\n description: \"$120 sent to your account\",\r\n time: \"12m ago\",\r\n icon: \"๐Ÿ’ณ\",\r\n color: \"#00BFA6\",\r\n },\r\n {\r\n name: \"New like\",\r\n description: \"Sophia liked your photo\",\r\n time: \"18m ago\",\r\n icon: \"โค๏ธ\",\r\n color: \"#FF1744\",\r\n },\r\n {\r\n name: \"System alert\",\r\n description: \"Server downtime detected\",\r\n time: \"25m ago\",\r\n icon: \"โš ๏ธ\",\r\n color: \"#FFC107\",\r\n },\r\n {\r\n name: \"Message received\",\r\n description: \"Daniel sent you a message\",\r\n time: \"45m ago\",\r\n icon: \"๐Ÿ“จ\",\r\n color: \"#2196F3\",\r\n },\r\n {\r\n name: \"Password updated\",\r\n description: \"Your account password has been changed\",\r\n time: \"1h ago\",\r\n icon: \"๐Ÿ”‘\",\r\n color: \"#9C27B0\",\r\n },\r\n];\r\nnotifications = Array.from({ length: 10 }, () => notifications).flat();\r\n\r\nexport function AnimatedListDemo({ className }: { className?: string }) {\r\n return (\r\n \r\n \r\n {notifications.map((item, idx) => (\r\n \r\n ))}\r\n \r\n\r\n
\r\n \r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/animated-list-items.tsx", - "content": "\"use client\";\nimport { cn } from \"@/lib/utils\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport React, {\n ComponentPropsWithoutRef,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\n\nexport function AnimatedListItem({ children }: { children: React.ReactNode }) {\n const initial = { scale: 0, opacity: 0 };\n const animate = { scale: 1, opacity: 1, originY: 0 };\n const exit = { scale: 0, opacity: 0 };\n const transition = { type: \"spring\" as const, stiffness: 350, damping: 40 };\n\n return (\n \n {children}\n \n );\n}\n\nexport interface AnimatedListProps extends ComponentPropsWithoutRef<\"div\"> {\n children: React.ReactNode;\n delay?: number;\n}\n\nexport const AnimatedList = React.memo(\n ({ children, className, delay = 1000, ...props }: AnimatedListProps) => {\n const [index, setIndex] = useState(0);\n const childrenArray = useMemo(\n () => React.Children.toArray(children),\n [children],\n );\n\n useEffect(() => {\n if (index < childrenArray.length - 1) {\n const timeout = setTimeout(() => {\n setIndex((prevIndex) => (prevIndex + 1) % childrenArray.length);\n }, delay);\n\n return () => clearTimeout(timeout);\n }\n }, [index, delay, childrenArray.length]);\n\n const itemsToShow = useMemo(() => {\n const result = childrenArray.slice(0, index + 1).reverse();\n return result;\n }, [index, childrenArray]);\n\n return (\n \n \n {itemsToShow.map((item) => (\n \n {item}\n \n ))}\n \n \n );\n },\n);\n\nAnimatedList.displayName = \"AnimatedList\";\n", + "content": "\"use client\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { AnimatePresence, motion } from \"motion/react\";\r\nimport React, {\r\n ComponentPropsWithoutRef,\r\n useEffect,\r\n useMemo,\r\n useState,\r\n} from \"react\";\r\n\r\nexport function AnimatedListItem({ children }: { children: React.ReactNode }) {\r\n const initial = { scale: 0, opacity: 0 };\r\n const animate = { scale: 1, opacity: 1, originY: 0 };\r\n const exit = { scale: 0, opacity: 0 };\r\n const transition = { type: \"spring\" as const, stiffness: 350, damping: 40 };\r\n\r\n return (\r\n \r\n {children}\r\n \r\n );\r\n}\r\n\r\nexport interface AnimatedListProps extends ComponentPropsWithoutRef<\"div\"> {\r\n children: React.ReactNode;\r\n delay?: number;\r\n}\r\n\r\nexport const AnimatedList = React.memo(\r\n ({ children, className, delay = 1000, ...props }: AnimatedListProps) => {\r\n const [index, setIndex] = useState(0);\r\n const childrenArray = useMemo(\r\n () => React.Children.toArray(children),\r\n [children],\r\n );\r\n\r\n useEffect(() => {\r\n if (index < childrenArray.length - 1) {\r\n const timeout = setTimeout(() => {\r\n setIndex((prevIndex) => (prevIndex + 1) % childrenArray.length);\r\n }, delay);\r\n\r\n return () => clearTimeout(timeout);\r\n }\r\n }, [index, delay, childrenArray.length]);\r\n\r\n const itemsToShow = useMemo(() => {\r\n const result = childrenArray.slice(0, index + 1).reverse();\r\n return result;\r\n }, [index, childrenArray]);\r\n\r\n return (\r\n \r\n \r\n {itemsToShow.map((item) => (\r\n \r\n {item}\r\n \r\n ))}\r\n \r\n \r\n );\r\n },\r\n);\r\n\r\nAnimatedList.displayName = \"AnimatedList\";\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/notification.tsx", - "content": "import { cn } from \"@/lib/utils\";\ninterface Item {\n name: string;\n description: string;\n icon: string;\n color: string;\n time: string;\n}\n\nexport const Notification = ({\n name,\n description,\n icon,\n color,\n time,\n}: Item) => {\n return (\n \n
\n \n {icon}\n
\n
\n
\n {name}\n ยท\n {time}\n
\n

\n {description}\n

\n
\n \n \n );\n};\n", + "content": "import { cn } from \"@/lib/utils\";\r\ninterface Item {\r\n name: string;\r\n description: string;\r\n icon: string;\r\n color: string;\r\n time: string;\r\n}\r\n\r\nexport const Notification = ({\r\n name,\r\n description,\r\n icon,\r\n color,\r\n time,\r\n}: Item) => {\r\n return (\r\n \r\n
\r\n \r\n {icon}\r\n
\r\n
\r\n
\r\n {name}\r\n ยท\r\n {time}\r\n
\r\n

\r\n {description}\r\n

\r\n
\r\n \r\n \r\n );\r\n};\r\n", "type": "registry:component" } ] diff --git a/public/r/animated-pricing.json b/public/r/animated-pricing.json index 51a5a86..ae53b10 100644 --- a/public/r/animated-pricing.json +++ b/public/r/animated-pricing.json @@ -11,22 +11,22 @@ "files": [ { "path": "./src/components/nurui/animated-pricing-demo.tsx", - "content": "import {\n ModernPricingPage,\n PricingCardProps,\n} from \"@/components/nurui/modern-pricing-page\";\n\nconst myPricingPlans: PricingCardProps[] = [\n {\n planName: \"Basic\",\n description: \"Perfect for personal projects and hobbyists.\",\n price: \"0\",\n features: [\"1 User\", \"1GB Storage\", \"Community Forum\"],\n buttonText: \"Get Started\",\n buttonVariant: \"secondary\",\n },\n {\n planName: \"Team\",\n description: \"Collaborate with your team on multiple projects.\",\n price: \"49\",\n features: [\n \"10 Users\",\n \"100GB Storage\",\n \"Email Support\",\n \"Shared Workspaces\",\n ],\n buttonText: \"Choose Team Plan\",\n isPopular: true,\n buttonVariant: \"primary\",\n },\n {\n planName: \"Agency\",\n description: \"Manage all your clients under one roof.\",\n price: \"149\",\n features: [\n \"Unlimited Users\",\n \"1TB Storage\",\n \"Dedicated Support\",\n \"Client Invoicing\",\n ],\n buttonText: \"Contact Us\",\n buttonVariant: \"primary\",\n },\n];\n\nconst AnimatedPricingDemo = () => {\n return (\n \n Find the Perfect Plan\n \n }\n subtitle=\"Start for free, then grow with us. Flexible plans for projects of all sizes.\"\n plans={myPricingPlans}\n showAnimatedBackground={true}\n />\n );\n};\n\nexport { AnimatedPricingDemo };\n", + "content": "import {\r\n ModernPricingPage,\r\n PricingCardProps,\r\n} from \"@/components/nurui/modern-pricing-page\";\r\n\r\nconst myPricingPlans: PricingCardProps[] = [\r\n {\r\n planName: \"Basic\",\r\n description: \"Perfect for personal projects and hobbyists.\",\r\n price: \"0\",\r\n features: [\"1 User\", \"1GB Storage\", \"Community Forum\"],\r\n buttonText: \"Get Started\",\r\n buttonVariant: \"secondary\",\r\n },\r\n {\r\n planName: \"Team\",\r\n description: \"Collaborate with your team on multiple projects.\",\r\n price: \"49\",\r\n features: [\r\n \"10 Users\",\r\n \"100GB Storage\",\r\n \"Email Support\",\r\n \"Shared Workspaces\",\r\n ],\r\n buttonText: \"Choose Team Plan\",\r\n isPopular: true,\r\n buttonVariant: \"primary\",\r\n },\r\n {\r\n planName: \"Agency\",\r\n description: \"Manage all your clients under one roof.\",\r\n price: \"149\",\r\n features: [\r\n \"Unlimited Users\",\r\n \"1TB Storage\",\r\n \"Dedicated Support\",\r\n \"Client Invoicing\",\r\n ],\r\n buttonText: \"Contact Us\",\r\n buttonVariant: \"primary\",\r\n },\r\n];\r\n\r\nconst AnimatedPricingDemo = () => {\r\n return (\r\n \r\n Find the Perfect Plan\r\n \r\n }\r\n subtitle=\"Start for free, then grow with us. Flexible plans for projects of all sizes.\"\r\n plans={myPricingPlans}\r\n showAnimatedBackground={true}\r\n />\r\n );\r\n};\r\n\r\nexport { AnimatedPricingDemo };\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/modern-pricing-page.tsx", - "content": "\"use client\"\nimport type React from \"react\"\nimport { ShaderCanvas } from \"@/components/nurui/shader-canvas\"\nimport { RippleButton } from \"@/components/nurui/multi-type-ripple-buttons\"\n\nconst CheckIcon = ({ className }: { className?: string }) => (\n \n \n \n)\n\nexport interface PricingCardProps {\n planName: string\n description: string\n price: string\n features: string[]\n buttonText: string\n isPopular?: boolean\n buttonVariant?: \"primary\" | \"secondary\"\n}\n\nexport const AnimatedPricingCard = ({\n planName,\n description,\n price,\n features,\n buttonText,\n isPopular = false,\n buttonVariant = \"primary\",\n}: PricingCardProps) => {\n // Prevent runtime errors if `features` is undefined\n const safeFeatures = features ?? []\n const cardClasses = `\n backdrop-blur-[14px] bg-white/80 dark:bg-black/20 rounded-2xl shadow-xl \n flex-1 max-w-sm w-full px-6 py-8 flex flex-col transition-all duration-300\n border border-gray-200/50 dark:border-white/10\n ${isPopular ? \"scale-105 relative ring-2 ring-cyan-400/50 shadow-2xl bg-white/90 dark:bg-black/30\" : \"hover:scale-[1.02]\"}\n`\n const buttonClasses = `\n mt-auto w-full py-2.5 rounded-xl font-semibold text-[14px] transition font-sans\n ${\n buttonVariant === \"primary\"\n ? \"bg-cyan-400 hover:bg-cyan-300 text-foreground\"\n : \"bg-black/10 hover:bg-black/20 text-foreground border border-black/20 dark:bg-white/10 dark:hover:bg-white/20 dark:text-white dark:border-white/20\"\n }\n`\n\n return (\n
\n {isPopular && (\n
\n Most Popular\n
\n )}\n
\n

{planName}

\n

{description}

\n
\n
\n ${price}\n /mo\n
\n
\n {/* use safeFeatures instead of features */}\n
    \n {safeFeatures.map((feature, index) => (\n
  • \n {feature}\n
  • \n ))}\n
\n {buttonText}\n
\n )\n}\n\ninterface ModernPricingPageProps {\n title: React.ReactNode\n subtitle: React.ReactNode\n plans: PricingCardProps[]\n showAnimatedBackground?: boolean\n}\n\nexport const ModernPricingPage = ({\n title,\n subtitle,\n plans,\n showAnimatedBackground = true,\n}: ModernPricingPageProps) => {\n return (\n
\n
\n
\n

\n {title}\n

\n

{subtitle}

\n
\n\n
\n {showAnimatedBackground && (\n
\n \n
\n )}\n
\n {plans.map((plan) => (\n \n ))}\n
\n
\n
\n
\n )\n}", + "content": "\"use client\"\r\nimport type React from \"react\"\r\nimport { ShaderCanvas } from \"@/components/nurui/shader-canvas\"\r\nimport { RippleButton } from \"@/components/nurui/multi-type-ripple-buttons\"\r\n\r\nconst CheckIcon = ({ className }: { className?: string }) => (\r\n \r\n \r\n \r\n)\r\n\r\nexport interface PricingCardProps {\r\n planName: string\r\n description: string\r\n price: string\r\n features: string[]\r\n buttonText: string\r\n isPopular?: boolean\r\n buttonVariant?: \"primary\" | \"secondary\"\r\n}\r\n\r\nexport const AnimatedPricingCard = ({\r\n planName,\r\n description,\r\n price,\r\n features,\r\n buttonText,\r\n isPopular = false,\r\n buttonVariant = \"primary\",\r\n}: PricingCardProps) => {\r\n // Prevent runtime errors if `features` is undefined\r\n const safeFeatures = features ?? []\r\n const cardClasses = `\r\n backdrop-blur-[14px] bg-white/80 dark:bg-black/20 rounded-2xl shadow-xl \r\n flex-1 max-w-sm w-full px-6 py-8 flex flex-col transition-all duration-300\r\n border border-gray-200/50 dark:border-white/10\r\n ${isPopular ? \"scale-105 relative ring-2 ring-cyan-400/50 shadow-2xl bg-white/90 dark:bg-black/30\" : \"hover:scale-[1.02]\"}\r\n`\r\n const buttonClasses = `\r\n mt-auto w-full py-2.5 rounded-xl font-semibold text-[14px] transition font-sans\r\n ${\r\n buttonVariant === \"primary\"\r\n ? \"bg-cyan-400 hover:bg-cyan-300 text-foreground\"\r\n : \"bg-black/10 hover:bg-black/20 text-foreground border border-black/20 dark:bg-white/10 dark:hover:bg-white/20 dark:text-white dark:border-white/20\"\r\n }\r\n`\r\n\r\n return (\r\n
\r\n {isPopular && (\r\n
\r\n Most Popular\r\n
\r\n )}\r\n
\r\n

{planName}

\r\n

{description}

\r\n
\r\n
\r\n ${price}\r\n /mo\r\n
\r\n
\r\n {/* use safeFeatures instead of features */}\r\n
    \r\n {safeFeatures.map((feature, index) => (\r\n
  • \r\n {feature}\r\n
  • \r\n ))}\r\n
\r\n {buttonText}\r\n
\r\n )\r\n}\r\n\r\ninterface ModernPricingPageProps {\r\n title: React.ReactNode\r\n subtitle: React.ReactNode\r\n plans: PricingCardProps[]\r\n showAnimatedBackground?: boolean\r\n}\r\n\r\nexport const ModernPricingPage = ({\r\n title,\r\n subtitle,\r\n plans,\r\n showAnimatedBackground = true,\r\n}: ModernPricingPageProps) => {\r\n return (\r\n
\r\n
\r\n
\r\n

\r\n {title}\r\n

\r\n

{subtitle}

\r\n
\r\n\r\n
\r\n {showAnimatedBackground && (\r\n
\r\n \r\n
\r\n )}\r\n
\r\n {plans.map((plan) => (\r\n \r\n ))}\r\n
\r\n
\r\n
\r\n
\r\n )\r\n}", "type": "registry:component" }, { "path": "./src/components/nurui/shader-canvas.tsx", - "content": "\"use client\";\nimport React, { useRef, useEffect } from \"react\";\n\nexport const ShaderCanvas = () => {\n const canvasRef = useRef(null);\n const glProgramRef = useRef(null);\n const glRef = useRef(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const gl = canvas.getContext(\"webgl\", { alpha: true });\n if (!gl) {\n console.error(\"WebGL not supported\");\n return;\n }\n glRef.current = gl;\n\n const vertexShaderSource = `\n attribute vec2 aPosition;\n void main() {\n gl_Position = vec4(aPosition, 0.0, 1.0);\n }\n `;\n\n const fragmentShaderSource = `\n precision highp float;\n uniform float iTime;\n uniform vec2 iResolution;\n\n mat2 rotate2d(float angle) {\n float c = cos(angle), s = sin(angle);\n return mat2(c, -s, s, c);\n }\n\n float variation(vec2 v1, vec2 v2, float strength, float speed) {\n return sin(dot(normalize(v1), normalize(v2)) * strength + iTime * speed) / 100.0;\n }\n\n vec3 paintCircle(vec2 uv, vec2 center, float rad, float width) {\n vec2 diff = center - uv;\n float len = length(diff);\n len += variation(diff, vec2(0., 1.), 5., 2.);\n len -= variation(diff, vec2(1., 0.), 5., 2.);\n float circle = smoothstep(rad - width, rad, len) - smoothstep(rad, rad + width, len);\n return vec3(circle);\n }\n\n void main() {\n vec2 uv = gl_FragCoord.xy / iResolution.xy;\n uv.x *= 1.5;\n uv.x -= 0.25;\n\n float mask = 0.0;\n float radius = 0.35;\n vec2 center = vec2(0.5);\n\n mask += paintCircle(uv, center, radius, 0.035).r;\n mask += paintCircle(uv, center, radius - 0.018, 0.01).r;\n mask += paintCircle(uv, center, radius + 0.018, 0.005).r;\n\n vec2 v = rotate2d(iTime) * uv;\n vec3 foregroundColor = vec3(v.x, v.y, 0.7 - v.y * v.x);\n\n vec3 color = foregroundColor * mask;\n gl_FragColor = vec4(color, mask); // Transparent outside glow\n }\n `;\n\n const compileShader = (type: number, source: string) => {\n const shader = gl.createShader(type);\n if (!shader) throw new Error(\"Could not create shader\");\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n throw new Error(gl.getShaderInfoLog(shader) || \"Shader compilation error\");\n }\n return shader;\n };\n\n const program = gl.createProgram();\n if (!program) throw new Error(\"Could not create program\");\n\n const vertexShader = compileShader(gl.VERTEX_SHADER, vertexShaderSource);\n const fragmentShader = compileShader(gl.FRAGMENT_SHADER, fragmentShaderSource);\n\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n gl.linkProgram(program);\n gl.useProgram(program);\n glProgramRef.current = program;\n\n const buffer = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n gl.bufferData(\n gl.ARRAY_BUFFER,\n new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),\n gl.STATIC_DRAW\n );\n\n const aPosition = gl.getAttribLocation(program, \"aPosition\");\n gl.enableVertexAttribArray(aPosition);\n gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);\n\n const iTimeLoc = gl.getUniformLocation(program, \"iTime\");\n const iResLoc = gl.getUniformLocation(program, \"iResolution\");\n\n gl.clearColor(0, 0, 0, 0); // Fully transparent\n gl.clear(gl.COLOR_BUFFER_BIT);\n\n const render = (time: number) => {\n gl.uniform1f(iTimeLoc, time * 0.001);\n gl.uniform2f(iResLoc, canvas.width, canvas.height);\n gl.drawArrays(gl.TRIANGLES, 0, 6);\n requestAnimationFrame(render);\n };\n\n const handleResize = () => {\n canvas.width = canvas.offsetWidth;\n canvas.height = canvas.offsetHeight;\n gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);\n };\n\n handleResize();\n window.addEventListener(\"resize\", handleResize);\n requestAnimationFrame(render);\n\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n };\n }, []);\n\n return (\n \n );\n};\n", + "content": "\"use client\";\r\nimport React, { useRef, useEffect } from \"react\";\r\n\r\nexport const ShaderCanvas = () => {\r\n const canvasRef = useRef(null);\r\n const glProgramRef = useRef(null);\r\n const glRef = useRef(null);\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n\r\n const gl = canvas.getContext(\"webgl\", { alpha: true });\r\n if (!gl) {\r\n console.error(\"WebGL not supported\");\r\n return;\r\n }\r\n glRef.current = gl;\r\n\r\n const vertexShaderSource = `\r\n attribute vec2 aPosition;\r\n void main() {\r\n gl_Position = vec4(aPosition, 0.0, 1.0);\r\n }\r\n `;\r\n\r\n const fragmentShaderSource = `\r\n precision highp float;\r\n uniform float iTime;\r\n uniform vec2 iResolution;\r\n\r\n mat2 rotate2d(float angle) {\r\n float c = cos(angle), s = sin(angle);\r\n return mat2(c, -s, s, c);\r\n }\r\n\r\n float variation(vec2 v1, vec2 v2, float strength, float speed) {\r\n return sin(dot(normalize(v1), normalize(v2)) * strength + iTime * speed) / 100.0;\r\n }\r\n\r\n vec3 paintCircle(vec2 uv, vec2 center, float rad, float width) {\r\n vec2 diff = center - uv;\r\n float len = length(diff);\r\n len += variation(diff, vec2(0., 1.), 5., 2.);\r\n len -= variation(diff, vec2(1., 0.), 5., 2.);\r\n float circle = smoothstep(rad - width, rad, len) - smoothstep(rad, rad + width, len);\r\n return vec3(circle);\r\n }\r\n\r\n void main() {\r\n vec2 uv = gl_FragCoord.xy / iResolution.xy;\r\n uv.x *= 1.5;\r\n uv.x -= 0.25;\r\n\r\n float mask = 0.0;\r\n float radius = 0.35;\r\n vec2 center = vec2(0.5);\r\n\r\n mask += paintCircle(uv, center, radius, 0.035).r;\r\n mask += paintCircle(uv, center, radius - 0.018, 0.01).r;\r\n mask += paintCircle(uv, center, radius + 0.018, 0.005).r;\r\n\r\n vec2 v = rotate2d(iTime) * uv;\r\n vec3 foregroundColor = vec3(v.x, v.y, 0.7 - v.y * v.x);\r\n\r\n vec3 color = foregroundColor * mask;\r\n gl_FragColor = vec4(color, mask); // Transparent outside glow\r\n }\r\n `;\r\n\r\n const compileShader = (type: number, source: string) => {\r\n const shader = gl.createShader(type);\r\n if (!shader) throw new Error(\"Could not create shader\");\r\n gl.shaderSource(shader, source);\r\n gl.compileShader(shader);\r\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\r\n throw new Error(gl.getShaderInfoLog(shader) || \"Shader compilation error\");\r\n }\r\n return shader;\r\n };\r\n\r\n const program = gl.createProgram();\r\n if (!program) throw new Error(\"Could not create program\");\r\n\r\n const vertexShader = compileShader(gl.VERTEX_SHADER, vertexShaderSource);\r\n const fragmentShader = compileShader(gl.FRAGMENT_SHADER, fragmentShaderSource);\r\n\r\n gl.attachShader(program, vertexShader);\r\n gl.attachShader(program, fragmentShader);\r\n gl.linkProgram(program);\r\n gl.useProgram(program);\r\n glProgramRef.current = program;\r\n\r\n const buffer = gl.createBuffer();\r\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\r\n gl.bufferData(\r\n gl.ARRAY_BUFFER,\r\n new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),\r\n gl.STATIC_DRAW\r\n );\r\n\r\n const aPosition = gl.getAttribLocation(program, \"aPosition\");\r\n gl.enableVertexAttribArray(aPosition);\r\n gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);\r\n\r\n const iTimeLoc = gl.getUniformLocation(program, \"iTime\");\r\n const iResLoc = gl.getUniformLocation(program, \"iResolution\");\r\n\r\n gl.clearColor(0, 0, 0, 0); // Fully transparent\r\n gl.clear(gl.COLOR_BUFFER_BIT);\r\n\r\n const render = (time: number) => {\r\n gl.uniform1f(iTimeLoc, time * 0.001);\r\n gl.uniform2f(iResLoc, canvas.width, canvas.height);\r\n gl.drawArrays(gl.TRIANGLES, 0, 6);\r\n requestAnimationFrame(render);\r\n };\r\n\r\n const handleResize = () => {\r\n canvas.width = canvas.offsetWidth;\r\n canvas.height = canvas.offsetHeight;\r\n gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);\r\n };\r\n\r\n handleResize();\r\n window.addEventListener(\"resize\", handleResize);\r\n requestAnimationFrame(render);\r\n\r\n return () => {\r\n window.removeEventListener(\"resize\", handleResize);\r\n };\r\n }, []);\r\n\r\n return (\r\n \r\n );\r\n};\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/multi-type-ripple-buttons.tsx", - "content": "import React, { ReactNode, useState, useMemo, MouseEvent, CSSProperties } from 'react';\n\ninterface RippleState {\n key: number;\n x: number;\n y: number;\n size: number;\n color: string;\n}\n\ninterface RippleButtonProps {\n children: ReactNode;\n onClick?: (event: MouseEvent) => void;\n className?: string;\n disabled?: boolean;\n variant?: 'default' | 'hover' | 'ghost' | 'hoverborder';\n rippleColor?: string; // User override for the JS click ripple color\n rippleDuration?: number; // Duration for the JS click ripple (all variants)\n\n // For 'hover' variant\n hoverBaseColor?: string;\n hoverRippleColor?: string;\n\n // For 'hoverborder' variant\n hoverBorderEffectColor?: string; // Color of the visual effect forming the border\n hoverBorderEffectThickness?: string; // Thickness of the border effect (e.g., \"0.3em\", \"2px\")\n}\n\nconst hexToRgba = (hex: string, alpha: number): string => {\n let hexValue = hex.startsWith('#') ? hex.slice(1) : hex;\n if (hexValue.length === 3) {\n hexValue = hexValue.split('').map(char => char + char).join('');\n }\n const r = parseInt(hexValue.slice(0, 2), 16);\n const g = parseInt(hexValue.slice(2, 4), 16);\n const b = parseInt(hexValue.slice(4, 6), 16);\n return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n};\n\nconst GRID_HOVER_NUM_COLS = 36;\nconst GRID_HOVER_NUM_ROWS = 12;\nconst GRID_HOVER_TOTAL_CELLS = GRID_HOVER_NUM_COLS * GRID_HOVER_NUM_ROWS;\nconst GRID_HOVER_RIPPLE_EFFECT_SIZE = \"18.973665961em\";\n\nconst JS_RIPPLE_KEYFRAMES = `\n @keyframes js-ripple-animation {\n 0% { transform: scale(0); opacity: 1; }\n 100% { transform: scale(1); opacity: 0; }\n }\n .animate-js-ripple-effect {\n animation: js-ripple-animation var(--ripple-duration) ease-out forwards;\n }\n`;\n\nconst RippleButton: React.FC = ({\n children,\n onClick,\n className = '',\n disabled = false,\n variant = 'default',\n rippleColor: userProvidedRippleColor,\n rippleDuration = 600,\n hoverBaseColor = '#6996e2',\n hoverRippleColor: customHoverRippleColor,\n hoverBorderEffectColor = '#6996e277',\n hoverBorderEffectThickness = '0.3em',\n}) => {\n const [jsRipples, setJsRipples] = useState([]);\n\n const determinedJsRippleColor = useMemo(() => {\n if (userProvidedRippleColor) {\n return userProvidedRippleColor;\n }\n return 'var(--button-ripple-color, rgba(0, 0, 0, 0.1))';\n }, [userProvidedRippleColor]);\n\n const dynamicGridHoverStyles = useMemo(() => {\n let nthChildHoverRules = '';\n const cellDim = 0.25;\n const initialTopOffset = 0.125;\n const initialLeftOffset = 0.1875;\n\n // Standardized hover transition duration for width and height\n const hoverEffectDuration = '0.9s'; // CHANGED: Standardized to 0.9s\n\n for (let r = 0; r < GRID_HOVER_NUM_ROWS; r++) {\n for (let c = 0; c < GRID_HOVER_NUM_COLS; c++) {\n const childIndex = r * GRID_HOVER_NUM_COLS + c + 1;\n const topPos = initialTopOffset + r * cellDim;\n const leftPos = initialLeftOffset + c * cellDim;\n\n if (variant === 'hover') {\n nthChildHoverRules += `\n .hover-variant-grid-cell:nth-child(${childIndex}):hover ~ .hover-variant-visual-ripple {\n top: ${topPos}em; left: ${leftPos}em;\n transition: width ${hoverEffectDuration} ease, height ${hoverEffectDuration} ease, top 0s linear, left 0s linear;\n }`;\n } else if (variant === 'hoverborder') {\n nthChildHoverRules += `\n .hoverborder-variant-grid-cell:nth-child(${childIndex}):hover ~ .hoverborder-variant-visual-ripple {\n top: ${topPos}em; left: ${leftPos}em;\n transition: width ${hoverEffectDuration} ease-out, height ${hoverEffectDuration} ease-out, top 0s linear, left 0s linear;\n }`; // Using ease-out for hoverborder as it was before, just changed duration\n }\n }\n }\n\n if (variant === 'hover') {\n const actualHoverRippleColor = customHoverRippleColor\n ? customHoverRippleColor\n : hexToRgba(hoverBaseColor, 0.466);\n return `\n .hover-variant-visual-ripple {\n background-color: ${actualHoverRippleColor};\n transition: width ${hoverEffectDuration} ease, height ${hoverEffectDuration} ease, top 99999s linear, left 99999s linear;\n }\n .hover-variant-grid-cell:hover ~ .hover-variant-visual-ripple {\n width: ${GRID_HOVER_RIPPLE_EFFECT_SIZE}; height: ${GRID_HOVER_RIPPLE_EFFECT_SIZE};\n }\n ${nthChildHoverRules}\n `;\n } else if (variant === 'hoverborder') {\n return `\n .hoverborder-variant-ripple-container {\n padding: ${hoverBorderEffectThickness};\n mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);\n mask-composite: exclude;\n -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n }\n .hoverborder-variant-visual-ripple {\n background-color: ${hoverBorderEffectColor};\n /* Ensure the base transition also uses the standardized duration for width/height */\n transition: width ${hoverEffectDuration} ease-out, height ${hoverEffectDuration} ease-out, top 99999s linear, left 9999s linear;\n }\n .hoverborder-variant-grid-cell:hover ~ .hoverborder-variant-visual-ripple {\n width: ${GRID_HOVER_RIPPLE_EFFECT_SIZE}; height: ${GRID_HOVER_RIPPLE_EFFECT_SIZE};\n }\n ${nthChildHoverRules}\n `;\n }\n return '';\n }, [variant, hoverBaseColor, customHoverRippleColor, hoverBorderEffectColor, hoverBorderEffectThickness]);\n\n const createJsRipple = (event: MouseEvent) => {\n const button = event.currentTarget;\n const rect = button.getBoundingClientRect();\n const size = Math.max(rect.width, rect.height) * 2;\n const x = event.clientX - rect.left - size / 2;\n const y = event.clientY - rect.top - size / 2;\n const newRipple: RippleState = { key: Date.now(), x, y, size, color: determinedJsRippleColor };\n setJsRipples(prev => [...prev, newRipple]);\n setTimeout(() => {\n setJsRipples(currentRipples => currentRipples.filter(r => r.key !== newRipple.key));\n }, rippleDuration);\n };\n\n const handleButtonClick = (event: MouseEvent) => {\n if (!disabled) {\n createJsRipple(event);\n if (onClick) onClick(event);\n }\n };\n\n const jsRippleElements = (\n
\n {jsRipples.map(ripple => (\n \n ))}\n
\n );\n\n if (variant === 'hover') {\n const hoverButtonFinalClassName = [\n \"relative\", \"rounded-lg\", \"text-lg\", \"px-4\", \"py-2\",\n \"border-none\", \"bg-transparent\", \"isolate\", \"overflow-hidden\", \"cursor-pointer\",\n disabled ? \"opacity-50 cursor-not-allowed pointer-events-none\" : \"\",\n className,\n ].filter(Boolean).join(\" \");\n return (\n <>\n \n ) : null}\n {globalKey ? (\n \n ) : null}\n {globalKey ? (\n \n ) : null}\n\n {variant === \"rainbow\"\n ? flow({\n colors: rainbowColors,\n })\n : null}\n {props.children}\n {id ? (\n {\n setOpen(false);\n if (globalKey) {\n localStorage.setItem(globalKey, \"true\");\n window.dispatchEvent(new Event(\"banner-status-changed\"));\n }\n }}\n className={cn(\n buttonVariants({\n variant: \"ghost\",\n className:\n \"absolute end-2 md:end-20 top-1/2 -translate-y-1/2 text-fd-muted-foreground/50\",\n size: \"icon\",\n }),\n )}\n >\n \n \n ) : null}\n \n );\n}\n\nconst maskImage =\n \"linear-gradient(to bottom,white,transparent), radial-gradient(circle at top center, white, transparent)\";\n\nfunction flow({ colors }: { colors: string[] }) {\n return (\n <>\n `${color} ${(i * 50) / colors.length}%`).join(\", \")})`,\n backgroundSize: \"200% 100%\",\n filter: \"saturate(2)\",\n } as object\n }\n />\n \n \n );\n}\n", + "content": "\"use client\";\r\nimport { type HTMLAttributes, useEffect, useState } from \"react\";\r\nimport { X } from \"lucide-react\";\r\nimport { buttonVariants } from \"@/components/ui/button\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\ntype BannerVariant = \"rainbow\" | \"normal\";\r\n\r\nexport function Banner({\r\n id,\r\n xColor,\r\n variant = \"normal\",\r\n changeLayout = true,\r\n height = \"3rem\",\r\n rainbowColors = [\r\n \"rgba(0,149,255,0.56)\",\r\n \"rgba(231,77,255,0.77)\",\r\n \"rgba(255,0,0,0.73)\",\r\n \"rgba(131,255,166,0.66)\",\r\n ],\r\n ...props\r\n}: HTMLAttributes & {\r\n /**\r\n * @defaultValue 3rem\r\n */\r\n height?: string;\r\n\r\n xColor?: string;\r\n\r\n /**\r\n * @defaultValue 'normal'\r\n */\r\n variant?: BannerVariant;\r\n\r\n /**\r\n * For rainbow variant only, customise the colors\r\n */\r\n rainbowColors?: string[];\r\n\r\n /**\r\n * Change Fumadocs layout styles\r\n *\r\n * @defaultValue true\r\n */\r\n changeLayout?: boolean;\r\n}) {\r\n const [open, setOpen] = useState(true);\r\n const globalKey = id ? `nd-banner-${id}` : null;\r\n\r\n useEffect(() => {\r\n if (globalKey) setOpen(localStorage.getItem(globalKey) !== \"true\");\r\n }, [globalKey]);\r\n\r\n if (!open) return null;\r\n\r\n return (\r\n \r\n {changeLayout && open ? (\r\n \r\n ) : null}\r\n {globalKey ? (\r\n \r\n ) : null}\r\n {globalKey ? (\r\n \r\n ) : null}\r\n\r\n {variant === \"rainbow\"\r\n ? flow({\r\n colors: rainbowColors,\r\n })\r\n : null}\r\n {props.children}\r\n {id ? (\r\n {\r\n setOpen(false);\r\n if (globalKey) {\r\n localStorage.setItem(globalKey, \"true\");\r\n window.dispatchEvent(new Event(\"banner-status-changed\"));\r\n }\r\n }}\r\n className={cn(\r\n buttonVariants({\r\n variant: \"ghost\",\r\n className:\r\n \"absolute end-2 md:end-20 top-1/2 -translate-y-1/2 text-fd-muted-foreground/50\",\r\n size: \"icon\",\r\n }),\r\n )}\r\n >\r\n \r\n \r\n ) : null}\r\n \r\n );\r\n}\r\n\r\nconst maskImage =\r\n \"linear-gradient(to bottom,white,transparent), radial-gradient(circle at top center, white, transparent)\";\r\n\r\nfunction flow({ colors }: { colors: string[] }) {\r\n return (\r\n <>\r\n `${color} ${(i * 50) / colors.length}%`).join(\", \")})`,\r\n backgroundSize: \"200% 100%\",\r\n filter: \"saturate(2)\",\r\n } as object\r\n }\r\n />\r\n \r\n \r\n );\r\n}\r\n", "type": "registry:component" } ] diff --git a/public/r/banner.json b/public/r/banner.json index 6924e24..d6bf13e 100644 --- a/public/r/banner.json +++ b/public/r/banner.json @@ -14,12 +14,12 @@ "files": [ { "path": "./src/components/nurui/banner-demo.tsx", - "content": "import React from \"react\";\nimport { Banner } from \"@/components/nurui/banner\";\n\nconst BannerDemo = () => {\n return (\n
\n \n ๐Ÿš€ Project evolving more features soon!\n \n
\n );\n};\n\nexport default BannerDemo;\n", + "content": "import React from \"react\";\r\nimport { Banner } from \"@/components/nurui/banner\";\r\n\r\nconst BannerDemo = () => {\r\n return (\r\n
\r\n \r\n ๐Ÿš€ Project evolving more features soon!\r\n \r\n
\r\n );\r\n};\r\n\r\nexport default BannerDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/banner.tsx", - "content": "\"use client\";\nimport { type HTMLAttributes, useEffect, useState } from \"react\";\nimport { X } from \"lucide-react\";\nimport { buttonVariants } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\ntype BannerVariant = \"rainbow\" | \"normal\";\n\nexport function Banner({\n id,\n xColor,\n variant = \"normal\",\n changeLayout = true,\n height = \"3rem\",\n rainbowColors = [\n \"rgba(0,149,255,0.56)\",\n \"rgba(231,77,255,0.77)\",\n \"rgba(255,0,0,0.73)\",\n \"rgba(131,255,166,0.66)\",\n ],\n ...props\n}: HTMLAttributes & {\n /**\n * @defaultValue 3rem\n */\n height?: string;\n\n xColor?: string;\n\n /**\n * @defaultValue 'normal'\n */\n variant?: BannerVariant;\n\n /**\n * For rainbow variant only, customise the colors\n */\n rainbowColors?: string[];\n\n /**\n * Change Fumadocs layout styles\n *\n * @defaultValue true\n */\n changeLayout?: boolean;\n}) {\n const [open, setOpen] = useState(true);\n const globalKey = id ? `nd-banner-${id}` : null;\n\n useEffect(() => {\n if (globalKey) setOpen(localStorage.getItem(globalKey) !== \"true\");\n }, [globalKey]);\n\n if (!open) return null;\n\n return (\n \n {changeLayout && open ? (\n \n ) : null}\n {globalKey ? (\n \n ) : null}\n {globalKey ? (\n \n ) : null}\n\n {variant === \"rainbow\"\n ? flow({\n colors: rainbowColors,\n })\n : null}\n {props.children}\n {id ? (\n {\n setOpen(false);\n if (globalKey) {\n localStorage.setItem(globalKey, \"true\");\n window.dispatchEvent(new Event(\"banner-status-changed\"));\n }\n }}\n className={cn(\n buttonVariants({\n variant: \"ghost\",\n className:\n \"absolute end-2 md:end-20 top-1/2 -translate-y-1/2 text-fd-muted-foreground/50\",\n size: \"icon\",\n }),\n )}\n >\n \n \n ) : null}\n \n );\n}\n\nconst maskImage =\n \"linear-gradient(to bottom,white,transparent), radial-gradient(circle at top center, white, transparent)\";\n\nfunction flow({ colors }: { colors: string[] }) {\n return (\n <>\n `${color} ${(i * 50) / colors.length}%`).join(\", \")})`,\n backgroundSize: \"200% 100%\",\n filter: \"saturate(2)\",\n } as object\n }\n />\n \n \n );\n}\n", + "content": "\"use client\";\r\nimport { type HTMLAttributes, useEffect, useState } from \"react\";\r\nimport { X } from \"lucide-react\";\r\nimport { buttonVariants } from \"@/components/ui/button\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\ntype BannerVariant = \"rainbow\" | \"normal\";\r\n\r\nexport function Banner({\r\n id,\r\n xColor,\r\n variant = \"normal\",\r\n changeLayout = true,\r\n height = \"3rem\",\r\n rainbowColors = [\r\n \"rgba(0,149,255,0.56)\",\r\n \"rgba(231,77,255,0.77)\",\r\n \"rgba(255,0,0,0.73)\",\r\n \"rgba(131,255,166,0.66)\",\r\n ],\r\n ...props\r\n}: HTMLAttributes & {\r\n /**\r\n * @defaultValue 3rem\r\n */\r\n height?: string;\r\n\r\n xColor?: string;\r\n\r\n /**\r\n * @defaultValue 'normal'\r\n */\r\n variant?: BannerVariant;\r\n\r\n /**\r\n * For rainbow variant only, customise the colors\r\n */\r\n rainbowColors?: string[];\r\n\r\n /**\r\n * Change Fumadocs layout styles\r\n *\r\n * @defaultValue true\r\n */\r\n changeLayout?: boolean;\r\n}) {\r\n const [open, setOpen] = useState(true);\r\n const globalKey = id ? `nd-banner-${id}` : null;\r\n\r\n useEffect(() => {\r\n if (globalKey) setOpen(localStorage.getItem(globalKey) !== \"true\");\r\n }, [globalKey]);\r\n\r\n if (!open) return null;\r\n\r\n return (\r\n \r\n {changeLayout && open ? (\r\n \r\n ) : null}\r\n {globalKey ? (\r\n \r\n ) : null}\r\n {globalKey ? (\r\n \r\n ) : null}\r\n\r\n {variant === \"rainbow\"\r\n ? flow({\r\n colors: rainbowColors,\r\n })\r\n : null}\r\n {props.children}\r\n {id ? (\r\n {\r\n setOpen(false);\r\n if (globalKey) {\r\n localStorage.setItem(globalKey, \"true\");\r\n window.dispatchEvent(new Event(\"banner-status-changed\"));\r\n }\r\n }}\r\n className={cn(\r\n buttonVariants({\r\n variant: \"ghost\",\r\n className:\r\n \"absolute end-2 md:end-20 top-1/2 -translate-y-1/2 text-fd-muted-foreground/50\",\r\n size: \"icon\",\r\n }),\r\n )}\r\n >\r\n \r\n \r\n ) : null}\r\n \r\n );\r\n}\r\n\r\nconst maskImage =\r\n \"linear-gradient(to bottom,white,transparent), radial-gradient(circle at top center, white, transparent)\";\r\n\r\nfunction flow({ colors }: { colors: string[] }) {\r\n return (\r\n <>\r\n `${color} ${(i * 50) / colors.length}%`).join(\", \")})`,\r\n backgroundSize: \"200% 100%\",\r\n filter: \"saturate(2)\",\r\n } as object\r\n }\r\n />\r\n \r\n \r\n );\r\n}\r\n", "type": "registry:component" } ] diff --git a/public/r/bars-background.json b/public/r/bars-background.json index 3a4effa..7c67661 100644 --- a/public/r/bars-background.json +++ b/public/r/bars-background.json @@ -10,12 +10,12 @@ "files": [ { "path": "./src/components/nurui/bars-background-demo.tsx", - "content": "import { GradientBars } from \"@/components/nurui/gradient-bars\";\n\nexport default function BarsBackgroundDemo() {\n return (\n
\n \n

Bars backgrounds :)

\n
\n );\n}\n", + "content": "import { GradientBars } from \"@/components/nurui/gradient-bars\";\r\n\r\nexport default function BarsBackgroundDemo() {\r\n return (\r\n
\r\n \r\n

Bars backgrounds :)

\r\n
\r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/gradient-bars.tsx", - "content": "'use client';\nimport { motion } from 'motion/react';\n\ninterface GradientBarsProps {\n bars?: number;\n colors?: string[];\n}\n\nexport const GradientBars = ({\n bars = 20,\n colors = ['#3ca2faD9', 'transparent'],\n}: GradientBarsProps) => {\n const gradientStyle = `linear-gradient(to top, ${colors.join(', ')})`;\n return (\n
\n
\n {Array.from({ length: bars }).map((_, index) => {\n const position = index / (bars - 1);\n const center = 0.5;\n const distance = Math.abs(position - center);\n const scale = 0.3 + 0.7 * Math.pow(distance * 2, 1.2);\n\n return (\n \n );\n })}\n
\n
\n );\n};\n", + "content": "'use client';\r\nimport { motion } from 'motion/react';\r\n\r\ninterface GradientBarsProps {\r\n bars?: number;\r\n colors?: string[];\r\n}\r\n\r\nexport const GradientBars = ({\r\n bars = 20,\r\n colors = ['#3ca2faD9', 'transparent'],\r\n}: GradientBarsProps) => {\r\n const gradientStyle = `linear-gradient(to top, ${colors.join(', ')})`;\r\n return (\r\n
\r\n
\r\n {Array.from({ length: bars }).map((_, index) => {\r\n const position = index / (bars - 1);\r\n const center = 0.5;\r\n const distance = Math.abs(position - center);\r\n const scale = 0.3 + 0.7 * Math.pow(distance * 2, 1.2);\r\n\r\n return (\r\n \r\n );\r\n })}\r\n
\r\n
\r\n );\r\n};\r\n", "type": "registry:component" } ] diff --git a/public/r/border-button.json b/public/r/border-button.json index 3a815ec..a560534 100644 --- a/public/r/border-button.json +++ b/public/r/border-button.json @@ -12,14 +12,12 @@ "files": [ { "path": "./src/components/nurui/border-button-demo.tsx", - "content": "import React from \"react\";\nimport BorderAnimationButton from \"@/components/nurui/border-button\";\n\nconst BorderAnimationButtonDemo = () => {\n return (\n
\n \n
\n );\n};\n\nexport default BorderAnimationButtonDemo;\n", + "content": "import React from \"react\";\r\nimport BorderAnimationButton from \"@/components/nurui/border-button\";\r\n\r\nconst BorderAnimationButtonDemo = () => {\r\n return (\r\n
\r\n \r\n
\r\n );\r\n};\r\n\r\nexport default BorderAnimationButtonDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/border-button.tsx", - "content": "import { cn } from \"@/lib/utils\";\r\nimport React from \"react\";\r\nimport { FaLocationArrow } from \"react-icons/fa\";\r\n\r\nconst BorderAnimationButton = ({ text , className}: { text: string , className?: string}) => {\r\n return (\r\n
\r\n \r\n

\r\n {text}\r\n \r\n

\r\n
\r\n );\r\n};\r\n\r\nexport default BorderAnimationButton;\r\n", - "type": "registry:component" } ] diff --git a/public/r/canvas-cursor.json b/public/r/canvas-cursor.json index 4064ccc..e52808a 100644 --- a/public/r/canvas-cursor.json +++ b/public/r/canvas-cursor.json @@ -10,12 +10,12 @@ "files": [ { "path": "./src/components/nurui/canvas-cursor-demo.tsx", - "content": "import React from \"react\";\nimport CanvasCursor from \"@/components/nurui/canvas-cursor\";\n\nconst CanvasCursorDemo = () => {\n return (\n <>\n

\n Move cursor to see the effect.\n

\n \n \n );\n};\n\nexport default CanvasCursorDemo;\n", + "content": "import React from \"react\";\r\nimport CanvasCursor from \"@/components/nurui/canvas-cursor\";\r\n\r\nconst CanvasCursorDemo = () => {\r\n return (\r\n <>\r\n

\r\n Move cursor to see the effect.\r\n

\r\n \r\n \r\n );\r\n};\r\n\r\nexport default CanvasCursorDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/canvas-cursor.tsx", - "content": "/* eslint-disable no-var */\n/* eslint-disable @typescript-eslint/no-unused-expressions */\n/* eslint-disable @typescript-eslint/ban-ts-comment */\n// @ts-nocheck\n\"use client\";\n\n// this is a client component\nimport { useEffect } from \"react\";\n\nexport default function CanvasCursor() {\n useEffect(() => {\n renderCanvas();\n }, []);\n return (\n \n );\n}\n\nfunction n(e) {\n // @ts-ignore\n this.init(e || {});\n}\nn.prototype = {\n // @ts-ignore\n init: function (e) {\n // @ts-ignore\n this.phase = e.phase || 0;\n // @ts-ignore\n this.offset = e.offset || 0;\n // @ts-ignore\n this.frequency = e.frequency || 0.001;\n // @ts-ignore\n this.amplitude = e.amplitude || 1;\n },\n update: function () {\n return (\n // @ts-ignore\n (\n (this.phase += this.frequency),\n // @ts-ignore\n (e = this.offset + Math.sin(this.phase) * this.amplitude)\n )\n );\n },\n value: function () {\n return e;\n },\n};\n\n// @ts-ignore\nfunction Line(e) {\n // @ts-ignore\n this.init(e || {});\n}\n\nLine.prototype = {\n // @ts-ignore\n init: function (e) {\n // @ts-ignore\n this.spring = e.spring + 0.1 * Math.random() - 0.05;\n // @ts-ignore\n this.friction = E.friction + 0.01 * Math.random() - 0.005;\n // @ts-ignore\n this.nodes = [];\n for (var t, n = 0; n < E.size; n++) {\n t = new Node();\n // @ts-ignore\n t.x = pos.x;\n // @ts-ignore\n t.y = pos.y;\n // @ts-ignore\n this.nodes.push(t);\n }\n },\n update: function () {\n // @ts-ignore\n let e = this.spring,\n // @ts-ignore\n t = this.nodes[0];\n // @ts-ignore\n t.vx += (pos.x - t.x) * e;\n // @ts-ignore\n t.vy += (pos.y - t.y) * e;\n // @ts-ignore\n for (var n, i = 0, a = this.nodes.length; i < a; i++)\n // @ts-ignore\n ((t = this.nodes[i]),\n 0 < i &&\n // @ts-ignore\n ((n = this.nodes[i - 1]),\n (t.vx += (n.x - t.x) * e),\n (t.vy += (n.y - t.y) * e),\n (t.vx += n.vx * E.dampening),\n (t.vy += n.vy * E.dampening)),\n // @ts-ignore\n (t.vx *= this.friction),\n // @ts-ignore\n (t.vy *= this.friction),\n (t.x += t.vx),\n (t.y += t.vy),\n (e *= E.tension));\n },\n draw: function () {\n let e,\n t,\n // @ts-ignore\n n = this.nodes[0].x,\n // @ts-ignore\n i = this.nodes[0].y;\n // @ts-ignore\n ctx.beginPath();\n // @ts-ignore\n ctx.moveTo(n, i);\n // @ts-ignore\n for (var a = 1, o = this.nodes.length - 2; a < o; a++) {\n // @ts-ignore\n e = this.nodes[a];\n // @ts-ignore\n t = this.nodes[a + 1];\n n = 0.5 * (e.x + t.x);\n i = 0.5 * (e.y + t.y);\n // @ts-ignore\n ctx.quadraticCurveTo(e.x, e.y, n, i);\n }\n // @ts-ignore\n e = this.nodes[a];\n // @ts-ignore\n t = this.nodes[a + 1];\n // @ts-ignore\n ctx.quadraticCurveTo(e.x, e.y, t.x, t.y);\n // @ts-ignore\n ctx.stroke();\n // @ts-ignore\n ctx.closePath();\n },\n};\n\n// @ts-ignore\nfunction onMousemove(e) {\n function o() {\n lines = [];\n for (let e = 0; e < E.trails; e++)\n lines.push(new Line({ spring: 0.45 + (e / E.trails) * 0.025 }));\n }\n // @ts-ignore\n function c(e) {\n (e.touches\n ? // @ts-ignore\n ((pos.x = e.touches[0].pageX), (pos.y = e.touches[0].pageY))\n : // @ts-ignore\n ((pos.x = e.clientX), (pos.y = e.clientY)),\n e.preventDefault());\n }\n // @ts-ignore\n function l(e) {\n // @ts-ignore\n 1 == e.touches.length &&\n ((pos.x = e.touches[0].pageX), (pos.y = e.touches[0].pageY));\n }\n (document.removeEventListener(\"mousemove\", onMousemove),\n document.removeEventListener(\"touchstart\", onMousemove),\n document.addEventListener(\"mousemove\", c),\n document.addEventListener(\"touchmove\", c),\n document.addEventListener(\"touchstart\", l),\n c(e),\n o(),\n render());\n}\n\nfunction render() {\n // @ts-ignore\n if (ctx.running) {\n // @ts-ignore\n ctx.globalCompositeOperation = \"source-over\";\n // @ts-ignore\n ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);\n // @ts-ignore\n ctx.globalCompositeOperation = \"lighter\";\n // @ts-ignore\n ctx.strokeStyle = \"hsla(\" + Math.round(f.update()) + \",100%,50%,0.025)\";\n // @ts-ignore\n ctx.lineWidth = 10;\n for (var e, t = 0; t < E.trails; t++) {\n // @ts-ignore\n (e = lines[t]).update();\n e.draw();\n }\n // @ts-ignore\n ctx.frame++;\n window.requestAnimationFrame(render);\n }\n}\n\nfunction resizeCanvas() {\n // @ts-ignore\n ctx.canvas.width = window.innerWidth - 20;\n // @ts-ignore\n ctx.canvas.height = window.innerHeight;\n}\n\n// @ts-ignore\nvar ctx,\n // @ts-ignore\n f,\n e = 0,\n pos = {},\n // @ts-ignore\n lines = [],\n E = {\n debug: true,\n friction: 0.5,\n trails: 80,\n size: 50,\n dampening: 0.025,\n tension: 0.99,\n };\nfunction Node() {\n this.x = 0;\n this.y = 0;\n this.vy = 0;\n this.vx = 0;\n}\n\nexport const renderCanvas = function () {\n // @ts-ignore\n ctx = document.getElementById(\"canvas\").getContext(\"2d\");\n ctx.running = true;\n ctx.frame = 1;\n f = new n({\n phase: Math.random() * 2 * Math.PI,\n amplitude: 85,\n frequency: 0.0015,\n offset: 285,\n });\n document.addEventListener(\"mousemove\", onMousemove);\n document.addEventListener(\"touchstart\", onMousemove);\n document.body.addEventListener(\"orientationchange\", resizeCanvas);\n window.addEventListener(\"resize\", resizeCanvas);\n window.addEventListener(\"focus\", () => {\n // @ts-ignore\n if (!ctx.running) {\n // @ts-ignore\n ctx.running = true;\n render();\n }\n });\n window.addEventListener(\"blur\", () => {\n // @ts-ignore\n ctx.running = true;\n });\n resizeCanvas();\n};\n", + "content": "/* eslint-disable no-var */\r\n/* eslint-disable @typescript-eslint/no-unused-expressions */\r\n/* eslint-disable @typescript-eslint/ban-ts-comment */\r\n// @ts-nocheck\r\n\"use client\";\r\n\r\n// this is a client component\r\nimport { useEffect } from \"react\";\r\n\r\nexport default function CanvasCursor() {\r\n useEffect(() => {\r\n renderCanvas();\r\n }, []);\r\n return (\r\n \r\n );\r\n}\r\n\r\nfunction n(e) {\r\n // @ts-ignore\r\n this.init(e || {});\r\n}\r\nn.prototype = {\r\n // @ts-ignore\r\n init: function (e) {\r\n // @ts-ignore\r\n this.phase = e.phase || 0;\r\n // @ts-ignore\r\n this.offset = e.offset || 0;\r\n // @ts-ignore\r\n this.frequency = e.frequency || 0.001;\r\n // @ts-ignore\r\n this.amplitude = e.amplitude || 1;\r\n },\r\n update: function () {\r\n return (\r\n // @ts-ignore\r\n (\r\n (this.phase += this.frequency),\r\n // @ts-ignore\r\n (e = this.offset + Math.sin(this.phase) * this.amplitude)\r\n )\r\n );\r\n },\r\n value: function () {\r\n return e;\r\n },\r\n};\r\n\r\n// @ts-ignore\r\nfunction Line(e) {\r\n // @ts-ignore\r\n this.init(e || {});\r\n}\r\n\r\nLine.prototype = {\r\n // @ts-ignore\r\n init: function (e) {\r\n // @ts-ignore\r\n this.spring = e.spring + 0.1 * Math.random() - 0.05;\r\n // @ts-ignore\r\n this.friction = E.friction + 0.01 * Math.random() - 0.005;\r\n // @ts-ignore\r\n this.nodes = [];\r\n for (var t, n = 0; n < E.size; n++) {\r\n t = new Node();\r\n // @ts-ignore\r\n t.x = pos.x;\r\n // @ts-ignore\r\n t.y = pos.y;\r\n // @ts-ignore\r\n this.nodes.push(t);\r\n }\r\n },\r\n update: function () {\r\n // @ts-ignore\r\n let e = this.spring,\r\n // @ts-ignore\r\n t = this.nodes[0];\r\n // @ts-ignore\r\n t.vx += (pos.x - t.x) * e;\r\n // @ts-ignore\r\n t.vy += (pos.y - t.y) * e;\r\n // @ts-ignore\r\n for (var n, i = 0, a = this.nodes.length; i < a; i++)\r\n // @ts-ignore\r\n ((t = this.nodes[i]),\r\n 0 < i &&\r\n // @ts-ignore\r\n ((n = this.nodes[i - 1]),\r\n (t.vx += (n.x - t.x) * e),\r\n (t.vy += (n.y - t.y) * e),\r\n (t.vx += n.vx * E.dampening),\r\n (t.vy += n.vy * E.dampening)),\r\n // @ts-ignore\r\n (t.vx *= this.friction),\r\n // @ts-ignore\r\n (t.vy *= this.friction),\r\n (t.x += t.vx),\r\n (t.y += t.vy),\r\n (e *= E.tension));\r\n },\r\n draw: function () {\r\n let e,\r\n t,\r\n // @ts-ignore\r\n n = this.nodes[0].x,\r\n // @ts-ignore\r\n i = this.nodes[0].y;\r\n // @ts-ignore\r\n ctx.beginPath();\r\n // @ts-ignore\r\n ctx.moveTo(n, i);\r\n // @ts-ignore\r\n for (var a = 1, o = this.nodes.length - 2; a < o; a++) {\r\n // @ts-ignore\r\n e = this.nodes[a];\r\n // @ts-ignore\r\n t = this.nodes[a + 1];\r\n n = 0.5 * (e.x + t.x);\r\n i = 0.5 * (e.y + t.y);\r\n // @ts-ignore\r\n ctx.quadraticCurveTo(e.x, e.y, n, i);\r\n }\r\n // @ts-ignore\r\n e = this.nodes[a];\r\n // @ts-ignore\r\n t = this.nodes[a + 1];\r\n // @ts-ignore\r\n ctx.quadraticCurveTo(e.x, e.y, t.x, t.y);\r\n // @ts-ignore\r\n ctx.stroke();\r\n // @ts-ignore\r\n ctx.closePath();\r\n },\r\n};\r\n\r\n// @ts-ignore\r\nfunction onMousemove(e) {\r\n function o() {\r\n lines = [];\r\n for (let e = 0; e < E.trails; e++)\r\n lines.push(new Line({ spring: 0.45 + (e / E.trails) * 0.025 }));\r\n }\r\n // @ts-ignore\r\n function c(e) {\r\n (e.touches\r\n ? // @ts-ignore\r\n ((pos.x = e.touches[0].pageX), (pos.y = e.touches[0].pageY))\r\n : // @ts-ignore\r\n ((pos.x = e.clientX), (pos.y = e.clientY)),\r\n e.preventDefault());\r\n }\r\n // @ts-ignore\r\n function l(e) {\r\n // @ts-ignore\r\n 1 == e.touches.length &&\r\n ((pos.x = e.touches[0].pageX), (pos.y = e.touches[0].pageY));\r\n }\r\n (document.removeEventListener(\"mousemove\", onMousemove),\r\n document.removeEventListener(\"touchstart\", onMousemove),\r\n document.addEventListener(\"mousemove\", c),\r\n document.addEventListener(\"touchmove\", c),\r\n document.addEventListener(\"touchstart\", l),\r\n c(e),\r\n o(),\r\n render());\r\n}\r\n\r\nfunction render() {\r\n // @ts-ignore\r\n if (ctx.running) {\r\n // @ts-ignore\r\n ctx.globalCompositeOperation = \"source-over\";\r\n // @ts-ignore\r\n ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);\r\n // @ts-ignore\r\n ctx.globalCompositeOperation = \"lighter\";\r\n // @ts-ignore\r\n ctx.strokeStyle = \"hsla(\" + Math.round(f.update()) + \",100%,50%,0.025)\";\r\n // @ts-ignore\r\n ctx.lineWidth = 10;\r\n for (var e, t = 0; t < E.trails; t++) {\r\n // @ts-ignore\r\n (e = lines[t]).update();\r\n e.draw();\r\n }\r\n // @ts-ignore\r\n ctx.frame++;\r\n window.requestAnimationFrame(render);\r\n }\r\n}\r\n\r\nfunction resizeCanvas() {\r\n // @ts-ignore\r\n ctx.canvas.width = window.innerWidth - 20;\r\n // @ts-ignore\r\n ctx.canvas.height = window.innerHeight;\r\n}\r\n\r\n// @ts-ignore\r\nvar ctx,\r\n // @ts-ignore\r\n f,\r\n e = 0,\r\n pos = {},\r\n // @ts-ignore\r\n lines = [],\r\n E = {\r\n debug: true,\r\n friction: 0.5,\r\n trails: 80,\r\n size: 50,\r\n dampening: 0.025,\r\n tension: 0.99,\r\n };\r\nfunction Node() {\r\n this.x = 0;\r\n this.y = 0;\r\n this.vy = 0;\r\n this.vx = 0;\r\n}\r\n\r\nexport const renderCanvas = function () {\r\n // @ts-ignore\r\n ctx = document.getElementById(\"canvas\").getContext(\"2d\");\r\n ctx.running = true;\r\n ctx.frame = 1;\r\n f = new n({\r\n phase: Math.random() * 2 * Math.PI,\r\n amplitude: 85,\r\n frequency: 0.0015,\r\n offset: 285,\r\n });\r\n document.addEventListener(\"mousemove\", onMousemove);\r\n document.addEventListener(\"touchstart\", onMousemove);\r\n document.body.addEventListener(\"orientationchange\", resizeCanvas);\r\n window.addEventListener(\"resize\", resizeCanvas);\r\n window.addEventListener(\"focus\", () => {\r\n // @ts-ignore\r\n if (!ctx.running) {\r\n // @ts-ignore\r\n ctx.running = true;\r\n render();\r\n }\r\n });\r\n window.addEventListener(\"blur\", () => {\r\n // @ts-ignore\r\n ctx.running = true;\r\n });\r\n resizeCanvas();\r\n};\r\n", "type": "registry:component" } ] diff --git a/public/r/code-cursor.json b/public/r/code-cursor.json index fa6d9f6..ec646da 100644 --- a/public/r/code-cursor.json +++ b/public/r/code-cursor.json @@ -8,12 +8,12 @@ "files": [ { "path": "./src/components/nurui/code-cursor-demo.tsx", - "content": "import React from \"react\";\nimport CodeCursor from \"@/components/nurui/code-cursor\";\n\nconst CodeCursorDemo = () => {\n return (\n <>\n

\n Move cursor to see the effect.\n

\n \n \n );\n};\n\nexport default CodeCursorDemo;\n", + "content": "import React from \"react\";\r\nimport CodeCursor from \"@/components/nurui/code-cursor\";\r\n\r\nconst CodeCursorDemo = () => {\r\n return (\r\n <>\r\n

\r\n Move cursor to see the effect.\r\n

\r\n \r\n \r\n );\r\n};\r\n\r\nexport default CodeCursorDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/code-cursor.tsx", - "content": "\"use client\";\nimport React, { useEffect, useRef } from \"react\";\n\ninterface IBinaryChar {\n x: number;\n y: number;\n alpha: number;\n char: \"0\" | \"1\";\n color: string;\n update: () => void;\n draw: () => void;\n}\n\nconst CodeCursor = () => {\n const canvasRef = useRef(null);\n const particles: IBinaryChar[] = [];\n\n useEffect(() => {\n const canvas = canvasRef.current!;\n const ctx = canvas.getContext(\"2d\")!;\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n\n class BinaryChar {\n x: number;\n y: number;\n alpha: number;\n char: \"0\" | \"1\";\n color: string;\n\n constructor(x: number, y: number) {\n this.x = x;\n this.y = y;\n this.alpha = 1;\n this.char = Math.random() > 0.5 ? \"1\" : \"0\";\n\n // Assign different colors for 0 and 1\n this.color = this.char === \"0\" ? \"#00FFC6\" : \"#00B4FF\"; // mint and blue\n }\n\n update() {\n this.y -= 0.5;\n this.alpha -= 0.02;\n }\n\n draw() {\n ctx.fillStyle = `${this.color}${Math.floor(this.alpha * 255)\n .toString(16)\n .padStart(2, \"0\")}`; // Apply fading alpha as hex\n ctx.font = \"18px monospace\";\n ctx.fillText(this.char, this.x, this.y);\n }\n }\n\n const animate = () => {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n particles.forEach((p, i) => {\n p.update();\n p.draw();\n if (p.alpha <= 0) particles.splice(i, 1);\n });\n requestAnimationFrame(animate);\n };\n\n animate();\n\n const onMove = (e: MouseEvent) => {\n for (let i = 0; i < 2; i++) {\n particles.push(new BinaryChar(e.clientX, e.clientY));\n }\n };\n\n window.addEventListener(\"mousemove\", onMove);\n\n return () => {\n window.removeEventListener(\"mousemove\", onMove);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return (\n \n );\n};\n\nexport default CodeCursor;\n", + "content": "\"use client\";\r\nimport React, { useEffect, useRef } from \"react\";\r\n\r\ninterface IBinaryChar {\r\n x: number;\r\n y: number;\r\n alpha: number;\r\n char: \"0\" | \"1\";\r\n color: string;\r\n update: () => void;\r\n draw: () => void;\r\n}\r\n\r\nconst CodeCursor = () => {\r\n const canvasRef = useRef(null);\r\n const particles: IBinaryChar[] = [];\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current!;\r\n const ctx = canvas.getContext(\"2d\")!;\r\n canvas.width = window.innerWidth;\r\n canvas.height = window.innerHeight;\r\n\r\n class BinaryChar {\r\n x: number;\r\n y: number;\r\n alpha: number;\r\n char: \"0\" | \"1\";\r\n color: string;\r\n\r\n constructor(x: number, y: number) {\r\n this.x = x;\r\n this.y = y;\r\n this.alpha = 1;\r\n this.char = Math.random() > 0.5 ? \"1\" : \"0\";\r\n\r\n // Assign different colors for 0 and 1\r\n this.color = this.char === \"0\" ? \"#00FFC6\" : \"#00B4FF\"; // mint and blue\r\n }\r\n\r\n update() {\r\n this.y -= 0.5;\r\n this.alpha -= 0.02;\r\n }\r\n\r\n draw() {\r\n ctx.fillStyle = `${this.color}${Math.floor(this.alpha * 255)\r\n .toString(16)\r\n .padStart(2, \"0\")}`; // Apply fading alpha as hex\r\n ctx.font = \"18px monospace\";\r\n ctx.fillText(this.char, this.x, this.y);\r\n }\r\n }\r\n\r\n const animate = () => {\r\n ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n particles.forEach((p, i) => {\r\n p.update();\r\n p.draw();\r\n if (p.alpha <= 0) particles.splice(i, 1);\r\n });\r\n requestAnimationFrame(animate);\r\n };\r\n\r\n animate();\r\n\r\n const onMove = (e: MouseEvent) => {\r\n for (let i = 0; i < 2; i++) {\r\n particles.push(new BinaryChar(e.clientX, e.clientY));\r\n }\r\n };\r\n\r\n window.addEventListener(\"mousemove\", onMove);\r\n\r\n return () => {\r\n window.removeEventListener(\"mousemove\", onMove);\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, []);\r\n\r\n return (\r\n \r\n );\r\n};\r\n\r\nexport default CodeCursor;\r\n", "type": "registry:component" } ] diff --git a/public/r/contact-form.json b/public/r/contact-form.json index 4021681..e1f4b9f 100644 --- a/public/r/contact-form.json +++ b/public/r/contact-form.json @@ -12,27 +12,27 @@ "files": [ { "path": "./src/components/nurui/contact-form-demo.tsx", - "content": "import React from \"react\";\nimport ContactForm from \"@/components/nurui/contact-form\";\n\nconst ContactFormDemo = () => {\n return (\n
\n \n
\n );\n};\n\nexport default ContactFormDemo;\n", + "content": "import React from \"react\";\r\nimport ContactForm from \"@/components/nurui/contact-form\";\r\n\r\nconst ContactFormDemo = () => {\r\n return (\r\n
\r\n \r\n
\r\n );\r\n};\r\n\r\nexport default ContactFormDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/contact-form.tsx", - "content": "import BackgroundShineButton from \"@/components/nurui/background-shine-button\";\nimport ShinyInput from \"@/components/nurui/shiny-input\";\nimport ShinyTextArea from \"@/components/nurui/shiny-text-area\";\n\nconst ContactForm = () => {\n return (\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n \n \n );\n};\n\nexport default ContactForm;\n", + "content": "import BackgroundShineButton from \"@/components/nurui/background-shine-button\";\r\nimport ShinyInput from \"@/components/nurui/shiny-input\";\r\nimport ShinyTextArea from \"@/components/nurui/shiny-text-area\";\r\n\r\nconst ContactForm = () => {\r\n return (\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n \r\n
\r\n \r\n \r\n );\r\n};\r\n\r\nexport default ContactForm;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/background-shine-button.tsx", - "content": "import { cn } from \"@/lib/utils\";\nimport { FC } from \"react\";\n\ninterface IProps {\n title: string;\n className?: string;\n}\n\nconst BackgroundShineButton: FC = ({ title, className }) => {\n return (\n \n {title}\n \n );\n};\n\nexport default BackgroundShineButton;\n", + "content": "import { cn } from \"@/lib/utils\";\r\nimport { FC } from \"react\";\r\n\r\ninterface IProps {\r\n title: string;\r\n className?: string;\r\n}\r\n\r\nconst BackgroundShineButton: FC = ({ title, className }) => {\r\n return (\r\n \r\n {title}\r\n \r\n );\r\n};\r\n\r\nexport default BackgroundShineButton;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/shiny-input.tsx", - "content": "\"use client\";\nimport { useState, useRef, ReactNode } from \"react\";\nimport { motion, useMotionValue, useTransform } from \"framer-motion\";\nimport { cn } from \"@/lib/utils\";\n\nconst ShinyInput = ({\n className,\n icon,\n placeholder,\n type,\n name,\n required,\n borderHoverAnimation = \"1px solid var(--primary-color)\",\n focus = \"focus:border-[var(--primary-color)]\",\n}: {\n className?: string;\n icon?: ReactNode;\n placeholder: string;\n type: string;\n name: string;\n required?: boolean;\n borderHoverAnimation?: string;\n focus?: string;\n}) => {\n const divRef = useRef(null);\n const [, setIsFocused] = useState(false);\n const positionX = useMotionValue(0);\n const positionY = useMotionValue(0);\n const [opacity, setOpacity] = useState(0);\n\n const handleMouseMove = (e: React.MouseEvent) => {\n if (divRef.current) {\n const rect = divRef.current.getBoundingClientRect();\n positionX.set(e.clientX - rect.left);\n positionY.set(e.clientY - rect.top);\n }\n };\n\n const handleFocus = () => {\n setIsFocused(true);\n setOpacity(1);\n };\n\n const handleBlur = () => {\n setIsFocused(false);\n setOpacity(0);\n };\n\n const handleMouseEnter = () => setOpacity(1);\n const handleMouseLeave = () => setOpacity(0);\n\n const shineBorder = useTransform(\n [positionX, positionY],\n ([x, y]) =>\n `radial-gradient(30% 30px at ${x}px ${y}px, black 45%, transparent)`,\n );\n\n return (\n
\n \n\n \n {icon}\n
\n );\n};\n\nexport default ShinyInput;\n", + "content": "\"use client\";\r\nimport { useState, useRef, ReactNode } from \"react\";\r\nimport { motion, useMotionValue, useTransform } from \"framer-motion\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nconst ShinyInput = ({\r\n className,\r\n icon,\r\n placeholder,\r\n type,\r\n name,\r\n required,\r\n borderHoverAnimation = \"1px solid var(--primary-color)\",\r\n focus = \"focus:border-[var(--primary-color)]\",\r\n}: {\r\n className?: string;\r\n icon?: ReactNode;\r\n placeholder: string;\r\n type: string;\r\n name: string;\r\n required?: boolean;\r\n borderHoverAnimation?: string;\r\n focus?: string;\r\n}) => {\r\n const divRef = useRef(null);\r\n const [, setIsFocused] = useState(false);\r\n const positionX = useMotionValue(0);\r\n const positionY = useMotionValue(0);\r\n const [opacity, setOpacity] = useState(0);\r\n\r\n const handleMouseMove = (e: React.MouseEvent) => {\r\n if (divRef.current) {\r\n const rect = divRef.current.getBoundingClientRect();\r\n positionX.set(e.clientX - rect.left);\r\n positionY.set(e.clientY - rect.top);\r\n }\r\n };\r\n\r\n const handleFocus = () => {\r\n setIsFocused(true);\r\n setOpacity(1);\r\n };\r\n\r\n const handleBlur = () => {\r\n setIsFocused(false);\r\n setOpacity(0);\r\n };\r\n\r\n const handleMouseEnter = () => setOpacity(1);\r\n const handleMouseLeave = () => setOpacity(0);\r\n\r\n const shineBorder = useTransform(\r\n [positionX, positionY],\r\n ([x, y]) =>\r\n `radial-gradient(30% 30px at ${x}px ${y}px, black 45%, transparent)`,\r\n );\r\n\r\n return (\r\n
\r\n \r\n\r\n \r\n {icon}\r\n
\r\n );\r\n};\r\n\r\nexport default ShinyInput;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/shiny-text-area.tsx", - "content": "\"use client\";\nimport { useState, useRef, ReactNode } from \"react\";\nimport { motion, useMotionValue, useTransform } from \"framer-motion\";\nimport { cn } from \"@/lib/utils\";\n\nconst ShinyTextArea = ({\n className,\n icon,\n placeholder,\n name,\n rows = 4,\n required,\n borderHoverAnimation = \"1px solid var(--primary-color)\",\n focus = \"focus:border-[var(--primary-color)]\",\n}: {\n className?: string;\n icon?: ReactNode;\n placeholder: string;\n name: string;\n rows?: number;\n required?: boolean;\n borderHoverAnimation?: string;\n focus?: string;\n}) => {\n const divRef = useRef(null);\n const [, setIsFocused] = useState(false);\n const positionX = useMotionValue(0);\n const positionY = useMotionValue(0);\n const [opacity, setOpacity] = useState(0);\n\n const handleMouseMove = (e: React.MouseEvent) => {\n if (divRef.current) {\n const rect = divRef.current.getBoundingClientRect();\n positionX.set(e.clientX - rect.left);\n positionY.set(e.clientY - rect.top);\n }\n };\n\n const handleFocus = () => {\n setIsFocused(true);\n setOpacity(1);\n };\n\n const handleBlur = () => {\n setIsFocused(false);\n setOpacity(0);\n };\n\n const handleMouseEnter = () => setOpacity(1);\n const handleMouseLeave = () => setOpacity(0);\n\n const shineBorder = useTransform(\n [positionX, positionY],\n ([x, y]) =>\n `radial-gradient(30% 30px at ${x}px ${y}px, black 45%, transparent)`,\n );\n\n return (\n
\n \n\n \n {icon}\n
\n );\n};\n\nexport default ShinyTextArea;", + "content": "\"use client\";\r\nimport { useState, useRef, ReactNode } from \"react\";\r\nimport { motion, useMotionValue, useTransform } from \"framer-motion\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nconst ShinyTextArea = ({\r\n className,\r\n icon,\r\n placeholder,\r\n name,\r\n rows = 4,\r\n required,\r\n borderHoverAnimation = \"1px solid var(--primary-color)\",\r\n focus = \"focus:border-[var(--primary-color)]\",\r\n}: {\r\n className?: string;\r\n icon?: ReactNode;\r\n placeholder: string;\r\n name: string;\r\n rows?: number;\r\n required?: boolean;\r\n borderHoverAnimation?: string;\r\n focus?: string;\r\n}) => {\r\n const divRef = useRef(null);\r\n const [, setIsFocused] = useState(false);\r\n const positionX = useMotionValue(0);\r\n const positionY = useMotionValue(0);\r\n const [opacity, setOpacity] = useState(0);\r\n\r\n const handleMouseMove = (e: React.MouseEvent) => {\r\n if (divRef.current) {\r\n const rect = divRef.current.getBoundingClientRect();\r\n positionX.set(e.clientX - rect.left);\r\n positionY.set(e.clientY - rect.top);\r\n }\r\n };\r\n\r\n const handleFocus = () => {\r\n setIsFocused(true);\r\n setOpacity(1);\r\n };\r\n\r\n const handleBlur = () => {\r\n setIsFocused(false);\r\n setOpacity(0);\r\n };\r\n\r\n const handleMouseEnter = () => setOpacity(1);\r\n const handleMouseLeave = () => setOpacity(0);\r\n\r\n const shineBorder = useTransform(\r\n [positionX, positionY],\r\n ([x, y]) =>\r\n `radial-gradient(30% 30px at ${x}px ${y}px, black 45%, transparent)`,\r\n );\r\n\r\n return (\r\n
\r\n \r\n\r\n \r\n {icon}\r\n
\r\n );\r\n};\r\n\r\nexport default ShinyTextArea;", "type": "registry:component" } ] diff --git a/public/r/creative-pricing.json b/public/r/creative-pricing.json index 055f178..6d016e2 100644 --- a/public/r/creative-pricing.json +++ b/public/r/creative-pricing.json @@ -14,17 +14,17 @@ "files": [ { "path": "./src/components/nurui/creative-pricing-demo.tsx", - "content": "import { CreativePricing } from \"@/components/nurui/creative-pricing\";\nimport type { PricingTier } from \"@/components/nurui/creative-pricing\";\nimport { Pencil, Star, Sparkles } from \"lucide-react\";\n\nconst sampleTiers: PricingTier[] = [\n {\n name: \"Creator\",\n icon: ,\n price: 29,\n description: \"Perfect for short video beginners\",\n color: \"amber\",\n features: [\n \"60-second Video Export\",\n \"10 Trending Templates\",\n \"Auto Text-to-Speech\",\n \"Basic Transitions\",\n ],\n },\n {\n name: \"Influencer\",\n icon: ,\n price: 79,\n description: \"For serious content creators\",\n color: \"blue\",\n features: [\n \"3-minute Video Export\",\n \"Voice Effects & Filters\",\n \"Trending Sound Library\",\n \"Auto Captions & Subtitles\",\n ],\n popular: true,\n },\n {\n name: \"Pro Studio\",\n icon: ,\n price: 149,\n description: \"For viral content masters\",\n color: \"purple\",\n features: [\n \"Multi-clip Editing\",\n \"Green Screen Effects\",\n \"Viral Sound Detection\",\n \"Engagement Analytics\",\n ],\n },\n];\n\nfunction CreativePricingDemo() {\n return (\n
\n \n
\n );\n}\n\nexport { CreativePricingDemo };\n", + "content": "import { CreativePricing } from \"@/components/nurui/creative-pricing\";\r\nimport type { PricingTier } from \"@/components/nurui/creative-pricing\";\r\nimport { Pencil, Star, Sparkles } from \"lucide-react\";\r\n\r\nconst sampleTiers: PricingTier[] = [\r\n {\r\n name: \"Creator\",\r\n icon: ,\r\n price: 29,\r\n description: \"Perfect for short video beginners\",\r\n color: \"amber\",\r\n features: [\r\n \"60-second Video Export\",\r\n \"10 Trending Templates\",\r\n \"Auto Text-to-Speech\",\r\n \"Basic Transitions\",\r\n ],\r\n },\r\n {\r\n name: \"Influencer\",\r\n icon: ,\r\n price: 79,\r\n description: \"For serious content creators\",\r\n color: \"blue\",\r\n features: [\r\n \"3-minute Video Export\",\r\n \"Voice Effects & Filters\",\r\n \"Trending Sound Library\",\r\n \"Auto Captions & Subtitles\",\r\n ],\r\n popular: true,\r\n },\r\n {\r\n name: \"Pro Studio\",\r\n icon: ,\r\n price: 149,\r\n description: \"For viral content masters\",\r\n color: \"purple\",\r\n features: [\r\n \"Multi-clip Editing\",\r\n \"Green Screen Effects\",\r\n \"Viral Sound Detection\",\r\n \"Engagement Analytics\",\r\n ],\r\n },\r\n];\r\n\r\nfunction CreativePricingDemo() {\r\n return (\r\n
\r\n \r\n
\r\n );\r\n}\r\n\r\nexport { CreativePricingDemo };\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/creative-pricing.tsx", - "content": "import { Button } from \"@/components/nurui/button\";\nimport { Check } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface PricingTier {\n name: string;\n icon: React.ReactNode;\n price: number;\n description: string;\n features: string[];\n popular?: boolean;\n color: string;\n}\n\nfunction CreativePricing({\n tag = \"Simple Pricing\",\n title = \"Make Short Videos That Pop\",\n description = \"Edit, enhance, and go viral in minutes\",\n tiers,\n}: {\n tag?: string;\n title?: string;\n description?: string;\n tiers: PricingTier[];\n}) {\n return (\n
\n
\n
\n {tag}\n
\n
\n

\n {title}\n
\n โœจ\n
\n
\n โญ๏ธ\n
\n

\n \n
\n

\n {description}\n

\n
\n\n
\n {tiers.map((tier, index) => (\n \n \n\n
\n {tier.popular && (\n \n Popular!\n
\n )}\n\n
\n \n {tier.icon}\n
\n

\n {tier.name}\n

\n

\n {tier.description}\n

\n
\n\n {/* Price */}\n
\n \n ${tier.price}\n \n /month\n
\n\n
\n {tier.features.map((feature) => (\n
\n \n \n
\n \n {feature}\n \n
\n ))}\n
\n\n \n Get Started\n \n \n \n ))}\n \n
\n
โœŽ
\n
\n โœ๏ธ\n
\n
\n \n );\n}\n\nexport { CreativePricing };\n", + "content": "import { Button } from \"@/components/nurui/button\";\r\nimport { Check } from \"lucide-react\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nexport interface PricingTier {\r\n name: string;\r\n icon: React.ReactNode;\r\n price: number;\r\n description: string;\r\n features: string[];\r\n popular?: boolean;\r\n color: string;\r\n}\r\n\r\nfunction CreativePricing({\r\n tag = \"Simple Pricing\",\r\n title = \"Make Short Videos That Pop\",\r\n description = \"Edit, enhance, and go viral in minutes\",\r\n tiers,\r\n}: {\r\n tag?: string;\r\n title?: string;\r\n description?: string;\r\n tiers: PricingTier[];\r\n}) {\r\n return (\r\n
\r\n
\r\n
\r\n {tag}\r\n
\r\n
\r\n

\r\n {title}\r\n
\r\n โœจ\r\n
\r\n
\r\n โญ๏ธ\r\n
\r\n

\r\n \r\n
\r\n

\r\n {description}\r\n

\r\n
\r\n\r\n
\r\n {tiers.map((tier, index) => (\r\n \r\n \r\n\r\n
\r\n {tier.popular && (\r\n \r\n Popular!\r\n
\r\n )}\r\n\r\n
\r\n \r\n {tier.icon}\r\n
\r\n

\r\n {tier.name}\r\n

\r\n

\r\n {tier.description}\r\n

\r\n
\r\n\r\n {/* Price */}\r\n
\r\n \r\n ${tier.price}\r\n \r\n /month\r\n
\r\n\r\n
\r\n {tier.features.map((feature) => (\r\n
\r\n \r\n \r\n
\r\n \r\n {feature}\r\n \r\n
\r\n ))}\r\n
\r\n\r\n \r\n Get Started\r\n \r\n \r\n \r\n ))}\r\n \r\n
\r\n
โœŽ
\r\n
\r\n โœ๏ธ\r\n
\r\n
\r\n \r\n );\r\n}\r\n\r\nexport { CreativePricing };\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/button.tsx", - "content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline:\n \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n)\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes,\n VariantProps {\n asChild?: boolean\n}\n\nconst Button = React.forwardRef(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n return (\n \n )\n },\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n", + "content": "import * as React from \"react\"\r\nimport { Slot } from \"@radix-ui/react-slot\"\r\nimport { cva, type VariantProps } from \"class-variance-authority\"\r\n\r\nimport { cn } from \"@/lib/utils\"\r\n\r\nconst buttonVariants = cva(\r\n \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\r\n {\r\n variants: {\r\n variant: {\r\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\r\n destructive:\r\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\r\n outline:\r\n \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\r\n secondary:\r\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\r\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\r\n link: \"text-primary underline-offset-4 hover:underline\",\r\n },\r\n size: {\r\n default: \"h-10 px-4 py-2\",\r\n sm: \"h-9 rounded-md px-3\",\r\n lg: \"h-11 rounded-md px-8\",\r\n icon: \"h-10 w-10\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"default\",\r\n size: \"default\",\r\n },\r\n },\r\n)\r\n\r\nexport interface ButtonProps\r\n extends React.ButtonHTMLAttributes,\r\n VariantProps {\r\n asChild?: boolean\r\n}\r\n\r\nconst Button = React.forwardRef(\r\n ({ className, variant, size, asChild = false, ...props }, ref) => {\r\n const Comp = asChild ? Slot : \"button\"\r\n return (\r\n \r\n )\r\n },\r\n)\r\nButton.displayName = \"Button\"\r\n\r\nexport { Button, buttonVariants }\r\n", "type": "registry:component" } ] diff --git a/public/r/digital-hero.json b/public/r/digital-hero.json index de554b8..7b2458e 100644 --- a/public/r/digital-hero.json +++ b/public/r/digital-hero.json @@ -8,17 +8,17 @@ "files": [ { "path": "./src/components/nurui/digital-hero-demo.tsx", - "content": "import React from \"react\";\nimport DigitalHero from \"@/components/nurui/digital-hero\";\n\nconst DigitalHeroDemo = () => {\n return ;\n};\n\nexport default DigitalHeroDemo;\n", + "content": "import React from \"react\";\r\nimport DigitalHero from \"@/components/nurui/digital-hero\";\r\n\r\nconst DigitalHeroDemo = () => {\r\n return ;\r\n};\r\n\r\nexport default DigitalHeroDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/digital-hero.tsx", - "content": "\"use client\";\nimport Image from \"next/image\";\nimport { useEffect, useState, useRef } from \"react\";\nimport \"@/components/nurui/styles/digital-hero.css\"; // Import custom styles\n\nexport default function DigitalHero() {\n const [, setScrollPosition] = useState(0);\n const [isLoaded, setIsLoaded] = useState(false);\n const canvasRef = useRef(null);\n\n // Animation for particles\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n\n const particles: {\n x: number;\n y: number;\n size: number;\n speedX: number;\n speedY: number;\n color: string;\n alpha: number;\n }[] = [];\n\n const createParticles = () => {\n for (let i = 0; i < 50; i++) {\n particles.push({\n x: Math.random() * canvas.width,\n y: Math.random() * canvas.height,\n size: Math.random() * 2 + 0.5,\n speedX: Math.random() * 0.5 - 0.25,\n speedY: Math.random() * 0.5 - 0.25,\n color: \"#a3ff12\",\n alpha: Math.random() * 0.5 + 0.1,\n });\n }\n };\n\n const animateParticles = () => {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n for (let i = 0; i < particles.length; i++) {\n const p = particles[i];\n ctx.beginPath();\n ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);\n ctx.fillStyle = `rgba(163, 255, 18, ${p.alpha})`;\n ctx.fill();\n\n p.x += p.speedX;\n p.y += p.speedY;\n\n if (p.x > canvas.width) p.x = 0;\n if (p.x < 0) p.x = canvas.width;\n if (p.y > canvas.height) p.y = 0;\n if (p.y < 0) p.y = canvas.height;\n }\n\n requestAnimationFrame(animateParticles);\n };\n\n createParticles();\n animateParticles();\n\n const handleResize = () => {\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n };\n\n window.addEventListener(\"resize\", handleResize);\n\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n };\n }, []);\n\n // Page load animation\n useEffect(() => {\n setIsLoaded(true);\n }, []);\n\n // Scroll position tracking\n useEffect(() => {\n const handleScroll = () => {\n const position = window.scrollY;\n setScrollPosition(position);\n };\n\n window.addEventListener(\"scroll\", handleScroll);\n return () => {\n window.removeEventListener(\"scroll\", handleScroll);\n };\n }, []);\n\n return (\n
\n {/* Particle canvas */}\n \n\n {/* Green glow border effect */}\n
\n\n {/* Corner brackets */}\n
\n
\n
\n
\n\n {/* Content container */}\n
\n {/* Main content */}\n
\n \n
\n
\n \n DECENTRALIZED\n
\n
\n
\n \n NFT EXCHANGE\n
\n
\n
\n \n PLATFORM\n
\n
\n \n\n \n DIVE INTO THE WORLD OF DIGITAL ART. EASILY CREATE, SELL, AND BUY\n NFTS WITH THE SECURITY AND TRANSPARENCY OF BLOCKCHAIN TECHNOLOGY.\n

\n\n \n \n GET STARTED\n \n \n \n \n LEARN MORE\n \n \n \n \n \n\n \n {/* NFT Images */}\n
\n
\n
\n \n
\n
\n\n
\n
\n \n
\n
\n\n
\n
\n \n
\n
\n
\n\n {/* Tech lines */}\n
\n
\n \n \n \n\n {/* Background pattern */}\n
\n\n {/* Green glow overlay */}\n
\n\n {/* Scan line effect */}\n
\n
\n
\n
\n );\n}\n", + "content": "\"use client\";\r\nimport Image from \"next/image\";\r\nimport { useEffect, useState, useRef } from \"react\";\r\nimport \"@/components/nurui/styles/digital-hero.css\"; // Import custom styles\r\n\r\nexport default function DigitalHero() {\r\n const [, setScrollPosition] = useState(0);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n const canvasRef = useRef(null);\r\n\r\n // Animation for particles\r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) return;\r\n\r\n canvas.width = window.innerWidth;\r\n canvas.height = window.innerHeight;\r\n\r\n const particles: {\r\n x: number;\r\n y: number;\r\n size: number;\r\n speedX: number;\r\n speedY: number;\r\n color: string;\r\n alpha: number;\r\n }[] = [];\r\n\r\n const createParticles = () => {\r\n for (let i = 0; i < 50; i++) {\r\n particles.push({\r\n x: Math.random() * canvas.width,\r\n y: Math.random() * canvas.height,\r\n size: Math.random() * 2 + 0.5,\r\n speedX: Math.random() * 0.5 - 0.25,\r\n speedY: Math.random() * 0.5 - 0.25,\r\n color: \"#a3ff12\",\r\n alpha: Math.random() * 0.5 + 0.1,\r\n });\r\n }\r\n };\r\n\r\n const animateParticles = () => {\r\n ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n\r\n for (let i = 0; i < particles.length; i++) {\r\n const p = particles[i];\r\n ctx.beginPath();\r\n ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);\r\n ctx.fillStyle = `rgba(163, 255, 18, ${p.alpha})`;\r\n ctx.fill();\r\n\r\n p.x += p.speedX;\r\n p.y += p.speedY;\r\n\r\n if (p.x > canvas.width) p.x = 0;\r\n if (p.x < 0) p.x = canvas.width;\r\n if (p.y > canvas.height) p.y = 0;\r\n if (p.y < 0) p.y = canvas.height;\r\n }\r\n\r\n requestAnimationFrame(animateParticles);\r\n };\r\n\r\n createParticles();\r\n animateParticles();\r\n\r\n const handleResize = () => {\r\n canvas.width = window.innerWidth;\r\n canvas.height = window.innerHeight;\r\n };\r\n\r\n window.addEventListener(\"resize\", handleResize);\r\n\r\n return () => {\r\n window.removeEventListener(\"resize\", handleResize);\r\n };\r\n }, []);\r\n\r\n // Page load animation\r\n useEffect(() => {\r\n setIsLoaded(true);\r\n }, []);\r\n\r\n // Scroll position tracking\r\n useEffect(() => {\r\n const handleScroll = () => {\r\n const position = window.scrollY;\r\n setScrollPosition(position);\r\n };\r\n\r\n window.addEventListener(\"scroll\", handleScroll);\r\n return () => {\r\n window.removeEventListener(\"scroll\", handleScroll);\r\n };\r\n }, []);\r\n\r\n return (\r\n
\r\n {/* Particle canvas */}\r\n \r\n\r\n {/* Green glow border effect */}\r\n
\r\n\r\n {/* Corner brackets */}\r\n
\r\n
\r\n
\r\n
\r\n\r\n {/* Content container */}\r\n
\r\n {/* Main content */}\r\n
\r\n \r\n
\r\n
\r\n \r\n DECENTRALIZED\r\n
\r\n
\r\n
\r\n \r\n NFT EXCHANGE\r\n
\r\n
\r\n
\r\n \r\n PLATFORM\r\n
\r\n
\r\n \r\n\r\n \r\n DIVE INTO THE WORLD OF DIGITAL ART. EASILY CREATE, SELL, AND BUY\r\n NFTS WITH THE SECURITY AND TRANSPARENCY OF BLOCKCHAIN TECHNOLOGY.\r\n

\r\n\r\n \r\n \r\n GET STARTED\r\n \r\n \r\n \r\n \r\n LEARN MORE\r\n \r\n \r\n \r\n \r\n \r\n\r\n \r\n {/* NFT Images */}\r\n
\r\n
\r\n
\r\n \r\n
\r\n
\r\n\r\n
\r\n
\r\n \r\n
\r\n
\r\n\r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n\r\n {/* Tech lines */}\r\n
\r\n
\r\n \r\n \r\n \r\n\r\n {/* Background pattern */}\r\n
\r\n\r\n {/* Green glow overlay */}\r\n
\r\n\r\n {/* Scan line effect */}\r\n
\r\n
\r\n
\r\n
\r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/styles/digital-hero.css", - "content": ".grow-underline {\n animation: growUnderline 1.5s ease-in-out forwards;\n}\n\n.scanline {\n animation: scanlineAnim 4s linear infinite;\n}\n\n@keyframes growUnderline {\n 0% {\n width: 0%;\n }\n 100% {\n width: 100%;\n }\n}\n\n@keyframes scanlineAnim {\n 0% {\n transform: translateY(-100vh);\n }\n 100% {\n transform: translateY(100vh);\n }\n}", + "content": ".grow-underline {\r\n animation: growUnderline 1.5s ease-in-out forwards;\r\n}\r\n\r\n.scanline {\r\n animation: scanlineAnim 4s linear infinite;\r\n}\r\n\r\n@keyframes growUnderline {\r\n 0% {\r\n width: 0%;\r\n }\r\n 100% {\r\n width: 100%;\r\n }\r\n}\r\n\r\n@keyframes scanlineAnim {\r\n 0% {\r\n transform: translateY(-100vh);\r\n }\r\n 100% {\r\n transform: translateY(100vh);\r\n }\r\n}", "type": "registry:component" } ] diff --git a/public/r/draw-cursor.json b/public/r/draw-cursor.json index 681afdf..46c9c16 100644 --- a/public/r/draw-cursor.json +++ b/public/r/draw-cursor.json @@ -10,12 +10,12 @@ "files": [ { "path": "./src/components/nurui/draw-cursor-demo.tsx", - "content": "import DrawCursor from \"@/components/nurui/draw-cursor\";\n\nexport function DrawCursorDemo() {\n return (\n <>\n

\n Move cursor to see the effect.\n

\n \n \n );\n}\n", + "content": "import DrawCursor from \"@/components/nurui/draw-cursor\";\r\n\r\nexport function DrawCursorDemo() {\r\n return (\r\n <>\r\n

\r\n Move cursor to see the effect.\r\n

\r\n \r\n \r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/draw-cursor.tsx", - "content": "\"use client\";\n\nimport React, { useRef, useEffect } from \"react\";\nimport { gsap } from \"gsap\";\n\ntype DrawingType = \"drawOnHold\" | \"drawAlways\";\n\ninterface Props {\n strokeColor?: string;\n strokeWidth?: number;\n type: DrawingType;\n followEffect?: boolean;\n}\n\nconst DrawCursor: React.FC = ({\n strokeColor = \"#FF9900\",\n strokeWidth = 10,\n type = \"drawAlways\",\n followEffect = false,\n}) => {\n const containerRef = useRef(null);\n const svgRef = useRef(null);\n const isDrawing = useRef(false);\n const last = useRef<{ x: number | null; y: number | null }>({\n x: null,\n y: null,\n });\n\n useEffect(() => {\n const svgNS = \"http://www.w3.org/2000/svg\";\n\n // Create SVG\n const svg = document.createElementNS(svgNS, \"svg\");\n svg.style.position = \"fixed\";\n svg.style.top = \"0\";\n svg.style.left = \"0\";\n svg.style.width = \"100vw\";\n svg.style.height = \"100vh\";\n svg.style.pointerEvents = \"none\";\n svg.style.zIndex = \"9999\"; // high z-index to be above everything\n document.body.appendChild(svg);\n svgRef.current = svg;\n\n // Resize handler to keep SVG full screen\n const resizeSvg = () => {\n svg.setAttribute(\"width\", window.innerWidth.toString());\n svg.setAttribute(\"height\", window.innerHeight.toString());\n };\n resizeSvg();\n window.addEventListener(\"resize\", resizeSvg);\n\n const drawLine = (x1: number, y1: number, x2: number, y2: number) => {\n const line = document.createElementNS(svgNS, \"line\");\n line.setAttribute(\"x1\", `${x1}`);\n line.setAttribute(\"y1\", `${y1}`);\n line.setAttribute(\"x2\", `${x2}`);\n line.setAttribute(\"y2\", `${y2}`);\n line.setAttribute(\"stroke\", strokeColor);\n line.setAttribute(\"stroke-width\", `${strokeWidth}`);\n line.setAttribute(\"stroke-linecap\", \"round\");\n svg.appendChild(line);\n\n if (followEffect) {\n gsap.to(line, {\n opacity: 0,\n duration: 0.5,\n ease: \"power1.out\",\n onComplete: () => line.remove(),\n });\n }\n };\n\n const onMouseMove = (e: MouseEvent) => {\n if (type === \"drawOnHold\" && !isDrawing.current) return;\n\n const x = e.clientX;\n const y = e.clientY;\n\n if (last.current.x != null && last.current.y != null) {\n drawLine(last.current.x, last.current.y, x, y);\n }\n\n last.current = { x, y };\n };\n\n const onMouseDown = () => {\n if (type === \"drawOnHold\") isDrawing.current = true;\n };\n\n const onMouseUp = () => {\n if (type === \"drawOnHold\") isDrawing.current = false;\n last.current = { x: null, y: null }; // reset on mouse up\n };\n\n const onMouseLeave = () => {\n last.current = { x: null, y: null };\n isDrawing.current = false;\n };\n\n window.addEventListener(\"mousemove\", onMouseMove);\n window.addEventListener(\"mousedown\", onMouseDown);\n window.addEventListener(\"mouseup\", onMouseUp);\n window.addEventListener(\"mouseleave\", onMouseLeave);\n\n return () => {\n window.removeEventListener(\"mousemove\", onMouseMove);\n window.removeEventListener(\"mousedown\", onMouseDown);\n window.removeEventListener(\"mouseup\", onMouseUp);\n window.removeEventListener(\"mouseleave\", onMouseLeave);\n window.removeEventListener(\"resize\", resizeSvg);\n svg.remove();\n };\n }, [strokeColor, strokeWidth, type, followEffect]);\n\n return
;\n};\n\nexport default DrawCursor;\n", + "content": "\"use client\";\r\n\r\nimport React, { useRef, useEffect } from \"react\";\r\nimport { gsap } from \"gsap\";\r\n\r\ntype DrawingType = \"drawOnHold\" | \"drawAlways\";\r\n\r\ninterface Props {\r\n strokeColor?: string;\r\n strokeWidth?: number;\r\n type: DrawingType;\r\n followEffect?: boolean;\r\n}\r\n\r\nconst DrawCursor: React.FC = ({\r\n strokeColor = \"#FF9900\",\r\n strokeWidth = 10,\r\n type = \"drawAlways\",\r\n followEffect = false,\r\n}) => {\r\n const containerRef = useRef(null);\r\n const svgRef = useRef(null);\r\n const isDrawing = useRef(false);\r\n const last = useRef<{ x: number | null; y: number | null }>({\r\n x: null,\r\n y: null,\r\n });\r\n\r\n useEffect(() => {\r\n const svgNS = \"http://www.w3.org/2000/svg\";\r\n\r\n // Create SVG\r\n const svg = document.createElementNS(svgNS, \"svg\");\r\n svg.style.position = \"fixed\";\r\n svg.style.top = \"0\";\r\n svg.style.left = \"0\";\r\n svg.style.width = \"100vw\";\r\n svg.style.height = \"100vh\";\r\n svg.style.pointerEvents = \"none\";\r\n svg.style.zIndex = \"9999\"; // high z-index to be above everything\r\n document.body.appendChild(svg);\r\n svgRef.current = svg;\r\n\r\n // Resize handler to keep SVG full screen\r\n const resizeSvg = () => {\r\n svg.setAttribute(\"width\", window.innerWidth.toString());\r\n svg.setAttribute(\"height\", window.innerHeight.toString());\r\n };\r\n resizeSvg();\r\n window.addEventListener(\"resize\", resizeSvg);\r\n\r\n const drawLine = (x1: number, y1: number, x2: number, y2: number) => {\r\n const line = document.createElementNS(svgNS, \"line\");\r\n line.setAttribute(\"x1\", `${x1}`);\r\n line.setAttribute(\"y1\", `${y1}`);\r\n line.setAttribute(\"x2\", `${x2}`);\r\n line.setAttribute(\"y2\", `${y2}`);\r\n line.setAttribute(\"stroke\", strokeColor);\r\n line.setAttribute(\"stroke-width\", `${strokeWidth}`);\r\n line.setAttribute(\"stroke-linecap\", \"round\");\r\n svg.appendChild(line);\r\n\r\n if (followEffect) {\r\n gsap.to(line, {\r\n opacity: 0,\r\n duration: 0.5,\r\n ease: \"power1.out\",\r\n onComplete: () => line.remove(),\r\n });\r\n }\r\n };\r\n\r\n const onMouseMove = (e: MouseEvent) => {\r\n if (type === \"drawOnHold\" && !isDrawing.current) return;\r\n\r\n const x = e.clientX;\r\n const y = e.clientY;\r\n\r\n if (last.current.x != null && last.current.y != null) {\r\n drawLine(last.current.x, last.current.y, x, y);\r\n }\r\n\r\n last.current = { x, y };\r\n };\r\n\r\n const onMouseDown = () => {\r\n if (type === \"drawOnHold\") isDrawing.current = true;\r\n };\r\n\r\n const onMouseUp = () => {\r\n if (type === \"drawOnHold\") isDrawing.current = false;\r\n last.current = { x: null, y: null }; // reset on mouse up\r\n };\r\n\r\n const onMouseLeave = () => {\r\n last.current = { x: null, y: null };\r\n isDrawing.current = false;\r\n };\r\n\r\n window.addEventListener(\"mousemove\", onMouseMove);\r\n window.addEventListener(\"mousedown\", onMouseDown);\r\n window.addEventListener(\"mouseup\", onMouseUp);\r\n window.addEventListener(\"mouseleave\", onMouseLeave);\r\n\r\n return () => {\r\n window.removeEventListener(\"mousemove\", onMouseMove);\r\n window.removeEventListener(\"mousedown\", onMouseDown);\r\n window.removeEventListener(\"mouseup\", onMouseUp);\r\n window.removeEventListener(\"mouseleave\", onMouseLeave);\r\n window.removeEventListener(\"resize\", resizeSvg);\r\n svg.remove();\r\n };\r\n }, [strokeColor, strokeWidth, type, followEffect]);\r\n\r\n return
;\r\n};\r\n\r\nexport default DrawCursor;\r\n", "type": "registry:component" } ] diff --git a/public/r/dynamic-card.json b/public/r/dynamic-card.json index fc0cfd1..3ed1d51 100644 --- a/public/r/dynamic-card.json +++ b/public/r/dynamic-card.json @@ -10,17 +10,17 @@ "files": [ { "path": "./src/components/nurui/dynamic-card-demo.tsx", - "content": "import DynamicCard from \"@/components/nurui/dynamic-card\";\n\nconst DynamicCardDemo = () => {\n return (\n
\n \n
\n );\n};\n\nexport { DynamicCardDemo };\n", + "content": "import DynamicCard from \"@/components/nurui/dynamic-card\";\r\n\r\nconst DynamicCardDemo = () => {\r\n return (\r\n
\r\n \r\n
\r\n );\r\n};\r\n\r\nexport { DynamicCardDemo };\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/dynamic-card.tsx", - "content": "\"use client\";\nimport React, { FC, useEffect, useRef } from \"react\";\nimport \"./styles/dynamic-card.css\";\n\ninterface IProps {\n normalTitle: string;\n colorfulTitle: string;\n description: string;\n buttonText: string;\n}\n\nconst DynamicCard: FC = ({\n normalTitle,\n colorfulTitle,\n description,\n buttonText,\n}) => {\n const topRef = useRef(null);\n const rightRef = useRef(null);\n const bottomRef = useRef(null);\n const leftRef = useRef(null);\n\n useEffect(() => {\n const animateBorder = () => {\n const now = Date.now() / 1000;\n const speed = 0.5; // Animation speed\n\n // Calculate positions based on time\n const topX = Math.sin(now * speed) * 100;\n const rightY = Math.cos(now * speed) * 100;\n const bottomX = Math.sin(now * speed + Math.PI) * 100;\n const leftY = Math.cos(now * speed + Math.PI) * 100;\n\n // Apply positions to elements\n if (topRef.current)\n topRef.current.style.transform = `translateX(${topX}%)`;\n if (rightRef.current)\n rightRef.current.style.transform = `translateY(${rightY}%)`;\n if (bottomRef.current)\n bottomRef.current.style.transform = `translateX(${bottomX}%)`;\n if (leftRef.current)\n leftRef.current.style.transform = `translateY(${leftY}%)`;\n\n requestAnimationFrame(animateBorder);\n };\n\n const animationId = requestAnimationFrame(animateBorder);\n return () => cancelAnimationFrame(animationId);\n }, []);\n\n return (\n
\n {/* Animated border elements */}\n
\n
\n
\n\n
\n
\n
\n\n
\n
\n
\n\n
\n
\n \n\n {/* Content */}\n
\n

\n {normalTitle}{\" \"}\n \n {colorfulTitle}\n \n

\n\n

{description}

\n\n
\n {[1, 2, 3, 4].map((item) => (\n \n
\n
\n {item}\n
\n
\n

Feature {item}

\n

\n Description of feature\n

\n
\n
\n
\n ))}\n
\n\n \n \n\n {/* Decorative elements */}\n
\n
\n
\n
\n \n );\n};\n\nexport default DynamicCard;\n", + "content": "\"use client\";\r\nimport React, { FC, useEffect, useRef } from \"react\";\r\nimport \"./styles/dynamic-card.css\";\r\n\r\ninterface IProps {\r\n normalTitle: string;\r\n colorfulTitle: string;\r\n description: string;\r\n buttonText: string;\r\n}\r\n\r\nconst DynamicCard: FC = ({\r\n normalTitle,\r\n colorfulTitle,\r\n description,\r\n buttonText,\r\n}) => {\r\n const topRef = useRef(null);\r\n const rightRef = useRef(null);\r\n const bottomRef = useRef(null);\r\n const leftRef = useRef(null);\r\n\r\n useEffect(() => {\r\n const animateBorder = () => {\r\n const now = Date.now() / 1000;\r\n const speed = 0.5; // Animation speed\r\n\r\n // Calculate positions based on time\r\n const topX = Math.sin(now * speed) * 100;\r\n const rightY = Math.cos(now * speed) * 100;\r\n const bottomX = Math.sin(now * speed + Math.PI) * 100;\r\n const leftY = Math.cos(now * speed + Math.PI) * 100;\r\n\r\n // Apply positions to elements\r\n if (topRef.current)\r\n topRef.current.style.transform = `translateX(${topX}%)`;\r\n if (rightRef.current)\r\n rightRef.current.style.transform = `translateY(${rightY}%)`;\r\n if (bottomRef.current)\r\n bottomRef.current.style.transform = `translateX(${bottomX}%)`;\r\n if (leftRef.current)\r\n leftRef.current.style.transform = `translateY(${leftY}%)`;\r\n\r\n requestAnimationFrame(animateBorder);\r\n };\r\n\r\n const animationId = requestAnimationFrame(animateBorder);\r\n return () => cancelAnimationFrame(animationId);\r\n }, []);\r\n\r\n return (\r\n
\r\n {/* Animated border elements */}\r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n \r\n\r\n
\r\n
\r\n \r\n\r\n
\r\n
\r\n \r\n\r\n {/* Content */}\r\n
\r\n

\r\n {normalTitle}{\" \"}\r\n \r\n {colorfulTitle}\r\n \r\n

\r\n\r\n

{description}

\r\n\r\n
\r\n {[1, 2, 3, 4].map((item) => (\r\n \r\n
\r\n
\r\n {item}\r\n
\r\n
\r\n

Feature {item}

\r\n

\r\n Description of feature\r\n

\r\n
\r\n
\r\n
\r\n ))}\r\n
\r\n\r\n \r\n \r\n\r\n {/* Decorative elements */}\r\n
\r\n
\r\n
\r\n
\r\n \r\n );\r\n};\r\n\r\nexport default DynamicCard;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/styles/dynamic-card.css", - "content": "/* @import \"tailwindcss\"; */\n@import \"tw-animate-css\";\n\n@keyframes float {\n 0% {\n transform: translateY(0px);\n }\n 50% {\n transform: translateY(-10px);\n }\n 100% {\n transform: translateY(0px);\n }\n}", + "content": "/* @import \"tailwindcss\"; */\r\n@import \"tw-animate-css\";\r\n\r\n@keyframes float {\r\n 0% {\r\n transform: translateY(0px);\r\n }\r\n 50% {\r\n transform: translateY(-10px);\r\n }\r\n 100% {\r\n transform: translateY(0px);\r\n }\r\n}", "type": "registry:component" } ] diff --git a/public/r/electric-cursor.json b/public/r/electric-cursor.json index b495584..f9ef1e2 100644 --- a/public/r/electric-cursor.json +++ b/public/r/electric-cursor.json @@ -8,12 +8,12 @@ "files": [ { "path": "./src/components/nurui/electric-cursor-demo.tsx", - "content": "import ElectricCursor from \"@/components/nurui/electric-cursor\";\n\nconst ElectricCursorDemo = () => {\n return (\n <>\n

\n Move cursor to see the effect.\n

\n \n \n );\n};\n\nexport default ElectricCursorDemo;\n", + "content": "import ElectricCursor from \"@/components/nurui/electric-cursor\";\r\n\r\nconst ElectricCursorDemo = () => {\r\n return (\r\n <>\r\n

\r\n Move cursor to see the effect.\r\n

\r\n \r\n \r\n );\r\n};\r\n\r\nexport default ElectricCursorDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/electric-cursor.tsx", - "content": "\"use client\";\nimport React, { useEffect, useRef } from \"react\";\n\n// Interface for Spark instance\ninterface ISpark {\n x: number;\n y: number;\n length: number;\n alpha: number;\n update: () => void;\n draw: () => void;\n}\n\nconst ElectricCursor: React.FC = () => {\n const canvasRef = useRef(null);\n\n useEffect(() => {\n const sparks: ISpark[] = [];\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n\n class Spark implements ISpark {\n x: number;\n y: number;\n length: number;\n alpha: number;\n\n constructor(x: number, y: number) {\n this.x = x;\n this.y = y;\n this.length = Math.random() * 10 + 5;\n this.alpha = 1;\n }\n\n update(): void {\n this.length -= 0.5;\n this.alpha -= 0.03;\n }\n\n draw(): void {\n if (!ctx) return;\n ctx.beginPath();\n ctx.moveTo(this.x, this.y);\n ctx.lineTo(\n this.x + Math.random() * this.length,\n this.y + Math.random() * this.length,\n );\n ctx.strokeStyle = `rgba(0, 200, 255, ${this.alpha})`;\n ctx.lineWidth = 1;\n ctx.stroke();\n }\n }\n\n const animate = (): void => {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n for (let i = sparks.length - 1; i >= 0; i--) {\n sparks[i].update();\n sparks[i].draw();\n if (sparks[i].alpha <= 0) {\n sparks.splice(i, 1);\n }\n }\n\n requestAnimationFrame(animate);\n };\n\n const onMove = (e: MouseEvent): void => {\n for (let i = 0; i < 3; i++) {\n sparks.push(new Spark(e.clientX, e.clientY));\n }\n };\n\n window.addEventListener(\"mousemove\", onMove);\n animate();\n\n return () => {\n window.removeEventListener(\"mousemove\", onMove);\n };\n }, []);\n\n return (\n \n );\n};\n\nexport default ElectricCursor;\n", + "content": "\"use client\";\r\nimport React, { useEffect, useRef } from \"react\";\r\n\r\n// Interface for Spark instance\r\ninterface ISpark {\r\n x: number;\r\n y: number;\r\n length: number;\r\n alpha: number;\r\n update: () => void;\r\n draw: () => void;\r\n}\r\n\r\nconst ElectricCursor: React.FC = () => {\r\n const canvasRef = useRef(null);\r\n\r\n useEffect(() => {\r\n const sparks: ISpark[] = [];\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) return;\r\n\r\n canvas.width = window.innerWidth;\r\n canvas.height = window.innerHeight;\r\n\r\n class Spark implements ISpark {\r\n x: number;\r\n y: number;\r\n length: number;\r\n alpha: number;\r\n\r\n constructor(x: number, y: number) {\r\n this.x = x;\r\n this.y = y;\r\n this.length = Math.random() * 10 + 5;\r\n this.alpha = 1;\r\n }\r\n\r\n update(): void {\r\n this.length -= 0.5;\r\n this.alpha -= 0.03;\r\n }\r\n\r\n draw(): void {\r\n if (!ctx) return;\r\n ctx.beginPath();\r\n ctx.moveTo(this.x, this.y);\r\n ctx.lineTo(\r\n this.x + Math.random() * this.length,\r\n this.y + Math.random() * this.length,\r\n );\r\n ctx.strokeStyle = `rgba(0, 200, 255, ${this.alpha})`;\r\n ctx.lineWidth = 1;\r\n ctx.stroke();\r\n }\r\n }\r\n\r\n const animate = (): void => {\r\n ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n\r\n for (let i = sparks.length - 1; i >= 0; i--) {\r\n sparks[i].update();\r\n sparks[i].draw();\r\n if (sparks[i].alpha <= 0) {\r\n sparks.splice(i, 1);\r\n }\r\n }\r\n\r\n requestAnimationFrame(animate);\r\n };\r\n\r\n const onMove = (e: MouseEvent): void => {\r\n for (let i = 0; i < 3; i++) {\r\n sparks.push(new Spark(e.clientX, e.clientY));\r\n }\r\n };\r\n\r\n window.addEventListener(\"mousemove\", onMove);\r\n animate();\r\n\r\n return () => {\r\n window.removeEventListener(\"mousemove\", onMove);\r\n };\r\n }, []);\r\n\r\n return (\r\n \r\n );\r\n};\r\n\r\nexport default ElectricCursor;\r\n", "type": "registry:component" } ] diff --git a/public/r/fire-cursor.json b/public/r/fire-cursor.json index f7a40b2..ae06938 100644 --- a/public/r/fire-cursor.json +++ b/public/r/fire-cursor.json @@ -8,12 +8,12 @@ "files": [ { "path": "./src/components/nurui/fire-cursor-demo.tsx", - "content": "import React from \"react\";\nimport FireCursor from \"@/components/nurui/fire-cursor\";\n\nconst FireCursorDemo = () => {\n return (\n <>\n

\n Move cursor to see the effect.\n

\n \n \n );\n};\n\nexport default FireCursorDemo;\n", + "content": "import React from \"react\";\r\nimport FireCursor from \"@/components/nurui/fire-cursor\";\r\n\r\nconst FireCursorDemo = () => {\r\n return (\r\n <>\r\n

\r\n Move cursor to see the effect.\r\n

\r\n \r\n \r\n );\r\n};\r\n\r\nexport default FireCursorDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/fire-cursor.tsx", - "content": "\"use client\";\nimport React, { useEffect, useRef } from \"react\";\n\nconst FireCursor = () => {\n const canvasRef = useRef(null);\n\n useEffect(() => {\n // Particle will be defined inside useEffect, so we need to use a type assertion here\n const particles: Array<{ update: () => void; draw: () => void; alpha: number }> = [];\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n\n class Particle {\n x: number;\n y: number;\n vx: number;\n vy: number;\n alpha: number;\n size: number;\n\n constructor(x: number, y: number) {\n this.x = x;\n this.y = y;\n this.vx = (Math.random() - 0.5) * 1.5;\n this.vy = -Math.random() * 2;\n this.alpha = 1;\n this.size = Math.random() * 3 + 2;\n }\n\n update() {\n this.x += this.vx;\n this.y += this.vy;\n this.alpha -= 0.02;\n }\n\n draw() {\n if (!ctx) return;\n ctx.beginPath();\n ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);\n ctx.fillStyle = `rgba(255, ${Math.floor(Math.random() * 100)}, 0, ${this.alpha})`;\n ctx.fill();\n }\n }\n\n const animate = () => {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n particles.forEach((p, i) => {\n p.update();\n p.draw();\n if (p.alpha <= 0) particles.splice(i, 1);\n });\n requestAnimationFrame(animate);\n };\n\n animate();\n\n const onMove = (e: MouseEvent) => {\n for (let i = 0; i < 5; i++) {\n particles.push(new Particle(e.clientX, e.clientY));\n }\n };\n\n window.addEventListener(\"mousemove\", onMove);\n\n return () => {\n window.removeEventListener(\"mousemove\", onMove);\n };\n }, []);\n\n return (\n \n );\n};\n\nexport default FireCursor;\n", + "content": "\"use client\";\r\nimport React, { useEffect, useRef } from \"react\";\r\n\r\nconst FireCursor = () => {\r\n const canvasRef = useRef(null);\r\n\r\n useEffect(() => {\r\n // Particle will be defined inside useEffect, so we need to use a type assertion here\r\n const particles: Array<{ update: () => void; draw: () => void; alpha: number }> = [];\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) return;\r\n\r\n canvas.width = window.innerWidth;\r\n canvas.height = window.innerHeight;\r\n\r\n class Particle {\r\n x: number;\r\n y: number;\r\n vx: number;\r\n vy: number;\r\n alpha: number;\r\n size: number;\r\n\r\n constructor(x: number, y: number) {\r\n this.x = x;\r\n this.y = y;\r\n this.vx = (Math.random() - 0.5) * 1.5;\r\n this.vy = -Math.random() * 2;\r\n this.alpha = 1;\r\n this.size = Math.random() * 3 + 2;\r\n }\r\n\r\n update() {\r\n this.x += this.vx;\r\n this.y += this.vy;\r\n this.alpha -= 0.02;\r\n }\r\n\r\n draw() {\r\n if (!ctx) return;\r\n ctx.beginPath();\r\n ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);\r\n ctx.fillStyle = `rgba(255, ${Math.floor(Math.random() * 100)}, 0, ${this.alpha})`;\r\n ctx.fill();\r\n }\r\n }\r\n\r\n const animate = () => {\r\n ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n particles.forEach((p, i) => {\r\n p.update();\r\n p.draw();\r\n if (p.alpha <= 0) particles.splice(i, 1);\r\n });\r\n requestAnimationFrame(animate);\r\n };\r\n\r\n animate();\r\n\r\n const onMove = (e: MouseEvent) => {\r\n for (let i = 0; i < 5; i++) {\r\n particles.push(new Particle(e.clientX, e.clientY));\r\n }\r\n };\r\n\r\n window.addEventListener(\"mousemove\", onMove);\r\n\r\n return () => {\r\n window.removeEventListener(\"mousemove\", onMove);\r\n };\r\n }, []);\r\n\r\n return (\r\n \r\n );\r\n};\r\n\r\nexport default FireCursor;\r\n", "type": "registry:component" } ] diff --git a/public/r/flow-form.json b/public/r/flow-form.json index 04b2f9a..adcce44 100644 --- a/public/r/flow-form.json +++ b/public/r/flow-form.json @@ -13,12 +13,12 @@ "files": [ { "path": "./src/components/nurui/flow-form-demo.tsx", - "content": "import { SignInPage } from \"@/components/nurui/flow-form\";\n\nconst FlowFormDemo = () => {\n return (\n
\n \n
\n );\n};\n\nexport { FlowFormDemo };\n", + "content": "import { SignInPage } from \"@/components/nurui/flow-form\";\r\n\r\nconst FlowFormDemo = () => {\r\n return (\r\n
\r\n \r\n
\r\n );\r\n};\r\n\r\nexport { FlowFormDemo };\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/flow-form.tsx", - "content": "\"use client\";\n\nimport React, { useState, useMemo, useRef, useEffect } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport Link from \"next/link\";\nimport { cn } from \"@/lib/utils\";\nimport { Canvas, useFrame, useThree } from \"@react-three/fiber\";\n\nimport * as THREE from \"three\";\n\ntype Uniforms = {\n [key: string]: {\n value: number[] | number[][] | number;\n type: string;\n };\n};\n\ninterface ShaderProps {\n source: string;\n uniforms: {\n [key: string]: {\n value: number[] | number[][] | number;\n type: string;\n };\n };\n maxFps?: number;\n}\n\ninterface SignInPageProps {\n className?: string;\n}\n\nexport const CanvasRevealEffect = ({\n animationSpeed = 10,\n opacities = [0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.8, 0.8, 0.8, 1],\n colors = [[0, 255, 255]],\n containerClassName,\n dotSize,\n showGradient = true,\n reverse = false, // This controls the direction\n}: {\n animationSpeed?: number;\n opacities?: number[];\n colors?: number[][];\n containerClassName?: string;\n dotSize?: number;\n showGradient?: boolean;\n reverse?: boolean; // This prop determines the direction\n}) => {\n return (\n
\n {\" \"}\n {/* Removed bg-white */}\n
\n \n
\n {showGradient && (\n // Adjust gradient colors if needed based on background (was bg-white, now likely uses containerClassName bg)\n // Example assuming a dark background like the SignInPage uses:\n
\n )}\n
\n );\n};\n\ninterface DotMatrixProps {\n colors?: number[][];\n opacities?: number[];\n totalSize?: number;\n dotSize?: number;\n shader?: string;\n center?: (\"x\" | \"y\")[];\n}\n\nconst DotMatrix: React.FC = ({\n colors = [[0, 0, 0]],\n opacities = [0.04, 0.04, 0.04, 0.04, 0.04, 0.08, 0.08, 0.08, 0.08, 0.14],\n totalSize = 20,\n dotSize = 2,\n shader = \"\", // This shader string will now contain the animation logic\n center = [\"x\", \"y\"],\n}) => {\n // ... uniforms calculation remains the same for colors, opacities, etc.\n const uniforms = React.useMemo(() => {\n let colorsArray = [\n colors[0],\n colors[0],\n colors[0],\n colors[0],\n colors[0],\n colors[0],\n ];\n if (colors.length === 2) {\n colorsArray = [\n colors[0],\n colors[0],\n colors[0],\n colors[1],\n colors[1],\n colors[1],\n ];\n } else if (colors.length === 3) {\n colorsArray = [\n colors[0],\n colors[0],\n colors[1],\n colors[1],\n colors[2],\n colors[2],\n ];\n }\n return {\n u_colors: {\n value: colorsArray.map((color) => [\n color[0] / 255,\n color[1] / 255,\n color[2] / 255,\n ]),\n type: \"uniform3fv\",\n },\n u_opacities: {\n value: opacities,\n type: \"uniform1fv\",\n },\n u_total_size: {\n value: totalSize,\n type: \"uniform1f\",\n },\n u_dot_size: {\n value: dotSize,\n type: \"uniform1f\",\n },\n u_reverse: {\n value: shader.includes(\"u_reverse_active\") ? 1 : 0, // Convert boolean to number (1 or 0)\n type: \"uniform1i\", // Use 1i for bool in WebGL1/GLSL100, or just bool for GLSL300+ if supported\n },\n };\n }, [colors, opacities, totalSize, dotSize, shader]); // Add shader to dependencies\n\n return (\n \n );\n};\n\nconst ShaderMaterial = ({\n source,\n uniforms,\n}: {\n source: string;\n hovered?: boolean;\n maxFps?: number;\n uniforms: Uniforms;\n}) => {\n const { size } = useThree();\n const ref = useRef(null);\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n let lastFrameTime = 0;\n\n useFrame(({ clock }) => {\n if (!ref.current) return;\n const timestamp = clock.getElapsedTime();\n\n lastFrameTime = timestamp;\n\n const material = ref.current.material as THREE.ShaderMaterial;\n const timeLocation = material.uniforms.u_time;\n timeLocation.value = timestamp;\n });\n\n const getUniforms = () => {\n const preparedUniforms: Record =\n {};\n\n for (const uniformName in uniforms) {\n const uniform = uniforms[uniformName];\n\n switch (uniform.type) {\n case \"uniform1f\":\n preparedUniforms[uniformName] = { value: uniform.value, type: \"1f\" };\n break;\n case \"uniform1i\":\n preparedUniforms[uniformName] = { value: uniform.value, type: \"1i\" };\n break;\n case \"uniform3f\":\n preparedUniforms[uniformName] = {\n value: new THREE.Vector3().fromArray(uniform.value as number[]),\n type: \"3f\",\n };\n break;\n case \"uniform1fv\":\n preparedUniforms[uniformName] = { value: uniform.value, type: \"1fv\" };\n break;\n case \"uniform3fv\":\n preparedUniforms[uniformName] = {\n value: Array.isArray(uniform.value)\n ? (uniform.value as number[][]).map((v: number[]) =>\n new THREE.Vector3().fromArray(v),\n )\n : [],\n type: \"3fv\",\n };\n break;\n case \"uniform2f\":\n preparedUniforms[uniformName] = {\n value: new THREE.Vector2().fromArray(uniform.value as number[]),\n type: \"2f\",\n };\n break;\n default:\n console.error(`Invalid uniform type for '${uniformName}'.`);\n break;\n }\n }\n\n preparedUniforms[\"u_time\"] = { value: 0, type: \"1f\" };\n preparedUniforms[\"u_resolution\"] = {\n value: new THREE.Vector2(size.width * 2, size.height * 2),\n }; // Initialize u_resolution\n return preparedUniforms;\n };\n\n // Shader material\n const material = useMemo(() => {\n const materialObject = new THREE.ShaderMaterial({\n vertexShader: `\n precision mediump float;\n in vec2 coordinates;\n uniform vec2 u_resolution;\n out vec2 fragCoord;\n void main(){\n float x = position.x;\n float y = position.y;\n gl_Position = vec4(x, y, 0.0, 1.0);\n fragCoord = (position.xy + vec2(1.0)) * 0.5 * u_resolution;\n fragCoord.y = u_resolution.y - fragCoord.y;\n }\n `,\n fragmentShader: source,\n uniforms: getUniforms(),\n glslVersion: THREE.GLSL3,\n blending: THREE.CustomBlending,\n blendSrc: THREE.SrcAlphaFactor,\n blendDst: THREE.OneFactor,\n });\n\n return materialObject;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [size.width, size.height, source]);\n\n return (\n \n \n \n \n );\n};\n\nconst Shader: React.FC = ({ source, uniforms, maxFps = 60 }) => {\n return (\n \n \n \n );\n};\n\nexport const SignInPage = ({ className }: SignInPageProps) => {\n const [email, setEmail] = useState(\"\");\n const [step, setStep] = useState<\"email\" | \"code\" | \"success\">(\"email\");\n const [code, setCode] = useState([\"\", \"\", \"\", \"\", \"\", \"\"]);\n const codeInputRefs = useRef<(HTMLInputElement | null)[]>([]);\n const [initialCanvasVisible, setInitialCanvasVisible] = useState(true);\n const [reverseCanvasVisible, setReverseCanvasVisible] = useState(false);\n\n const handleEmailSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (email) {\n setStep(\"code\");\n }\n };\n\n // Focus first input when code screen appears\n useEffect(() => {\n if (step === \"code\") {\n setTimeout(() => {\n codeInputRefs.current[0]?.focus();\n }, 500);\n }\n }, [step]);\n\n const handleCodeChange = (index: number, value: string) => {\n if (value.length <= 1) {\n const newCode = [...code];\n newCode[index] = value;\n setCode(newCode);\n\n // Focus next input if value is entered\n if (value && index < 5) {\n codeInputRefs.current[index + 1]?.focus();\n }\n\n // Check if code is complete\n if (index === 5 && value) {\n const isComplete = newCode.every((digit) => digit.length === 1);\n if (isComplete) {\n // First show the new reverse canvas\n setReverseCanvasVisible(true);\n\n // Then hide the original canvas after a small delay\n setTimeout(() => {\n setInitialCanvasVisible(false);\n }, 50);\n\n // Transition to success screen after animation\n setTimeout(() => {\n setStep(\"success\");\n }, 2000);\n }\n }\n }\n };\n\n const handleKeyDown = (\n index: number,\n e: React.KeyboardEvent,\n ) => {\n if (e.key === \"Backspace\" && !code[index] && index > 0) {\n codeInputRefs.current[index - 1]?.focus();\n }\n };\n\n const handleBackClick = () => {\n setStep(\"email\");\n setCode([\"\", \"\", \"\", \"\", \"\", \"\"]);\n // Reset animations if going back\n setReverseCanvasVisible(false);\n setInitialCanvasVisible(true);\n };\n\n return (\n \n
\n {/* Initial canvas (forward animation) */}\n {initialCanvasVisible && (\n
\n \n
\n )}\n\n {/* Reverse canvas (appears when code is complete) */}\n {reverseCanvasVisible && (\n
\n \n
\n )}\n\n
\n
\n
\n\n {/* Content Layer */}\n
\n {/* Main content container */}\n
\n {/* Left side (form) */}\n
\n
\n \n {step === \"email\" ? (\n \n
\n

\n Welcome Developer\n

\n

\n Your sign in component\n

\n
\n\n
\n \n\n
\n
\n or\n
\n
\n\n
\n
\n setEmail(e.target.value)}\n className=\"w-full backdrop-blur-[1px] text-white border-1 border-white/10 rounded-full py-3 px-4 focus:outline-none focus:border focus:border-white/30 text-center\"\n required\n />\n \n \n \n โ†’\n \n \n โ†’\n \n \n \n
\n
\n
\n\n

\n By signing up, you agree to the{\" \"}\n \n MSA\n \n ,{\" \"}\n \n Product Terms\n \n ,{\" \"}\n \n Policies\n \n ,{\" \"}\n \n Privacy Notice\n \n , and{\" \"}\n \n Cookie Notice\n \n .\n

\n \n ) : step === \"code\" ? (\n \n
\n

\n We sent you a code\n

\n

\n Please enter it\n

\n
\n\n
\n
\n
\n {code.map((digit, i) => (\n
\n
\n {\n codeInputRefs.current[i] = el;\n }}\n type=\"text\"\n inputMode=\"numeric\"\n pattern=\"[0-9]*\"\n maxLength={1}\n value={digit}\n onChange={(e) =>\n handleCodeChange(i, e.target.value)\n }\n onKeyDown={(e) => handleKeyDown(i, e)}\n className=\"w-8 text-center text-xl bg-transparent text-white border-none focus:outline-none focus:ring-0 appearance-none\"\n style={{ caretColor: \"transparent\" }}\n />\n {!digit && (\n
\n \n 0\n \n
\n )}\n
\n {i < 5 && (\n |\n )}\n
\n ))}\n
\n
\n
\n\n
\n \n Resend code\n \n
\n\n
\n \n Back\n \n d !== \"\")\n ? \"bg-white text-black border-transparent hover:bg-white/90 cursor-pointer\"\n : \"bg-[#111] text-white/50 border-white/10 cursor-not-allowed\"\n }`}\n disabled={!code.every((d) => d !== \"\")}\n >\n Continue\n \n
\n\n
\n

\n By signing up, you agree to the{\" \"}\n \n MSA\n \n ,{\" \"}\n \n Product Terms\n \n ,{\" \"}\n \n Policies\n \n ,{\" \"}\n \n Privacy Notice\n \n , and{\" \"}\n \n Cookie Notice\n \n .\n

\n
\n \n ) : (\n \n
\n

\n You're in!\n

\n

\n Welcome\n

\n
\n\n \n
\n \n \n \n
\n \n\n \n Continue to Dashboard\n \n \n )}\n \n
\n
\n
\n
\n
\n );\n};\n", + "content": "\"use client\";\r\n\r\nimport React, { useState, useMemo, useRef, useEffect } from \"react\";\r\nimport { motion, AnimatePresence } from \"framer-motion\";\r\nimport Link from \"next/link\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Canvas, useFrame, useThree } from \"@react-three/fiber\";\r\n\r\nimport * as THREE from \"three\";\r\n\r\ntype Uniforms = {\r\n [key: string]: {\r\n value: number[] | number[][] | number;\r\n type: string;\r\n };\r\n};\r\n\r\ninterface ShaderProps {\r\n source: string;\r\n uniforms: {\r\n [key: string]: {\r\n value: number[] | number[][] | number;\r\n type: string;\r\n };\r\n };\r\n maxFps?: number;\r\n}\r\n\r\ninterface SignInPageProps {\r\n className?: string;\r\n}\r\n\r\nexport const CanvasRevealEffect = ({\r\n animationSpeed = 10,\r\n opacities = [0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.8, 0.8, 0.8, 1],\r\n colors = [[0, 255, 255]],\r\n containerClassName,\r\n dotSize,\r\n showGradient = true,\r\n reverse = false, // This controls the direction\r\n}: {\r\n animationSpeed?: number;\r\n opacities?: number[];\r\n colors?: number[][];\r\n containerClassName?: string;\r\n dotSize?: number;\r\n showGradient?: boolean;\r\n reverse?: boolean; // This prop determines the direction\r\n}) => {\r\n return (\r\n
\r\n {\" \"}\r\n {/* Removed bg-white */}\r\n
\r\n \r\n
\r\n {showGradient && (\r\n // Adjust gradient colors if needed based on background (was bg-white, now likely uses containerClassName bg)\r\n // Example assuming a dark background like the SignInPage uses:\r\n
\r\n )}\r\n
\r\n );\r\n};\r\n\r\ninterface DotMatrixProps {\r\n colors?: number[][];\r\n opacities?: number[];\r\n totalSize?: number;\r\n dotSize?: number;\r\n shader?: string;\r\n center?: (\"x\" | \"y\")[];\r\n}\r\n\r\nconst DotMatrix: React.FC = ({\r\n colors = [[0, 0, 0]],\r\n opacities = [0.04, 0.04, 0.04, 0.04, 0.04, 0.08, 0.08, 0.08, 0.08, 0.14],\r\n totalSize = 20,\r\n dotSize = 2,\r\n shader = \"\", // This shader string will now contain the animation logic\r\n center = [\"x\", \"y\"],\r\n}) => {\r\n // ... uniforms calculation remains the same for colors, opacities, etc.\r\n const uniforms = React.useMemo(() => {\r\n let colorsArray = [\r\n colors[0],\r\n colors[0],\r\n colors[0],\r\n colors[0],\r\n colors[0],\r\n colors[0],\r\n ];\r\n if (colors.length === 2) {\r\n colorsArray = [\r\n colors[0],\r\n colors[0],\r\n colors[0],\r\n colors[1],\r\n colors[1],\r\n colors[1],\r\n ];\r\n } else if (colors.length === 3) {\r\n colorsArray = [\r\n colors[0],\r\n colors[0],\r\n colors[1],\r\n colors[1],\r\n colors[2],\r\n colors[2],\r\n ];\r\n }\r\n return {\r\n u_colors: {\r\n value: colorsArray.map((color) => [\r\n color[0] / 255,\r\n color[1] / 255,\r\n color[2] / 255,\r\n ]),\r\n type: \"uniform3fv\",\r\n },\r\n u_opacities: {\r\n value: opacities,\r\n type: \"uniform1fv\",\r\n },\r\n u_total_size: {\r\n value: totalSize,\r\n type: \"uniform1f\",\r\n },\r\n u_dot_size: {\r\n value: dotSize,\r\n type: \"uniform1f\",\r\n },\r\n u_reverse: {\r\n value: shader.includes(\"u_reverse_active\") ? 1 : 0, // Convert boolean to number (1 or 0)\r\n type: \"uniform1i\", // Use 1i for bool in WebGL1/GLSL100, or just bool for GLSL300+ if supported\r\n },\r\n };\r\n }, [colors, opacities, totalSize, dotSize, shader]); // Add shader to dependencies\r\n\r\n return (\r\n \r\n );\r\n};\r\n\r\nconst ShaderMaterial = ({\r\n source,\r\n uniforms,\r\n}: {\r\n source: string;\r\n hovered?: boolean;\r\n maxFps?: number;\r\n uniforms: Uniforms;\r\n}) => {\r\n const { size } = useThree();\r\n const ref = useRef(null);\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n let lastFrameTime = 0;\r\n\r\n useFrame(({ clock }) => {\r\n if (!ref.current) return;\r\n const timestamp = clock.getElapsedTime();\r\n\r\n lastFrameTime = timestamp;\r\n\r\n const material = ref.current.material as THREE.ShaderMaterial;\r\n const timeLocation = material.uniforms.u_time;\r\n timeLocation.value = timestamp;\r\n });\r\n\r\n const getUniforms = () => {\r\n const preparedUniforms: Record =\r\n {};\r\n\r\n for (const uniformName in uniforms) {\r\n const uniform = uniforms[uniformName];\r\n\r\n switch (uniform.type) {\r\n case \"uniform1f\":\r\n preparedUniforms[uniformName] = { value: uniform.value, type: \"1f\" };\r\n break;\r\n case \"uniform1i\":\r\n preparedUniforms[uniformName] = { value: uniform.value, type: \"1i\" };\r\n break;\r\n case \"uniform3f\":\r\n preparedUniforms[uniformName] = {\r\n value: new THREE.Vector3().fromArray(uniform.value as number[]),\r\n type: \"3f\",\r\n };\r\n break;\r\n case \"uniform1fv\":\r\n preparedUniforms[uniformName] = { value: uniform.value, type: \"1fv\" };\r\n break;\r\n case \"uniform3fv\":\r\n preparedUniforms[uniformName] = {\r\n value: Array.isArray(uniform.value)\r\n ? (uniform.value as number[][]).map((v: number[]) =>\r\n new THREE.Vector3().fromArray(v),\r\n )\r\n : [],\r\n type: \"3fv\",\r\n };\r\n break;\r\n case \"uniform2f\":\r\n preparedUniforms[uniformName] = {\r\n value: new THREE.Vector2().fromArray(uniform.value as number[]),\r\n type: \"2f\",\r\n };\r\n break;\r\n default:\r\n console.error(`Invalid uniform type for '${uniformName}'.`);\r\n break;\r\n }\r\n }\r\n\r\n preparedUniforms[\"u_time\"] = { value: 0, type: \"1f\" };\r\n preparedUniforms[\"u_resolution\"] = {\r\n value: new THREE.Vector2(size.width * 2, size.height * 2),\r\n }; // Initialize u_resolution\r\n return preparedUniforms;\r\n };\r\n\r\n // Shader material\r\n const material = useMemo(() => {\r\n const materialObject = new THREE.ShaderMaterial({\r\n vertexShader: `\r\n precision mediump float;\r\n in vec2 coordinates;\r\n uniform vec2 u_resolution;\r\n out vec2 fragCoord;\r\n void main(){\r\n float x = position.x;\r\n float y = position.y;\r\n gl_Position = vec4(x, y, 0.0, 1.0);\r\n fragCoord = (position.xy + vec2(1.0)) * 0.5 * u_resolution;\r\n fragCoord.y = u_resolution.y - fragCoord.y;\r\n }\r\n `,\r\n fragmentShader: source,\r\n uniforms: getUniforms(),\r\n glslVersion: THREE.GLSL3,\r\n blending: THREE.CustomBlending,\r\n blendSrc: THREE.SrcAlphaFactor,\r\n blendDst: THREE.OneFactor,\r\n });\r\n\r\n return materialObject;\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [size.width, size.height, source]);\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nconst Shader: React.FC = ({ source, uniforms, maxFps = 60 }) => {\r\n return (\r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport const SignInPage = ({ className }: SignInPageProps) => {\r\n const [email, setEmail] = useState(\"\");\r\n const [step, setStep] = useState<\"email\" | \"code\" | \"success\">(\"email\");\r\n const [code, setCode] = useState([\"\", \"\", \"\", \"\", \"\", \"\"]);\r\n const codeInputRefs = useRef<(HTMLInputElement | null)[]>([]);\r\n const [initialCanvasVisible, setInitialCanvasVisible] = useState(true);\r\n const [reverseCanvasVisible, setReverseCanvasVisible] = useState(false);\r\n\r\n const handleEmailSubmit = (e: React.FormEvent) => {\r\n e.preventDefault();\r\n if (email) {\r\n setStep(\"code\");\r\n }\r\n };\r\n\r\n // Focus first input when code screen appears\r\n useEffect(() => {\r\n if (step === \"code\") {\r\n setTimeout(() => {\r\n codeInputRefs.current[0]?.focus();\r\n }, 500);\r\n }\r\n }, [step]);\r\n\r\n const handleCodeChange = (index: number, value: string) => {\r\n if (value.length <= 1) {\r\n const newCode = [...code];\r\n newCode[index] = value;\r\n setCode(newCode);\r\n\r\n // Focus next input if value is entered\r\n if (value && index < 5) {\r\n codeInputRefs.current[index + 1]?.focus();\r\n }\r\n\r\n // Check if code is complete\r\n if (index === 5 && value) {\r\n const isComplete = newCode.every((digit) => digit.length === 1);\r\n if (isComplete) {\r\n // First show the new reverse canvas\r\n setReverseCanvasVisible(true);\r\n\r\n // Then hide the original canvas after a small delay\r\n setTimeout(() => {\r\n setInitialCanvasVisible(false);\r\n }, 50);\r\n\r\n // Transition to success screen after animation\r\n setTimeout(() => {\r\n setStep(\"success\");\r\n }, 2000);\r\n }\r\n }\r\n }\r\n };\r\n\r\n const handleKeyDown = (\r\n index: number,\r\n e: React.KeyboardEvent,\r\n ) => {\r\n if (e.key === \"Backspace\" && !code[index] && index > 0) {\r\n codeInputRefs.current[index - 1]?.focus();\r\n }\r\n };\r\n\r\n const handleBackClick = () => {\r\n setStep(\"email\");\r\n setCode([\"\", \"\", \"\", \"\", \"\", \"\"]);\r\n // Reset animations if going back\r\n setReverseCanvasVisible(false);\r\n setInitialCanvasVisible(true);\r\n };\r\n\r\n return (\r\n \r\n
\r\n {/* Initial canvas (forward animation) */}\r\n {initialCanvasVisible && (\r\n
\r\n \r\n
\r\n )}\r\n\r\n {/* Reverse canvas (appears when code is complete) */}\r\n {reverseCanvasVisible && (\r\n
\r\n \r\n
\r\n )}\r\n\r\n
\r\n
\r\n
\r\n\r\n {/* Content Layer */}\r\n
\r\n {/* Main content container */}\r\n
\r\n {/* Left side (form) */}\r\n
\r\n
\r\n \r\n {step === \"email\" ? (\r\n \r\n
\r\n

\r\n Welcome Developer\r\n

\r\n

\r\n Your sign in component\r\n

\r\n
\r\n\r\n
\r\n \r\n\r\n
\r\n
\r\n or\r\n
\r\n
\r\n\r\n
\r\n
\r\n setEmail(e.target.value)}\r\n className=\"w-full backdrop-blur-[1px] text-white border-1 border-white/10 rounded-full py-3 px-4 focus:outline-none focus:border focus:border-white/30 text-center\"\r\n required\r\n />\r\n \r\n \r\n \r\n โ†’\r\n \r\n \r\n โ†’\r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n

\r\n By signing up, you agree to the{\" \"}\r\n \r\n MSA\r\n \r\n ,{\" \"}\r\n \r\n Product Terms\r\n \r\n ,{\" \"}\r\n \r\n Policies\r\n \r\n ,{\" \"}\r\n \r\n Privacy Notice\r\n \r\n , and{\" \"}\r\n \r\n Cookie Notice\r\n \r\n .\r\n

\r\n \r\n ) : step === \"code\" ? (\r\n \r\n
\r\n

\r\n We sent you a code\r\n

\r\n

\r\n Please enter it\r\n

\r\n
\r\n\r\n
\r\n
\r\n
\r\n {code.map((digit, i) => (\r\n
\r\n
\r\n {\r\n codeInputRefs.current[i] = el;\r\n }}\r\n type=\"text\"\r\n inputMode=\"numeric\"\r\n pattern=\"[0-9]*\"\r\n maxLength={1}\r\n value={digit}\r\n onChange={(e) =>\r\n handleCodeChange(i, e.target.value)\r\n }\r\n onKeyDown={(e) => handleKeyDown(i, e)}\r\n className=\"w-8 text-center text-xl bg-transparent text-white border-none focus:outline-none focus:ring-0 appearance-none\"\r\n style={{ caretColor: \"transparent\" }}\r\n />\r\n {!digit && (\r\n
\r\n \r\n 0\r\n \r\n
\r\n )}\r\n
\r\n {i < 5 && (\r\n |\r\n )}\r\n
\r\n ))}\r\n
\r\n
\r\n
\r\n\r\n
\r\n \r\n Resend code\r\n \r\n
\r\n\r\n
\r\n \r\n Back\r\n \r\n d !== \"\")\r\n ? \"bg-white text-black border-transparent hover:bg-white/90 cursor-pointer\"\r\n : \"bg-[#111] text-white/50 border-white/10 cursor-not-allowed\"\r\n }`}\r\n disabled={!code.every((d) => d !== \"\")}\r\n >\r\n Continue\r\n \r\n
\r\n\r\n
\r\n

\r\n By signing up, you agree to the{\" \"}\r\n \r\n MSA\r\n \r\n ,{\" \"}\r\n \r\n Product Terms\r\n \r\n ,{\" \"}\r\n \r\n Policies\r\n \r\n ,{\" \"}\r\n \r\n Privacy Notice\r\n \r\n , and{\" \"}\r\n \r\n Cookie Notice\r\n \r\n .\r\n

\r\n
\r\n \r\n ) : (\r\n \r\n
\r\n

\r\n You're in!\r\n

\r\n

\r\n Welcome\r\n

\r\n
\r\n\r\n \r\n
\r\n \r\n \r\n \r\n
\r\n \r\n\r\n \r\n Continue to Dashboard\r\n \r\n \r\n )}\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n );\r\n};\r\n", "type": "registry:component" } ] diff --git a/public/r/following-eye.json b/public/r/following-eye.json index 2c0766d..0e1c69d 100644 --- a/public/r/following-eye.json +++ b/public/r/following-eye.json @@ -8,12 +8,12 @@ "files": [ { "path": "./src/components/nurui/following-eye-demo.tsx", - "content": "import { FollowingEye } from \"@/components/nurui/following-eye\";\n\nconst FollowingEyeDemo = () => {\n return ;\n};\n\nexport { FollowingEyeDemo };\n", + "content": "import { FollowingEye } from \"@/components/nurui/following-eye\";\r\n\r\nconst FollowingEyeDemo = () => {\r\n return ;\r\n};\r\n\r\nexport { FollowingEyeDemo };\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/following-eye.tsx", - "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport * as React from \"react\";\nimport { useState, useRef, useEffect } from \"react\";\n\nconst FollowingEye = ({ className }: { className?: string }) => {\n const [mousePos, setMousePos] = useState({ x: 0, y: 0 });\n const eye1Ref = useRef(null);\n const eye2Ref = useRef(null);\n\n const handleMouseMove = (e: React.MouseEvent) => {\n setMousePos({ x: e.clientX, y: e.clientY });\n };\n\n return (\n \n
\n }\n otherRef={eye2Ref as React.RefObject}\n />\n }\n otherRef={eye1Ref as React.RefObject}\n />\n
\n
\n );\n};\n\ninterface EyeProps {\n mouseX: number;\n mouseY: number;\n selfRef: React.RefObject;\n otherRef: React.RefObject;\n}\n\nconst Eye: React.FC = ({ mouseX, mouseY, selfRef, otherRef }) => {\n const pupilRef = useRef(null);\n const [center, setCenter] = useState({ x: 0, y: 0 });\n\n const updateCenter = React.useCallback(() => {\n if (!selfRef.current) return;\n const rect = selfRef.current.getBoundingClientRect();\n setCenter({\n x: rect.left + rect.width / 2,\n y: rect.top + rect.height / 2,\n });\n }, [selfRef]);\n\n useEffect(() => {\n const updateCenter = () => {\n if (!selfRef.current) return;\n const rect = selfRef.current.getBoundingClientRect();\n setCenter({\n x: rect.left + rect.width / 2,\n y: rect.top + rect.height / 2,\n });\n };\n\n updateCenter();\n window.addEventListener(\"resize\", updateCenter);\n return () => window.removeEventListener(\"resize\", updateCenter);\n }, [selfRef]);\n\n useEffect(() => {\n updateCenter();\n\n const isInside = (ref: React.RefObject) => {\n const rect = ref.current?.getBoundingClientRect();\n if (!rect) return false;\n return (\n mouseX >= rect.left &&\n mouseX <= rect.right &&\n mouseY >= rect.top &&\n mouseY <= rect.bottom\n );\n };\n\n if (isInside(selfRef) || isInside(otherRef)) return;\n\n const dx = mouseX - center.x;\n const dy = mouseY - center.y;\n const angle = Math.atan2(dy, dx);\n\n const maxMove = 20;\n const pupilX = Math.cos(angle) * maxMove;\n const pupilY = Math.sin(angle) * maxMove;\n\n if (pupilRef.current) {\n pupilRef.current.style.transform = `translate(${pupilX}px, ${pupilY}px)`;\n }\n }, [mouseX, mouseY, center.x, center.y, otherRef, selfRef, updateCenter]);\n\n return (\n \n \n
\n
\n
\n );\n};\n\nexport { FollowingEye };\n", + "content": "\"use client\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\nimport * as React from \"react\";\r\nimport { useState, useRef, useEffect } from \"react\";\r\n\r\nconst FollowingEye = ({ className }: { className?: string }) => {\r\n const [mousePos, setMousePos] = useState({ x: 0, y: 0 });\r\n const eye1Ref = useRef(null);\r\n const eye2Ref = useRef(null);\r\n\r\n const handleMouseMove = (e: React.MouseEvent) => {\r\n setMousePos({ x: e.clientX, y: e.clientY });\r\n };\r\n\r\n return (\r\n \r\n
\r\n }\r\n otherRef={eye2Ref as React.RefObject}\r\n />\r\n }\r\n otherRef={eye1Ref as React.RefObject}\r\n />\r\n
\r\n
\r\n );\r\n};\r\n\r\ninterface EyeProps {\r\n mouseX: number;\r\n mouseY: number;\r\n selfRef: React.RefObject;\r\n otherRef: React.RefObject;\r\n}\r\n\r\nconst Eye: React.FC = ({ mouseX, mouseY, selfRef, otherRef }) => {\r\n const pupilRef = useRef(null);\r\n const [center, setCenter] = useState({ x: 0, y: 0 });\r\n\r\n const updateCenter = React.useCallback(() => {\r\n if (!selfRef.current) return;\r\n const rect = selfRef.current.getBoundingClientRect();\r\n setCenter({\r\n x: rect.left + rect.width / 2,\r\n y: rect.top + rect.height / 2,\r\n });\r\n }, [selfRef]);\r\n\r\n useEffect(() => {\r\n const updateCenter = () => {\r\n if (!selfRef.current) return;\r\n const rect = selfRef.current.getBoundingClientRect();\r\n setCenter({\r\n x: rect.left + rect.width / 2,\r\n y: rect.top + rect.height / 2,\r\n });\r\n };\r\n\r\n updateCenter();\r\n window.addEventListener(\"resize\", updateCenter);\r\n return () => window.removeEventListener(\"resize\", updateCenter);\r\n }, [selfRef]);\r\n\r\n useEffect(() => {\r\n updateCenter();\r\n\r\n const isInside = (ref: React.RefObject) => {\r\n const rect = ref.current?.getBoundingClientRect();\r\n if (!rect) return false;\r\n return (\r\n mouseX >= rect.left &&\r\n mouseX <= rect.right &&\r\n mouseY >= rect.top &&\r\n mouseY <= rect.bottom\r\n );\r\n };\r\n\r\n if (isInside(selfRef) || isInside(otherRef)) return;\r\n\r\n const dx = mouseX - center.x;\r\n const dy = mouseY - center.y;\r\n const angle = Math.atan2(dy, dx);\r\n\r\n const maxMove = 20;\r\n const pupilX = Math.cos(angle) * maxMove;\r\n const pupilY = Math.sin(angle) * maxMove;\r\n\r\n if (pupilRef.current) {\r\n pupilRef.current.style.transform = `translate(${pupilX}px, ${pupilY}px)`;\r\n }\r\n }, [mouseX, mouseY, center.x, center.y, otherRef, selfRef, updateCenter]);\r\n\r\n return (\r\n \r\n \r\n
\r\n
\r\n
\r\n );\r\n};\r\n\r\nexport { FollowingEye };\r\n", "type": "registry:component" } ] diff --git a/public/r/future-button.json b/public/r/future-button.json index e9646c7..b5d1878 100644 --- a/public/r/future-button.json +++ b/public/r/future-button.json @@ -13,17 +13,17 @@ "files": [ { "path": "./src/components/nurui/future-button-demo.tsx", - "content": "\"use client\";\nimport { ShoppingCart } from \"lucide-react\";\nimport FutureButton from \"./future-button\";\n\nexport default function FutureButtonDemo() {\n return (\n
\n \n \n Purchase Item\n \n
\n );\n}\n", + "content": "\"use client\";\r\nimport { ShoppingCart } from \"lucide-react\";\r\nimport FutureButton from \"./future-button\";\r\n\r\nexport default function FutureButtonDemo() {\r\n return (\r\n
\r\n \r\n \r\n Purchase Item\r\n \r\n
\r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/future-button.tsx", - "content": "import { Frame } from \"@/components/nurui/frame\";\nimport { twMerge } from \"tailwind-merge\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\n// ๐ŸŽจ Theme colors โ€” no CSS variables, pure hex/rgba\nconst COLORS = {\n default: {\n stroke1: \"#4f46e5\",\n fill1: \"rgba(79,70,229,0.22)\",\n stroke2: \"#4f46e5\",\n fill2: \"rgba(79,70,229,0.1)\",\n text: \"#ffffff\",\n },\n accent: {\n stroke1: \"#f97316\",\n fill1: \"rgba(249,115,22,0.4)\",\n stroke2: \"#f97316\",\n fill2: \"rgba(249,115,22,0.2)\",\n text: \"#ffffff\",\n },\n destructive: {\n stroke1: \"#dc2626\",\n fill1: \"rgba(220,38,38,0.22)\",\n stroke2: \"#dc2626\",\n fill2: \"rgba(220,38,38,0.1)\",\n text: \"#ffffff\",\n },\n secondary: {\n stroke1: \"#64748b\",\n fill1: \"rgba(100,116,139,0.15)\",\n stroke2: \"#64748b\",\n fill2: \"rgba(100,116,139,0.1)\",\n text: \"#ffffff\",\n },\n success: {\n stroke1: \"#16a34a\",\n fill1: \"rgba(22,163,74,0.22)\",\n stroke2: \"#16a34a\",\n fill2: \"rgba(22,163,74,0.1)\",\n text: \"#ffffff\",\n },\n};\n\nconst buttonVariants = cva(\n \"group font-bold mb-2 relative px-8 py-2 cursor-pointer transition-all outline-none [&>span]:relative [&>span]:flex [&>span]:items-center [&>span]:justify-center\",\n {\n variants: {\n shape: {\n default: \"\",\n flat: \"\",\n simple: \"ps-8 pe-6\",\n },\n },\n defaultVariants: {\n shape: \"default\",\n },\n },\n);\n\nfunction FutureButton({\n className,\n children,\n shape = \"default\",\n enableBackdropBlur = false,\n enableViewBox = false,\n customPaths,\n textColor,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps & {\n customPaths?: string[];\n enableBackdropBlur?: boolean;\n enableViewBox?: boolean;\n bgColor?: string;\n textColor?: string;\n }) {\n const colors = COLORS.default;\n\n return (\n \n
\n {!customPaths && (shape === \"default\" || shape === \"flat\") && (\n \n )}\n\n {!customPaths && shape === \"simple\" && (\n \n )}\n\n {customPaths?.map((customPath, i) => (\n \n ))}\n
\n {children}\n \n );\n}\n\nexport default FutureButton;\n", + "content": "import { Frame } from \"@/components/nurui/frame\";\r\nimport { twMerge } from \"tailwind-merge\";\r\nimport { cva, type VariantProps } from \"class-variance-authority\";\r\n\r\n// ๐ŸŽจ Theme colors โ€” no CSS variables, pure hex/rgba\r\nconst COLORS = {\r\n default: {\r\n stroke1: \"#4f46e5\",\r\n fill1: \"rgba(79,70,229,0.22)\",\r\n stroke2: \"#4f46e5\",\r\n fill2: \"rgba(79,70,229,0.1)\",\r\n text: \"#ffffff\",\r\n },\r\n accent: {\r\n stroke1: \"#f97316\",\r\n fill1: \"rgba(249,115,22,0.4)\",\r\n stroke2: \"#f97316\",\r\n fill2: \"rgba(249,115,22,0.2)\",\r\n text: \"#ffffff\",\r\n },\r\n destructive: {\r\n stroke1: \"#dc2626\",\r\n fill1: \"rgba(220,38,38,0.22)\",\r\n stroke2: \"#dc2626\",\r\n fill2: \"rgba(220,38,38,0.1)\",\r\n text: \"#ffffff\",\r\n },\r\n secondary: {\r\n stroke1: \"#64748b\",\r\n fill1: \"rgba(100,116,139,0.15)\",\r\n stroke2: \"#64748b\",\r\n fill2: \"rgba(100,116,139,0.1)\",\r\n text: \"#ffffff\",\r\n },\r\n success: {\r\n stroke1: \"#16a34a\",\r\n fill1: \"rgba(22,163,74,0.22)\",\r\n stroke2: \"#16a34a\",\r\n fill2: \"rgba(22,163,74,0.1)\",\r\n text: \"#ffffff\",\r\n },\r\n};\r\n\r\nconst buttonVariants = cva(\r\n \"group font-bold mb-2 relative px-8 py-2 cursor-pointer transition-all outline-none [&>span]:relative [&>span]:flex [&>span]:items-center [&>span]:justify-center\",\r\n {\r\n variants: {\r\n shape: {\r\n default: \"\",\r\n flat: \"\",\r\n simple: \"ps-8 pe-6\",\r\n },\r\n },\r\n defaultVariants: {\r\n shape: \"default\",\r\n },\r\n },\r\n);\r\n\r\nfunction FutureButton({\r\n className,\r\n children,\r\n shape = \"default\",\r\n enableBackdropBlur = false,\r\n enableViewBox = false,\r\n customPaths,\r\n textColor,\r\n ...props\r\n}: React.ComponentProps<\"button\"> &\r\n VariantProps & {\r\n customPaths?: string[];\r\n enableBackdropBlur?: boolean;\r\n enableViewBox?: boolean;\r\n bgColor?: string;\r\n textColor?: string;\r\n }) {\r\n const colors = COLORS.default;\r\n\r\n return (\r\n \r\n
\r\n {!customPaths && (shape === \"default\" || shape === \"flat\") && (\r\n \r\n )}\r\n\r\n {!customPaths && shape === \"simple\" && (\r\n \r\n )}\r\n\r\n {customPaths?.map((customPath, i) => (\r\n \r\n ))}\r\n
\r\n {children}\r\n \r\n );\r\n}\r\n\r\nexport default FutureButton;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/frame.tsx", - "content": "import { useRef, useEffect } from \"react\";\nimport { twMerge } from \"tailwind-merge\";\nimport { type Paths, setupSvgRenderer } from \"@left4code/svg-renderer\";\n\nfunction Frame({\n className,\n paths,\n enableBackdropBlur,\n enableViewBox,\n ...props\n}: {\n paths: Paths;\n enableBackdropBlur?: boolean;\n enableViewBox?: boolean;\n} & React.ComponentProps<\"svg\">) {\n const svgRef = useRef(null);\n\n useEffect(() => {\n if (svgRef.current && svgRef.current.parentElement) {\n const instance = setupSvgRenderer({\n el: svgRef.current,\n paths,\n enableBackdropBlur,\n enableViewBox,\n });\n\n return () => instance.destroy();\n }\n }, [paths, enableViewBox, enableBackdropBlur]);\n\n return (\n \n );\n}\n\nexport { Frame };\n", + "content": "import { useRef, useEffect } from \"react\";\r\nimport { twMerge } from \"tailwind-merge\";\r\nimport { type Paths, setupSvgRenderer } from \"@left4code/svg-renderer\";\r\n\r\nfunction Frame({\r\n className,\r\n paths,\r\n enableBackdropBlur,\r\n enableViewBox,\r\n ...props\r\n}: {\r\n paths: Paths;\r\n enableBackdropBlur?: boolean;\r\n enableViewBox?: boolean;\r\n} & React.ComponentProps<\"svg\">) {\r\n const svgRef = useRef(null);\r\n\r\n useEffect(() => {\r\n if (svgRef.current && svgRef.current.parentElement) {\r\n const instance = setupSvgRenderer({\r\n el: svgRef.current,\r\n paths,\r\n enableBackdropBlur,\r\n enableViewBox,\r\n });\r\n\r\n return () => instance.destroy();\r\n }\r\n }, [paths, enableViewBox, enableBackdropBlur]);\r\n\r\n return (\r\n \r\n );\r\n}\r\n\r\nexport { Frame };\r\n", "type": "registry:component" } ] diff --git a/public/r/future-navbar.json b/public/r/future-navbar.json index 5e2b830..2bf4104 100644 --- a/public/r/future-navbar.json +++ b/public/r/future-navbar.json @@ -13,17 +13,17 @@ "files": [ { "path": "./src/components/nurui/futrue-navbar.tsx", - "content": "\"use client\";\nimport { createContext, useState } from \"react\";\nimport { Github, Zap } from \"lucide-react\";\nimport { Frame } from \"@/components/nurui/frame\";\nimport Link from \"next/link\";\nimport FutureButton from \"./future-button\";\n\nexport const MobileMenuContext = createContext<{\n showMenu: boolean;\n setShowMenu: React.Dispatch>;\n}>({\n showMenu: true,\n setShowMenu: () => {},\n});\n\nfunction FutureNavbar() {\n const [showMenu, setShowMenu] = useState(false);\n\n // ๐ŸŽจ Direct color constants\n const primaryStroke = \"#4f46e5\"; // Indigo\n const primaryFill = \"rgba(79, 70, 229, 0.2)\";\n\n return (\n \n
\n \n
\n
\n \n
\n \n Nur/ui\n \n
\n Docs\n Components\n About\n
\n setShowMenu(true)}\n className=\"cursor-pointer ms-auto flex items-center gap-2 lg:hidden font-medium\"\n >\n \n Menu\n
\n
\n
\n
\n \n
\n \n
Search Docsโ€ฆ
\n
โŒ˜+k
\n \n \n \n \n \n \n
\n
\n
\n \n
\n \n );\n}\n\nexport default FutureNavbar;\n", + "content": "\"use client\";\r\nimport { createContext, useState } from \"react\";\r\nimport { Github, Zap } from \"lucide-react\";\r\nimport { Frame } from \"@/components/nurui/frame\";\r\nimport Link from \"next/link\";\r\nimport FutureButton from \"./future-button\";\r\n\r\nexport const MobileMenuContext = createContext<{\r\n showMenu: boolean;\r\n setShowMenu: React.Dispatch>;\r\n}>({\r\n showMenu: true,\r\n setShowMenu: () => {},\r\n});\r\n\r\nfunction FutureNavbar() {\r\n const [showMenu, setShowMenu] = useState(false);\r\n\r\n // ๐ŸŽจ Direct color constants\r\n const primaryStroke = \"#4f46e5\"; // Indigo\r\n const primaryFill = \"rgba(79, 70, 229, 0.2)\";\r\n\r\n return (\r\n \r\n
\r\n \r\n
\r\n
\r\n \r\n
\r\n \r\n Nur/ui\r\n \r\n
\r\n Docs\r\n Components\r\n About\r\n
\r\n setShowMenu(true)}\r\n className=\"cursor-pointer ms-auto flex items-center gap-2 lg:hidden font-medium\"\r\n >\r\n \r\n Menu\r\n
\r\n
\r\n
\r\n
\r\n \r\n
\r\n \r\n
Search Docsโ€ฆ
\r\n
โŒ˜+k
\r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n \r\n
\r\n \r\n );\r\n}\r\n\r\nexport default FutureNavbar;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/frame.tsx", - "content": "import { useRef, useEffect } from \"react\";\nimport { twMerge } from \"tailwind-merge\";\nimport { type Paths, setupSvgRenderer } from \"@left4code/svg-renderer\";\n\nfunction Frame({\n className,\n paths,\n enableBackdropBlur,\n enableViewBox,\n ...props\n}: {\n paths: Paths;\n enableBackdropBlur?: boolean;\n enableViewBox?: boolean;\n} & React.ComponentProps<\"svg\">) {\n const svgRef = useRef(null);\n\n useEffect(() => {\n if (svgRef.current && svgRef.current.parentElement) {\n const instance = setupSvgRenderer({\n el: svgRef.current,\n paths,\n enableBackdropBlur,\n enableViewBox,\n });\n\n return () => instance.destroy();\n }\n }, [paths, enableViewBox, enableBackdropBlur]);\n\n return (\n \n );\n}\n\nexport { Frame };\n", + "content": "import { useRef, useEffect } from \"react\";\r\nimport { twMerge } from \"tailwind-merge\";\r\nimport { type Paths, setupSvgRenderer } from \"@left4code/svg-renderer\";\r\n\r\nfunction Frame({\r\n className,\r\n paths,\r\n enableBackdropBlur,\r\n enableViewBox,\r\n ...props\r\n}: {\r\n paths: Paths;\r\n enableBackdropBlur?: boolean;\r\n enableViewBox?: boolean;\r\n} & React.ComponentProps<\"svg\">) {\r\n const svgRef = useRef(null);\r\n\r\n useEffect(() => {\r\n if (svgRef.current && svgRef.current.parentElement) {\r\n const instance = setupSvgRenderer({\r\n el: svgRef.current,\r\n paths,\r\n enableBackdropBlur,\r\n enableViewBox,\r\n });\r\n\r\n return () => instance.destroy();\r\n }\r\n }, [paths, enableViewBox, enableBackdropBlur]);\r\n\r\n return (\r\n \r\n );\r\n}\r\n\r\nexport { Frame };\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/future-button.tsx", - "content": "import { Frame } from \"@/components/nurui/frame\";\nimport { twMerge } from \"tailwind-merge\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\n// ๐ŸŽจ Theme colors โ€” no CSS variables, pure hex/rgba\nconst COLORS = {\n default: {\n stroke1: \"#4f46e5\",\n fill1: \"rgba(79,70,229,0.22)\",\n stroke2: \"#4f46e5\",\n fill2: \"rgba(79,70,229,0.1)\",\n text: \"#ffffff\",\n },\n accent: {\n stroke1: \"#f97316\",\n fill1: \"rgba(249,115,22,0.4)\",\n stroke2: \"#f97316\",\n fill2: \"rgba(249,115,22,0.2)\",\n text: \"#ffffff\",\n },\n destructive: {\n stroke1: \"#dc2626\",\n fill1: \"rgba(220,38,38,0.22)\",\n stroke2: \"#dc2626\",\n fill2: \"rgba(220,38,38,0.1)\",\n text: \"#ffffff\",\n },\n secondary: {\n stroke1: \"#64748b\",\n fill1: \"rgba(100,116,139,0.15)\",\n stroke2: \"#64748b\",\n fill2: \"rgba(100,116,139,0.1)\",\n text: \"#ffffff\",\n },\n success: {\n stroke1: \"#16a34a\",\n fill1: \"rgba(22,163,74,0.22)\",\n stroke2: \"#16a34a\",\n fill2: \"rgba(22,163,74,0.1)\",\n text: \"#ffffff\",\n },\n};\n\nconst buttonVariants = cva(\n \"group font-bold mb-2 relative px-8 py-2 cursor-pointer transition-all outline-none [&>span]:relative [&>span]:flex [&>span]:items-center [&>span]:justify-center\",\n {\n variants: {\n shape: {\n default: \"\",\n flat: \"\",\n simple: \"ps-8 pe-6\",\n },\n },\n defaultVariants: {\n shape: \"default\",\n },\n },\n);\n\nfunction FutureButton({\n className,\n children,\n shape = \"default\",\n enableBackdropBlur = false,\n enableViewBox = false,\n customPaths,\n textColor,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps & {\n customPaths?: string[];\n enableBackdropBlur?: boolean;\n enableViewBox?: boolean;\n bgColor?: string;\n textColor?: string;\n }) {\n const colors = COLORS.default;\n\n return (\n \n
\n {!customPaths && (shape === \"default\" || shape === \"flat\") && (\n \n )}\n\n {!customPaths && shape === \"simple\" && (\n \n )}\n\n {customPaths?.map((customPath, i) => (\n \n ))}\n
\n {children}\n \n );\n}\n\nexport default FutureButton;\n", + "content": "import { Frame } from \"@/components/nurui/frame\";\r\nimport { twMerge } from \"tailwind-merge\";\r\nimport { cva, type VariantProps } from \"class-variance-authority\";\r\n\r\n// ๐ŸŽจ Theme colors โ€” no CSS variables, pure hex/rgba\r\nconst COLORS = {\r\n default: {\r\n stroke1: \"#4f46e5\",\r\n fill1: \"rgba(79,70,229,0.22)\",\r\n stroke2: \"#4f46e5\",\r\n fill2: \"rgba(79,70,229,0.1)\",\r\n text: \"#ffffff\",\r\n },\r\n accent: {\r\n stroke1: \"#f97316\",\r\n fill1: \"rgba(249,115,22,0.4)\",\r\n stroke2: \"#f97316\",\r\n fill2: \"rgba(249,115,22,0.2)\",\r\n text: \"#ffffff\",\r\n },\r\n destructive: {\r\n stroke1: \"#dc2626\",\r\n fill1: \"rgba(220,38,38,0.22)\",\r\n stroke2: \"#dc2626\",\r\n fill2: \"rgba(220,38,38,0.1)\",\r\n text: \"#ffffff\",\r\n },\r\n secondary: {\r\n stroke1: \"#64748b\",\r\n fill1: \"rgba(100,116,139,0.15)\",\r\n stroke2: \"#64748b\",\r\n fill2: \"rgba(100,116,139,0.1)\",\r\n text: \"#ffffff\",\r\n },\r\n success: {\r\n stroke1: \"#16a34a\",\r\n fill1: \"rgba(22,163,74,0.22)\",\r\n stroke2: \"#16a34a\",\r\n fill2: \"rgba(22,163,74,0.1)\",\r\n text: \"#ffffff\",\r\n },\r\n};\r\n\r\nconst buttonVariants = cva(\r\n \"group font-bold mb-2 relative px-8 py-2 cursor-pointer transition-all outline-none [&>span]:relative [&>span]:flex [&>span]:items-center [&>span]:justify-center\",\r\n {\r\n variants: {\r\n shape: {\r\n default: \"\",\r\n flat: \"\",\r\n simple: \"ps-8 pe-6\",\r\n },\r\n },\r\n defaultVariants: {\r\n shape: \"default\",\r\n },\r\n },\r\n);\r\n\r\nfunction FutureButton({\r\n className,\r\n children,\r\n shape = \"default\",\r\n enableBackdropBlur = false,\r\n enableViewBox = false,\r\n customPaths,\r\n textColor,\r\n ...props\r\n}: React.ComponentProps<\"button\"> &\r\n VariantProps & {\r\n customPaths?: string[];\r\n enableBackdropBlur?: boolean;\r\n enableViewBox?: boolean;\r\n bgColor?: string;\r\n textColor?: string;\r\n }) {\r\n const colors = COLORS.default;\r\n\r\n return (\r\n \r\n
\r\n {!customPaths && (shape === \"default\" || shape === \"flat\") && (\r\n \r\n )}\r\n\r\n {!customPaths && shape === \"simple\" && (\r\n \r\n )}\r\n\r\n {customPaths?.map((customPath, i) => (\r\n \r\n ))}\r\n
\r\n {children}\r\n \r\n );\r\n}\r\n\r\nexport default FutureButton;\r\n", "type": "registry:component" } ] diff --git a/public/r/gaming-form.json b/public/r/gaming-form.json index c6f361d..cb3270a 100644 --- a/public/r/gaming-form.json +++ b/public/r/gaming-form.json @@ -10,12 +10,12 @@ "files": [ { "path": "./src/components/nurui/gaming-form-demo.tsx", - "content": "\"use client\";\nimport LoginPage from \"@/components/nurui/gaming-form\";\n\nfunction GamingFormDemo() {\n const handleLogin = (email: string, password: string, remember: boolean) => {\n console.log(\"Login attempt:\", { email, password, remember });\n };\n\n return (\n
\n \n\n
\n \n
\n\n
\n ยฉ 2025 NexusGate. All rights reserved.\n
\n
\n );\n}\n\nexport default GamingFormDemo;\n", + "content": "\"use client\";\r\nimport LoginPage from \"@/components/nurui/gaming-form\";\r\n\r\nfunction GamingFormDemo() {\r\n const handleLogin = (email: string, password: string, remember: boolean) => {\r\n console.log(\"Login attempt:\", { email, password, remember });\r\n };\r\n\r\n return (\r\n
\r\n \r\n\r\n
\r\n \r\n
\r\n\r\n
\r\n ยฉ 2025 NexusGate. All rights reserved.\r\n
\r\n
\r\n );\r\n}\r\n\r\nexport default GamingFormDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/gaming-form.tsx", - "content": "\"use client\";\nimport React, { useState, useRef, useEffect } from \"react\";\nimport {\n Eye,\n EyeOff,\n Mail,\n Lock,\n Chrome,\n Twitter,\n Gamepad2,\n} from \"lucide-react\";\n\ninterface LoginFormProps {\n onSubmit: (email: string, password: string, remember: boolean) => void;\n}\n\ninterface VideoBackgroundProps {\n videoUrl: string;\n}\n\ninterface FormInputProps {\n icon: React.ReactNode;\n type: string;\n placeholder: string;\n value: string;\n onChange: (e: React.ChangeEvent) => void;\n required?: boolean;\n}\n\ninterface SocialButtonProps {\n icon: React.ReactNode;\n name: string;\n}\n\ninterface ToggleSwitchProps {\n checked: boolean;\n onChange: () => void;\n id: string;\n}\n\n// FormInput Component\nconst FormInput: React.FC = ({\n icon,\n type,\n placeholder,\n value,\n onChange,\n required,\n}) => {\n return (\n
\n
{icon}
\n \n
\n );\n};\n\n// SocialButton Component\nconst SocialButton: React.FC = ({ icon }) => {\n return (\n \n );\n};\n\n// ToggleSwitch Component\nconst ToggleSwitch: React.FC = ({\n checked,\n onChange,\n id,\n}) => {\n return (\n
\n \n \n \n
\n \n );\n};\n\n// VideoBackground Component\nconst VideoBackground: React.FC = ({ videoUrl }) => {\n const videoRef = useRef(null);\n\n useEffect(() => {\n if (videoRef.current) {\n videoRef.current.play().catch((error) => {\n console.error(\"Video autoplay failed:\", error);\n });\n }\n }, []);\n\n return (\n
\n
\n \n \n Your browser does not support the video tag.\n \n
\n );\n};\n\n// Main LoginForm Component\nconst LoginForm: React.FC = ({ onSubmit }) => {\n const [email, setEmail] = useState(\"\");\n const [password, setPassword] = useState(\"\");\n const [showPassword, setShowPassword] = useState(false);\n const [remember, setRemember] = useState(false);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [isSuccess, setIsSuccess] = useState(false);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setIsSubmitting(true);\n\n await new Promise((resolve) => setTimeout(resolve, 1000));\n setIsSuccess(true);\n await new Promise((resolve) => setTimeout(resolve, 500));\n\n onSubmit(email, password, remember);\n setIsSubmitting(false);\n setIsSuccess(false);\n };\n\n return (\n
\n
\n

\n \n \n NexusGate\n \n \n

\n
\n \n \n \n Your gaming universe awaits\n \n \n \n [Press Enter to join the adventure]\n \n
\n โš”๏ธ\n ๐ŸŽฎ\n ๐Ÿ†\n
\n
\n
\n\n
\n }\n type=\"email\"\n placeholder=\"Email address\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n required\n />\n\n
\n }\n type={showPassword ? \"text\" : \"password\"}\n placeholder=\"Password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n required\n />\n setShowPassword(!showPassword)}\n aria-label={showPassword ? \"Hide password\" : \"Show password\"}\n >\n {showPassword ? : }\n \n
\n\n
\n
\n setRemember(!remember)}\n className=\"cursor-pointer\"\n >\n setRemember(!remember)}\n id=\"remember-me\"\n />\n
\n setRemember(!remember)}\n >\n Remember me\n \n
\n \n Forgot password?\n \n
\n\n \n {isSubmitting ? \"Logging in...\" : \"Enter NexusGate\"}\n \n \n\n
\n
\n
\n
\n quick access via\n
\n
\n\n
\n } name=\"Chrome\" />\n } name=\"X\" />\n } name=\"Steam\" />\n
\n
\n\n
\n Don't have an account?{\" \"}\n \n Create Account\n \n
\n
\n );\n};\n\n// Export as default components\nconst LoginPage = {\n LoginForm,\n VideoBackground,\n};\n\nexport default LoginPage;\n", + "content": "\"use client\";\r\nimport React, { useState, useRef, useEffect } from \"react\";\r\nimport {\r\n Eye,\r\n EyeOff,\r\n Mail,\r\n Lock,\r\n Chrome,\r\n Twitter,\r\n Gamepad2,\r\n} from \"lucide-react\";\r\n\r\ninterface LoginFormProps {\r\n onSubmit: (email: string, password: string, remember: boolean) => void;\r\n}\r\n\r\ninterface VideoBackgroundProps {\r\n videoUrl: string;\r\n}\r\n\r\ninterface FormInputProps {\r\n icon: React.ReactNode;\r\n type: string;\r\n placeholder: string;\r\n value: string;\r\n onChange: (e: React.ChangeEvent) => void;\r\n required?: boolean;\r\n}\r\n\r\ninterface SocialButtonProps {\r\n icon: React.ReactNode;\r\n name: string;\r\n}\r\n\r\ninterface ToggleSwitchProps {\r\n checked: boolean;\r\n onChange: () => void;\r\n id: string;\r\n}\r\n\r\n// FormInput Component\r\nconst FormInput: React.FC = ({\r\n icon,\r\n type,\r\n placeholder,\r\n value,\r\n onChange,\r\n required,\r\n}) => {\r\n return (\r\n
\r\n
{icon}
\r\n \r\n
\r\n );\r\n};\r\n\r\n// SocialButton Component\r\nconst SocialButton: React.FC = ({ icon }) => {\r\n return (\r\n \r\n );\r\n};\r\n\r\n// ToggleSwitch Component\r\nconst ToggleSwitch: React.FC = ({\r\n checked,\r\n onChange,\r\n id,\r\n}) => {\r\n return (\r\n
\r\n \r\n \r\n \r\n
\r\n \r\n );\r\n};\r\n\r\n// VideoBackground Component\r\nconst VideoBackground: React.FC = ({ videoUrl }) => {\r\n const videoRef = useRef(null);\r\n\r\n useEffect(() => {\r\n if (videoRef.current) {\r\n videoRef.current.play().catch((error) => {\r\n console.error(\"Video autoplay failed:\", error);\r\n });\r\n }\r\n }, []);\r\n\r\n return (\r\n
\r\n
\r\n \r\n \r\n Your browser does not support the video tag.\r\n \r\n
\r\n );\r\n};\r\n\r\n// Main LoginForm Component\r\nconst LoginForm: React.FC = ({ onSubmit }) => {\r\n const [email, setEmail] = useState(\"\");\r\n const [password, setPassword] = useState(\"\");\r\n const [showPassword, setShowPassword] = useState(false);\r\n const [remember, setRemember] = useState(false);\r\n const [isSubmitting, setIsSubmitting] = useState(false);\r\n const [isSuccess, setIsSuccess] = useState(false);\r\n\r\n const handleSubmit = async (e: React.FormEvent) => {\r\n e.preventDefault();\r\n setIsSubmitting(true);\r\n\r\n await new Promise((resolve) => setTimeout(resolve, 1000));\r\n setIsSuccess(true);\r\n await new Promise((resolve) => setTimeout(resolve, 500));\r\n\r\n onSubmit(email, password, remember);\r\n setIsSubmitting(false);\r\n setIsSuccess(false);\r\n };\r\n\r\n return (\r\n
\r\n
\r\n

\r\n \r\n \r\n NexusGate\r\n \r\n \r\n

\r\n
\r\n \r\n \r\n \r\n Your gaming universe awaits\r\n \r\n \r\n \r\n [Press Enter to join the adventure]\r\n \r\n
\r\n โš”๏ธ\r\n ๐ŸŽฎ\r\n ๐Ÿ†\r\n
\r\n
\r\n
\r\n\r\n
\r\n }\r\n type=\"email\"\r\n placeholder=\"Email address\"\r\n value={email}\r\n onChange={(e) => setEmail(e.target.value)}\r\n required\r\n />\r\n\r\n
\r\n }\r\n type={showPassword ? \"text\" : \"password\"}\r\n placeholder=\"Password\"\r\n value={password}\r\n onChange={(e) => setPassword(e.target.value)}\r\n required\r\n />\r\n setShowPassword(!showPassword)}\r\n aria-label={showPassword ? \"Hide password\" : \"Show password\"}\r\n >\r\n {showPassword ? : }\r\n \r\n
\r\n\r\n
\r\n
\r\n setRemember(!remember)}\r\n className=\"cursor-pointer\"\r\n >\r\n setRemember(!remember)}\r\n id=\"remember-me\"\r\n />\r\n
\r\n setRemember(!remember)}\r\n >\r\n Remember me\r\n \r\n
\r\n \r\n Forgot password?\r\n \r\n
\r\n\r\n \r\n {isSubmitting ? \"Logging in...\" : \"Enter NexusGate\"}\r\n \r\n \r\n\r\n
\r\n
\r\n
\r\n
\r\n quick access via\r\n
\r\n
\r\n\r\n
\r\n } name=\"Chrome\" />\r\n } name=\"X\" />\r\n } name=\"Steam\" />\r\n
\r\n
\r\n\r\n
\r\n Don't have an account?{\" \"}\r\n \r\n Create Account\r\n \r\n
\r\n
\r\n );\r\n};\r\n\r\n// Export as default components\r\nconst LoginPage = {\r\n LoginForm,\r\n VideoBackground,\r\n};\r\n\r\nexport default LoginPage;\r\n", "type": "registry:component" } ] diff --git a/public/r/ghost-cursor.json b/public/r/ghost-cursor.json index b83754d..ea12353 100644 --- a/public/r/ghost-cursor.json +++ b/public/r/ghost-cursor.json @@ -8,12 +8,12 @@ "files": [ { "path": "./src/components/nurui/ghost-cursor-demo.tsx", - "content": "import React from \"react\";\nimport GhostTrailCursor from \"@/components/nurui/ghost-cursor\";\n\nconst GhostCursorDemo = () => {\n return (\n <>\n

\n Move cursor to see the effect.\n

\n \n \n );\n};\n\nexport default GhostCursorDemo;\n", + "content": "import React from \"react\";\r\nimport GhostTrailCursor from \"@/components/nurui/ghost-cursor\";\r\n\r\nconst GhostCursorDemo = () => {\r\n return (\r\n <>\r\n

\r\n Move cursor to see the effect.\r\n

\r\n \r\n \r\n );\r\n};\r\n\r\nexport default GhostCursorDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/ghost-cursor.tsx", - "content": "\"use client\";\nimport React, { useRef, useEffect } from \"react\";\n\nconst GhostTrailCursor = () => {\n const canvasRef = useRef(null);\n // Define a Particle type to avoid using 'any'\n type ParticleType = {\n x: number;\n y: number;\n radius: number;\n alpha: number;\n color: string;\n draw: () => void;\n update: () => void;\n };\n\n useEffect(() => {\n // Move particles array inside useEffect to avoid dependency warning\n const particles: ParticleType[] = [];\n\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n const resize = () => {\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n };\n\n resize();\n window.addEventListener(\"resize\", resize);\n\n class Particle {\n x: number;\n y: number;\n radius: number;\n alpha: number;\n color: string;\n\n constructor(x: number, y: number) {\n this.x = x;\n this.y = y;\n this.radius = Math.random() * 6 + 2;\n this.alpha = 1;\n this.color = \"255, 255, 255\";\n }\n\n draw() {\n if (!ctx) return;\n ctx.beginPath();\n ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);\n ctx.fillStyle = `rgba(${this.color}, ${this.alpha})`;\n ctx.fill();\n }\n\n update() {\n this.alpha -= 0.02;\n }\n }\n\n const animate = () => {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n particles.forEach((p, index) => {\n p.update();\n p.draw();\n if (p.alpha <= 0) particles.splice(index, 1);\n });\n requestAnimationFrame(animate);\n };\n animate();\n\n const onMove = (e: MouseEvent) => {\n particles.push(new Particle(e.clientX, e.clientY));\n };\n\n window.addEventListener(\"mousemove\", onMove);\n\n return () => {\n window.removeEventListener(\"mousemove\", onMove);\n window.removeEventListener(\"resize\", resize);\n };\n }, []);\n\n return (\n \n );\n};\n\nexport default GhostTrailCursor;\n", + "content": "\"use client\";\r\nimport React, { useRef, useEffect } from \"react\";\r\n\r\nconst GhostTrailCursor = () => {\r\n const canvasRef = useRef(null);\r\n // Define a Particle type to avoid using 'any'\r\n type ParticleType = {\r\n x: number;\r\n y: number;\r\n radius: number;\r\n alpha: number;\r\n color: string;\r\n draw: () => void;\r\n update: () => void;\r\n };\r\n\r\n useEffect(() => {\r\n // Move particles array inside useEffect to avoid dependency warning\r\n const particles: ParticleType[] = [];\r\n\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) return;\r\n\r\n const resize = () => {\r\n canvas.width = window.innerWidth;\r\n canvas.height = window.innerHeight;\r\n };\r\n\r\n resize();\r\n window.addEventListener(\"resize\", resize);\r\n\r\n class Particle {\r\n x: number;\r\n y: number;\r\n radius: number;\r\n alpha: number;\r\n color: string;\r\n\r\n constructor(x: number, y: number) {\r\n this.x = x;\r\n this.y = y;\r\n this.radius = Math.random() * 6 + 2;\r\n this.alpha = 1;\r\n this.color = \"255, 255, 255\";\r\n }\r\n\r\n draw() {\r\n if (!ctx) return;\r\n ctx.beginPath();\r\n ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);\r\n ctx.fillStyle = `rgba(${this.color}, ${this.alpha})`;\r\n ctx.fill();\r\n }\r\n\r\n update() {\r\n this.alpha -= 0.02;\r\n }\r\n }\r\n\r\n const animate = () => {\r\n ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n particles.forEach((p, index) => {\r\n p.update();\r\n p.draw();\r\n if (p.alpha <= 0) particles.splice(index, 1);\r\n });\r\n requestAnimationFrame(animate);\r\n };\r\n animate();\r\n\r\n const onMove = (e: MouseEvent) => {\r\n particles.push(new Particle(e.clientX, e.clientY));\r\n };\r\n\r\n window.addEventListener(\"mousemove\", onMove);\r\n\r\n return () => {\r\n window.removeEventListener(\"mousemove\", onMove);\r\n window.removeEventListener(\"resize\", resize);\r\n };\r\n }, []);\r\n\r\n return (\r\n \r\n );\r\n};\r\n\r\nexport default GhostTrailCursor;\r\n", "type": "registry:component" } ] diff --git a/public/r/glowing-card.json b/public/r/glowing-card.json index 6b07604..78c8b11 100644 --- a/public/r/glowing-card.json +++ b/public/r/glowing-card.json @@ -11,12 +11,12 @@ "files": [ { "path": "./src/components/nurui/glowing-card-demo.tsx", - "content": "import GlowingCard from \"@/components/nurui/glowing-card\";\n\nexport default function GlowingCardDemo() {\n return (\n
\n \n
\n );\n}\n", + "content": "import GlowingCard from \"@/components/nurui/glowing-card\";\r\n\r\nexport default function GlowingCardDemo() {\r\n return (\r\n
\r\n \r\n
\r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/glowing-card.tsx", - "content": "\"use client\";\nimport { cn } from \"@/lib/utils\";\nimport { useEffect, useState } from \"react\";\n\nexport default function GlowingCard({ className }: { className?: string }) {\n const [angle, setAngle] = useState(0);\n\n useEffect(() => {\n const interval = setInterval(() => {\n setAngle((prev) => (prev + 1) % 360);\n }, 10); // smooth rotation\n return () => clearInterval(interval);\n }, []);\n\n return (\n
\n
\n I glow :)\n \n
\n
\n );\n}\n", + "content": "\"use client\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { useEffect, useState } from \"react\";\r\n\r\nexport default function GlowingCard({ className }: { className?: string }) {\r\n const [angle, setAngle] = useState(0);\r\n\r\n useEffect(() => {\r\n const interval = setInterval(() => {\r\n setAngle((prev) => (prev + 1) % 360);\r\n }, 10); // smooth rotation\r\n return () => clearInterval(interval);\r\n }, []);\r\n\r\n return (\r\n
\r\n
\r\n I glow :)\r\n \r\n
\r\n
\r\n );\r\n}\r\n", "type": "registry:component" } ] diff --git a/public/r/gradient-background.json b/public/r/gradient-background.json index 4cef4ef..015489e 100644 --- a/public/r/gradient-background.json +++ b/public/r/gradient-background.json @@ -11,12 +11,12 @@ "files": [ { "path": "./src/components/nurui/gradient-background-demo.tsx", - "content": "import React from \"react\";\nimport { GradientBackground } from \"@/components/nurui/gradient-background\";\n\nconst GradientBackgroundDemo = () => {\n return (\n
\n

\n Background\n

\n \n
\n );\n};\n\nexport default GradientBackgroundDemo;\n", + "content": "import React from \"react\";\r\nimport { GradientBackground } from \"@/components/nurui/gradient-background\";\r\n\r\nconst GradientBackgroundDemo = () => {\r\n return (\r\n
\r\n

\r\n Background\r\n

\r\n \r\n
\r\n );\r\n};\r\n\r\nexport default GradientBackgroundDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/gradient-background.tsx", - "content": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { useTheme } from \"next-themes\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport const GradientBackground = ({\n firstColor = \"60, 162, 250\",\n secondColor = \"60, 162, 250\",\n thirdColor = \"60, 162, 250\",\n fourthColor = \"60, 162, 250\",\n fifthColor = \"60, 162, 250\",\n pointerColor = \"60, 162, 250\",\n size = \"50%\",\n blendingValue = \"hard-light\",\n children,\n className,\n interactive = true,\n containerClassName,\n}: {\n gradientBackgroundStart?: string;\n gradientBackgroundEnd?: string;\n firstColor?: string;\n secondColor?: string;\n thirdColor?: string;\n fourthColor?: string;\n fifthColor?: string;\n pointerColor?: string;\n size?: string;\n blendingValue?: string;\n children?: React.ReactNode;\n className?: string;\n interactive?: boolean;\n containerClassName?: string;\n}) => {\n const interactiveRef = useRef(null);\n\n // Use refs to store animation values instead of state\n const curXRef = useRef(0);\n const curYRef = useRef(0);\n const tgXRef = useRef(0);\n const tgYRef = useRef(0);\n const animationFrameRef = useRef(null);\n\n const [isSafari, setIsSafari] = useState(false);\n\n const { theme } = useTheme();\n\n // Set up gradient background colors based on theme\n const gradientBackgroundStart = theme === \"dark\" ? \"#01031333\" : \"#ffffff\";\n const gradientBackgroundEnd = theme === \"dark\" ? \"#01031333\" : \"#ffffff\";\n\n // Set up CSS variables\n useEffect(() => {\n document.body.style.setProperty(\n \"--gradient-background-start\",\n gradientBackgroundStart,\n );\n document.body.style.setProperty(\n \"--gradient-background-end\",\n gradientBackgroundEnd,\n );\n document.body.style.setProperty(\"--first-color\", firstColor);\n document.body.style.setProperty(\"--second-color\", secondColor);\n document.body.style.setProperty(\"--third-color\", thirdColor);\n document.body.style.setProperty(\"--fourth-color\", fourthColor);\n document.body.style.setProperty(\"--fifth-color\", fifthColor);\n document.body.style.setProperty(\"--pointer-color\", pointerColor);\n document.body.style.setProperty(\"--size\", size);\n document.body.style.setProperty(\"--blending-value\", blendingValue);\n }, [\n gradientBackgroundStart,\n gradientBackgroundEnd,\n firstColor,\n secondColor,\n thirdColor,\n fourthColor,\n fifthColor,\n pointerColor,\n size,\n blendingValue,\n ]);\n\n // Set up Safari detection\n useEffect(() => {\n setIsSafari(/^((?!chrome|android).)*safari/i.test(navigator.userAgent));\n }, []);\n\n // Set up animation loop\n useEffect(() => {\n if (!interactive) return;\n\n function animateMovement() {\n if (!interactiveRef.current) {\n animationFrameRef.current = requestAnimationFrame(animateMovement);\n return;\n }\n\n // Calculate new position with easing\n curXRef.current =\n curXRef.current + (tgXRef.current - curXRef.current) / 20;\n curYRef.current =\n curYRef.current + (tgYRef.current - curYRef.current) / 20;\n\n // Apply transform directly to DOM element\n interactiveRef.current.style.transform = `translate(${Math.round(curXRef.current)}px, ${Math.round(curYRef.current)}px)`;\n\n // Continue animation loop\n animationFrameRef.current = requestAnimationFrame(animateMovement);\n }\n\n // Start animation loop\n animationFrameRef.current = requestAnimationFrame(animateMovement);\n\n // Clean up animation loop on unmount\n return () => {\n if (animationFrameRef.current !== null) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n };\n }, [interactive]);\n\n // Handle mouse movement\n const handleMouseMove = (event: React.MouseEvent) => {\n if (!interactiveRef.current) return;\n\n const rect = interactiveRef.current.getBoundingClientRect();\n // Update target position directly in ref (no state update)\n tgXRef.current = event.clientX - rect.left;\n tgYRef.current = event.clientY - rect.top;\n };\n\n return (\n \n \n \n \n \n \n \n \n \n \n
{children}
\n \n \n \n \n \n \n\n {interactive && (\n \n )}\n \n \n );\n};\n", + "content": "\"use client\";\r\n\r\nimport { useEffect, useRef, useState } from \"react\";\r\nimport { useTheme } from \"next-themes\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nexport const GradientBackground = ({\r\n firstColor = \"60, 162, 250\",\r\n secondColor = \"60, 162, 250\",\r\n thirdColor = \"60, 162, 250\",\r\n fourthColor = \"60, 162, 250\",\r\n fifthColor = \"60, 162, 250\",\r\n pointerColor = \"60, 162, 250\",\r\n size = \"50%\",\r\n blendingValue = \"hard-light\",\r\n children,\r\n className,\r\n interactive = true,\r\n containerClassName,\r\n}: {\r\n gradientBackgroundStart?: string;\r\n gradientBackgroundEnd?: string;\r\n firstColor?: string;\r\n secondColor?: string;\r\n thirdColor?: string;\r\n fourthColor?: string;\r\n fifthColor?: string;\r\n pointerColor?: string;\r\n size?: string;\r\n blendingValue?: string;\r\n children?: React.ReactNode;\r\n className?: string;\r\n interactive?: boolean;\r\n containerClassName?: string;\r\n}) => {\r\n const interactiveRef = useRef(null);\r\n\r\n // Use refs to store animation values instead of state\r\n const curXRef = useRef(0);\r\n const curYRef = useRef(0);\r\n const tgXRef = useRef(0);\r\n const tgYRef = useRef(0);\r\n const animationFrameRef = useRef(null);\r\n\r\n const [isSafari, setIsSafari] = useState(false);\r\n\r\n const { theme } = useTheme();\r\n\r\n // Set up gradient background colors based on theme\r\n const gradientBackgroundStart = theme === \"dark\" ? \"#01031333\" : \"#ffffff\";\r\n const gradientBackgroundEnd = theme === \"dark\" ? \"#01031333\" : \"#ffffff\";\r\n\r\n // Set up CSS variables\r\n useEffect(() => {\r\n document.body.style.setProperty(\r\n \"--gradient-background-start\",\r\n gradientBackgroundStart,\r\n );\r\n document.body.style.setProperty(\r\n \"--gradient-background-end\",\r\n gradientBackgroundEnd,\r\n );\r\n document.body.style.setProperty(\"--first-color\", firstColor);\r\n document.body.style.setProperty(\"--second-color\", secondColor);\r\n document.body.style.setProperty(\"--third-color\", thirdColor);\r\n document.body.style.setProperty(\"--fourth-color\", fourthColor);\r\n document.body.style.setProperty(\"--fifth-color\", fifthColor);\r\n document.body.style.setProperty(\"--pointer-color\", pointerColor);\r\n document.body.style.setProperty(\"--size\", size);\r\n document.body.style.setProperty(\"--blending-value\", blendingValue);\r\n }, [\r\n gradientBackgroundStart,\r\n gradientBackgroundEnd,\r\n firstColor,\r\n secondColor,\r\n thirdColor,\r\n fourthColor,\r\n fifthColor,\r\n pointerColor,\r\n size,\r\n blendingValue,\r\n ]);\r\n\r\n // Set up Safari detection\r\n useEffect(() => {\r\n setIsSafari(/^((?!chrome|android).)*safari/i.test(navigator.userAgent));\r\n }, []);\r\n\r\n // Set up animation loop\r\n useEffect(() => {\r\n if (!interactive) return;\r\n\r\n function animateMovement() {\r\n if (!interactiveRef.current) {\r\n animationFrameRef.current = requestAnimationFrame(animateMovement);\r\n return;\r\n }\r\n\r\n // Calculate new position with easing\r\n curXRef.current =\r\n curXRef.current + (tgXRef.current - curXRef.current) / 20;\r\n curYRef.current =\r\n curYRef.current + (tgYRef.current - curYRef.current) / 20;\r\n\r\n // Apply transform directly to DOM element\r\n interactiveRef.current.style.transform = `translate(${Math.round(curXRef.current)}px, ${Math.round(curYRef.current)}px)`;\r\n\r\n // Continue animation loop\r\n animationFrameRef.current = requestAnimationFrame(animateMovement);\r\n }\r\n\r\n // Start animation loop\r\n animationFrameRef.current = requestAnimationFrame(animateMovement);\r\n\r\n // Clean up animation loop on unmount\r\n return () => {\r\n if (animationFrameRef.current !== null) {\r\n cancelAnimationFrame(animationFrameRef.current);\r\n }\r\n };\r\n }, [interactive]);\r\n\r\n // Handle mouse movement\r\n const handleMouseMove = (event: React.MouseEvent) => {\r\n if (!interactiveRef.current) return;\r\n\r\n const rect = interactiveRef.current.getBoundingClientRect();\r\n // Update target position directly in ref (no state update)\r\n tgXRef.current = event.clientX - rect.left;\r\n tgYRef.current = event.clientY - rect.top;\r\n };\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
{children}
\r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n {interactive && (\r\n \r\n )}\r\n \r\n \r\n );\r\n};\r\n", "type": "registry:component" } ] diff --git a/public/r/gradient-button.json b/public/r/gradient-button.json index a1ef28b..01ace27 100644 --- a/public/r/gradient-button.json +++ b/public/r/gradient-button.json @@ -11,17 +11,17 @@ "files": [ { "path": "./src/components/nurui/gradient-button-demo.tsx", - "content": "import React from \"react\";\nimport GradientButton from \"@/components/nurui/gradient-button\";\n\nconst GradientButtonDemo = () => {\n return (\n
\n \n
\n );\n};\n\nexport default GradientButtonDemo;\n", + "content": "import React from \"react\";\r\nimport GradientButton from \"@/components/nurui/gradient-button\";\r\n\r\nconst GradientButtonDemo = () => {\r\n return (\r\n
\r\n \r\n
\r\n );\r\n};\r\n\r\nexport default GradientButtonDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/gradient-button.tsx", - "content": "import { cn } from \"@/lib/utils\";\nimport React, { CSSProperties } from \"react\";\nimport \"./styles/gradient-button.css\";\n\ninterface GradientButtonProps {\n borderWidth?: number;\n colors?: string[];\n duration?: number;\n borderRadius?: number;\n blur?: number;\n className?: string;\n bgColor?: string;\n text?: string;\n}\n\nconst GradientButton: React.FC = ({\n borderWidth = 2,\n colors = [\n \"#FF0000\",\n \"#FFA500\",\n \"#FFFF00\",\n \"#008000\",\n \"#0000FF\",\n \"#4B0082\",\n \"#EE82EE\",\n \"#FF0000\",\n ],\n duration = 2500,\n borderRadius = 8,\n blur = 4,\n className,\n bgColor = \"#000\",\n text = \"Zooooooooooom ๐Ÿš€\",\n}) => {\n const gradientStyle = {\n \"--allColors\": colors.join(\", \"),\n \"--duration\": `${duration}ms`,\n \"--borderWidth\": `${borderWidth}px`,\n \"--borderRadius\": `${borderRadius}px`,\n \"--blur\": `${blur}px`,\n \"--bgColor\": bgColor,\n } as CSSProperties;\n\n return (\n
\n \n \n {text}\n \n \n
\n );\n};\n\nexport default GradientButton;\n", + "content": "import { cn } from \"@/lib/utils\";\r\nimport React, { CSSProperties } from \"react\";\r\nimport \"./styles/gradient-button.css\";\r\n\r\ninterface GradientButtonProps {\r\n borderWidth?: number;\r\n colors?: string[];\r\n duration?: number;\r\n borderRadius?: number;\r\n blur?: number;\r\n className?: string;\r\n bgColor?: string;\r\n text?: string;\r\n}\r\n\r\nconst GradientButton: React.FC = ({\r\n borderWidth = 2,\r\n colors = [\r\n \"#FF0000\",\r\n \"#FFA500\",\r\n \"#FFFF00\",\r\n \"#008000\",\r\n \"#0000FF\",\r\n \"#4B0082\",\r\n \"#EE82EE\",\r\n \"#FF0000\",\r\n ],\r\n duration = 2500,\r\n borderRadius = 8,\r\n blur = 4,\r\n className,\r\n bgColor = \"#000\",\r\n text = \"Zooooooooooom ๐Ÿš€\",\r\n}) => {\r\n const gradientStyle = {\r\n \"--allColors\": colors.join(\", \"),\r\n \"--duration\": `${duration}ms`,\r\n \"--borderWidth\": `${borderWidth}px`,\r\n \"--borderRadius\": `${borderRadius}px`,\r\n \"--blur\": `${blur}px`,\r\n \"--bgColor\": bgColor,\r\n } as CSSProperties;\r\n\r\n return (\r\n
\r\n \r\n \r\n {text}\r\n \r\n \r\n
\r\n );\r\n};\r\n\r\nexport default GradientButton;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/styles/gradient-button.css", - "content": " @keyframes rotate-rainbow {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n }\n\n .rainbow-btn {\n padding: var(--borderWidth);\n border-radius: var(--borderRadius);\n }\n\n .rainbow-btn::before {\n content: \"\";\n background: conic-gradient(var(--allColors));\n animation: rotate-rainbow var(--duration) linear infinite;\n filter: blur(var(--blur));\n padding: var(--borderWidth);\n position: absolute;\n inset: -200%;\n z-index: 0;\n }\n\n .btn-content {\n border-radius: var(--borderRadius);\n background-color: var(--bgColor);\n z-index: 10;\n position: relative;\n }", + "content": " @keyframes rotate-rainbow {\r\n 0% {\r\n transform: rotate(0deg);\r\n }\r\n 100% {\r\n transform: rotate(360deg);\r\n }\r\n }\r\n\r\n .rainbow-btn {\r\n padding: var(--borderWidth);\r\n border-radius: var(--borderRadius);\r\n }\r\n\r\n .rainbow-btn::before {\r\n content: \"\";\r\n background: conic-gradient(var(--allColors));\r\n animation: rotate-rainbow var(--duration) linear infinite;\r\n filter: blur(var(--blur));\r\n padding: var(--borderWidth);\r\n position: absolute;\r\n inset: -200%;\r\n z-index: 0;\r\n }\r\n\r\n .btn-content {\r\n border-radius: var(--borderRadius);\r\n background-color: var(--bgColor);\r\n z-index: 10;\r\n position: relative;\r\n }", "type": "registry:component" } ] diff --git a/public/r/gradient-hero.json b/public/r/gradient-hero.json index 130e612..15e4866 100644 --- a/public/r/gradient-hero.json +++ b/public/r/gradient-hero.json @@ -15,17 +15,17 @@ "files": [ { "path": "./src/components/nurui/gradient-hero.tsx", - "content": "import React from \"react\";\nimport { Button } from \"@/components/nurui/button\";\nimport { ArrowRight, Github, Linkedin, Mail } from \"lucide-react\";\nimport Link from \"next/link\";\nimport { GradientGridHero } from \"@/components/nurui/gradient-grid-hero\";\n\nconst GradientHero = () => {\n return (\n
\n
\n
\n
\n
\n
\n\n
\n
\n
\n
\n \n Software Engineer & Creative Developer\n \n \n
\n
\n

\n Hi, I'm\n \n Afsar Mahmud\n \n

\n

\n I craft exceptional digital experiences with code, creativity, and a\n passion for innovation.\n

\n
\n \n \n Contact Me\n \n
\n
\n \n \n \n GitHub\n \n \n \n \n \n LinkedIn\n \n \n \n \n \n Email\n \n \n
\n
\n
\n \n
\n
\n\n
\n
\n
\n
\n
\n
\n );\n};\n\nexport default GradientHero;", + "content": "import React from \"react\";\r\nimport { Button } from \"@/components/nurui/button\";\r\nimport { ArrowRight, Github, Linkedin, Mail } from \"lucide-react\";\r\nimport Link from \"next/link\";\r\nimport { GradientGridHero } from \"@/components/nurui/gradient-grid-hero\";\r\n\r\nconst GradientHero = () => {\r\n return (\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n
\r\n
\r\n \r\n Software Engineer & Creative Developer\r\n \r\n \r\n
\r\n
\r\n

\r\n Hi, I'm\r\n \r\n Afsar Mahmud\r\n \r\n

\r\n

\r\n I craft exceptional digital experiences with code, creativity, and a\r\n passion for innovation.\r\n

\r\n
\r\n \r\n \r\n Contact Me\r\n \r\n
\r\n
\r\n \r\n \r\n \r\n GitHub\r\n \r\n \r\n \r\n \r\n \r\n LinkedIn\r\n \r\n \r\n \r\n \r\n \r\n Email\r\n \r\n \r\n
\r\n
\r\n
\r\n \r\n
\r\n
\r\n\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n );\r\n};\r\n\r\nexport default GradientHero;", "type": "registry:component" }, { "path": "./src/components/nurui/gradient-grid-hero.tsx", - "content": "\"use client\"\n\nimport { useEffect, useRef } from \"react\"\nimport { motion } from \"framer-motion\"\n\nexport function GradientGridHero() {\n const canvasRef = useRef(null)\n\n useEffect(() => {\n const canvas = canvasRef.current\n if (!canvas) return\n\n const ctx = canvas.getContext(\"2d\")\n if (!ctx) return\n\n let devicePixelRatio: number\n\n // Set canvas dimensions\n const setCanvasDimensions = () => {\n devicePixelRatio = window.devicePixelRatio || 1\n const rect = canvas.getBoundingClientRect()\n\n canvas.width = rect.width * devicePixelRatio\n canvas.height = rect.height * devicePixelRatio\n\n ctx.scale(devicePixelRatio, devicePixelRatio)\n }\n\n setCanvasDimensions()\n window.addEventListener(\"resize\", setCanvasDimensions)\n\n // Mouse position\n let mouseX = 0\n let mouseY = 0\n let targetX = 0\n let targetY = 0\n\n window.addEventListener(\"mousemove\", (e) => {\n const rect = canvas.getBoundingClientRect()\n targetX = e.clientX - rect.left\n targetY = e.clientY - rect.top\n })\n\n // Particle class\n class Particle {\n x: number\n y: number\n size: number\n baseX: number\n baseY: number\n density: number\n color: string\n distance: number\n\n constructor(x: number, y: number) {\n this.x = x\n this.y = y\n this.baseX = x\n this.baseY = y\n this.size = Math.random() * 5 + 2\n this.density = Math.random() * 30 + 1\n this.distance = 0\n\n // Create a gradient from purple to pink\n const hue = Math.random() * 60 + 270 // 270-330 range for purples and pinks\n this.color = `hsl(${hue}, 70%, 60%)`\n }\n\n update() {\n // Calculate distance between mouse and particle\n const dx = mouseX - this.x\n const dy = mouseY - this.y\n this.distance = Math.sqrt(dx * dx + dy * dy)\n\n const forceDirectionX = dx / this.distance\n const forceDirectionY = dy / this.distance\n\n const maxDistance = 100\n const force = (maxDistance - this.distance) / maxDistance\n\n if (this.distance < maxDistance) {\n const directionX = forceDirectionX * force * this.density\n const directionY = forceDirectionY * force * this.density\n\n this.x -= directionX\n this.y -= directionY\n } else {\n if (this.x !== this.baseX) {\n const dx = this.x - this.baseX\n this.x -= dx / 10\n }\n if (this.y !== this.baseY) {\n const dy = this.y - this.baseY\n this.y -= dy / 10\n }\n }\n }\n\n draw(ctx: CanvasRenderingContext2D | null) {\n if (!ctx) return\n ctx.fillStyle = this.color\n ctx.beginPath()\n ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2)\n ctx.closePath()\n ctx.fill()\n }\n }\n\n // Create particle grid\n const particlesArray: Particle[] = []\n const gridSize = 30\n\n function init() {\n particlesArray.length = 0\n\n if (!canvas) return;\n\n const canvasWidth = canvas.width / devicePixelRatio\n const canvasHeight = canvas.height / devicePixelRatio\n\n const numX = Math.floor(canvasWidth / gridSize)\n const numY = Math.floor(canvasHeight / gridSize)\n\n for (let y = 0; y < numY; y++) {\n for (let x = 0; x < numX; x++) {\n const posX = x * gridSize + gridSize / 2\n const posY = y * gridSize + gridSize / 2\n particlesArray.push(new Particle(posX, posY))\n }\n }\n }\n\n init()\n\n // Animation loop\n const animate = () => {\n ctx.clearRect(0, 0, canvas.width, canvas.height)\n\n // Smooth mouse following\n mouseX += (targetX - mouseX) * 0.1\n mouseY += (targetY - mouseY) * 0.1\n\n // Draw connections\n for (let i = 0; i < particlesArray.length; i++) {\n particlesArray[i].update()\n particlesArray[i].draw(ctx)\n\n // Draw connections\n for (let j = i; j < particlesArray.length; j++) {\n const dx = particlesArray[i].x - particlesArray[j].x\n const dy = particlesArray[i].y - particlesArray[j].y\n const distance = Math.sqrt(dx * dx + dy * dy)\n\n if (distance < 30) {\n ctx.beginPath()\n ctx.strokeStyle = `rgba(180, 120, 255, ${0.2 - distance / 150})`\n ctx.lineWidth = 0.5\n ctx.moveTo(particlesArray[i].x, particlesArray[i].y)\n ctx.lineTo(particlesArray[j].x, particlesArray[j].y)\n ctx.stroke()\n }\n }\n }\n\n requestAnimationFrame(animate)\n }\n\n animate()\n\n // Handle window resize\n window.addEventListener(\"resize\", init)\n\n return () => {\n window.removeEventListener(\"resize\", setCanvasDimensions)\n window.removeEventListener(\"resize\", init)\n }\n }, [])\n\n return (\n \n \n \n )\n}", + "content": "\"use client\"\r\n\r\nimport { useEffect, useRef } from \"react\"\r\nimport { motion } from \"framer-motion\"\r\n\r\nexport function GradientGridHero() {\r\n const canvasRef = useRef(null)\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current\r\n if (!canvas) return\r\n\r\n const ctx = canvas.getContext(\"2d\")\r\n if (!ctx) return\r\n\r\n let devicePixelRatio: number\r\n\r\n // Set canvas dimensions\r\n const setCanvasDimensions = () => {\r\n devicePixelRatio = window.devicePixelRatio || 1\r\n const rect = canvas.getBoundingClientRect()\r\n\r\n canvas.width = rect.width * devicePixelRatio\r\n canvas.height = rect.height * devicePixelRatio\r\n\r\n ctx.scale(devicePixelRatio, devicePixelRatio)\r\n }\r\n\r\n setCanvasDimensions()\r\n window.addEventListener(\"resize\", setCanvasDimensions)\r\n\r\n // Mouse position\r\n let mouseX = 0\r\n let mouseY = 0\r\n let targetX = 0\r\n let targetY = 0\r\n\r\n window.addEventListener(\"mousemove\", (e) => {\r\n const rect = canvas.getBoundingClientRect()\r\n targetX = e.clientX - rect.left\r\n targetY = e.clientY - rect.top\r\n })\r\n\r\n // Particle class\r\n class Particle {\r\n x: number\r\n y: number\r\n size: number\r\n baseX: number\r\n baseY: number\r\n density: number\r\n color: string\r\n distance: number\r\n\r\n constructor(x: number, y: number) {\r\n this.x = x\r\n this.y = y\r\n this.baseX = x\r\n this.baseY = y\r\n this.size = Math.random() * 5 + 2\r\n this.density = Math.random() * 30 + 1\r\n this.distance = 0\r\n\r\n // Create a gradient from purple to pink\r\n const hue = Math.random() * 60 + 270 // 270-330 range for purples and pinks\r\n this.color = `hsl(${hue}, 70%, 60%)`\r\n }\r\n\r\n update() {\r\n // Calculate distance between mouse and particle\r\n const dx = mouseX - this.x\r\n const dy = mouseY - this.y\r\n this.distance = Math.sqrt(dx * dx + dy * dy)\r\n\r\n const forceDirectionX = dx / this.distance\r\n const forceDirectionY = dy / this.distance\r\n\r\n const maxDistance = 100\r\n const force = (maxDistance - this.distance) / maxDistance\r\n\r\n if (this.distance < maxDistance) {\r\n const directionX = forceDirectionX * force * this.density\r\n const directionY = forceDirectionY * force * this.density\r\n\r\n this.x -= directionX\r\n this.y -= directionY\r\n } else {\r\n if (this.x !== this.baseX) {\r\n const dx = this.x - this.baseX\r\n this.x -= dx / 10\r\n }\r\n if (this.y !== this.baseY) {\r\n const dy = this.y - this.baseY\r\n this.y -= dy / 10\r\n }\r\n }\r\n }\r\n\r\n draw(ctx: CanvasRenderingContext2D | null) {\r\n if (!ctx) return\r\n ctx.fillStyle = this.color\r\n ctx.beginPath()\r\n ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2)\r\n ctx.closePath()\r\n ctx.fill()\r\n }\r\n }\r\n\r\n // Create particle grid\r\n const particlesArray: Particle[] = []\r\n const gridSize = 30\r\n\r\n function init() {\r\n particlesArray.length = 0\r\n\r\n if (!canvas) return;\r\n\r\n const canvasWidth = canvas.width / devicePixelRatio\r\n const canvasHeight = canvas.height / devicePixelRatio\r\n\r\n const numX = Math.floor(canvasWidth / gridSize)\r\n const numY = Math.floor(canvasHeight / gridSize)\r\n\r\n for (let y = 0; y < numY; y++) {\r\n for (let x = 0; x < numX; x++) {\r\n const posX = x * gridSize + gridSize / 2\r\n const posY = y * gridSize + gridSize / 2\r\n particlesArray.push(new Particle(posX, posY))\r\n }\r\n }\r\n }\r\n\r\n init()\r\n\r\n // Animation loop\r\n const animate = () => {\r\n ctx.clearRect(0, 0, canvas.width, canvas.height)\r\n\r\n // Smooth mouse following\r\n mouseX += (targetX - mouseX) * 0.1\r\n mouseY += (targetY - mouseY) * 0.1\r\n\r\n // Draw connections\r\n for (let i = 0; i < particlesArray.length; i++) {\r\n particlesArray[i].update()\r\n particlesArray[i].draw(ctx)\r\n\r\n // Draw connections\r\n for (let j = i; j < particlesArray.length; j++) {\r\n const dx = particlesArray[i].x - particlesArray[j].x\r\n const dy = particlesArray[i].y - particlesArray[j].y\r\n const distance = Math.sqrt(dx * dx + dy * dy)\r\n\r\n if (distance < 30) {\r\n ctx.beginPath()\r\n ctx.strokeStyle = `rgba(180, 120, 255, ${0.2 - distance / 150})`\r\n ctx.lineWidth = 0.5\r\n ctx.moveTo(particlesArray[i].x, particlesArray[i].y)\r\n ctx.lineTo(particlesArray[j].x, particlesArray[j].y)\r\n ctx.stroke()\r\n }\r\n }\r\n }\r\n\r\n requestAnimationFrame(animate)\r\n }\r\n\r\n animate()\r\n\r\n // Handle window resize\r\n window.addEventListener(\"resize\", init)\r\n\r\n return () => {\r\n window.removeEventListener(\"resize\", setCanvasDimensions)\r\n window.removeEventListener(\"resize\", init)\r\n }\r\n }, [])\r\n\r\n return (\r\n \r\n \r\n \r\n )\r\n}", "type": "registry:component" }, { "path": "./src/components/nurui/button.tsx", - "content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline:\n \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n)\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes,\n VariantProps {\n asChild?: boolean\n}\n\nconst Button = React.forwardRef(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n return (\n \n )\n },\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n", + "content": "import * as React from \"react\"\r\nimport { Slot } from \"@radix-ui/react-slot\"\r\nimport { cva, type VariantProps } from \"class-variance-authority\"\r\n\r\nimport { cn } from \"@/lib/utils\"\r\n\r\nconst buttonVariants = cva(\r\n \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\r\n {\r\n variants: {\r\n variant: {\r\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\r\n destructive:\r\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\r\n outline:\r\n \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\r\n secondary:\r\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\r\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\r\n link: \"text-primary underline-offset-4 hover:underline\",\r\n },\r\n size: {\r\n default: \"h-10 px-4 py-2\",\r\n sm: \"h-9 rounded-md px-3\",\r\n lg: \"h-11 rounded-md px-8\",\r\n icon: \"h-10 w-10\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"default\",\r\n size: \"default\",\r\n },\r\n },\r\n)\r\n\r\nexport interface ButtonProps\r\n extends React.ButtonHTMLAttributes,\r\n VariantProps {\r\n asChild?: boolean\r\n}\r\n\r\nconst Button = React.forwardRef(\r\n ({ className, variant, size, asChild = false, ...props }, ref) => {\r\n const Comp = asChild ? Slot : \"button\"\r\n return (\r\n \r\n )\r\n },\r\n)\r\nButton.displayName = \"Button\"\r\n\r\nexport { Button, buttonVariants }\r\n", "type": "registry:component" } ] diff --git a/public/r/gradient-text.json b/public/r/gradient-text.json index 09f6ecc..6017cf0 100644 --- a/public/r/gradient-text.json +++ b/public/r/gradient-text.json @@ -8,12 +8,12 @@ "files": [ { "path": "./src/components/nurui/gradient-text-demo.tsx", - "content": "import React from \"react\";\nimport GradientText from \"@/components/nurui/gradient-text\";\n\nconst GradientTextDemo = () => {\n return (\n
\n \n Welcome to Nur/ui\n \n
\n );\n};\n\nexport default GradientTextDemo;\n", + "content": "import React from \"react\";\r\nimport GradientText from \"@/components/nurui/gradient-text\";\r\n\r\nconst GradientTextDemo = () => {\r\n return (\r\n
\r\n \r\n Welcome to Nur/ui\r\n \r\n
\r\n );\r\n};\r\n\r\nexport default GradientTextDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/gradient-text.tsx", - "content": "import React, { ReactNode } from \"react\";\n\ninterface GradientTextProps {\n children: ReactNode;\n className?: string;\n colors?: string[];\n animationSpeed?: number;\n showBorder?: boolean;\n}\n\nexport default function GradientText({\n children,\n className = \"\",\n colors = [\"#ffaa40\", \"#9c40ff\", \"#ffaa40\"],\n animationSpeed = 8,\n showBorder = false,\n}: GradientTextProps) {\n const gradientStyle = {\n backgroundImage: `linear-gradient(to right, ${colors.join(\", \")})`,\n animationDuration: `${animationSpeed}s`,\n };\n\n return (\n \n {showBorder && (\n \n \n \n )}\n \n {children}\n \n \n );\n}\n", + "content": "import React, { ReactNode } from \"react\";\r\n\r\ninterface GradientTextProps {\r\n children: ReactNode;\r\n className?: string;\r\n colors?: string[];\r\n animationSpeed?: number;\r\n showBorder?: boolean;\r\n}\r\n\r\nexport default function GradientText({\r\n children,\r\n className = \"\",\r\n colors = [\"#ffaa40\", \"#9c40ff\", \"#ffaa40\"],\r\n animationSpeed = 8,\r\n showBorder = false,\r\n}: GradientTextProps) {\r\n const gradientStyle = {\r\n backgroundImage: `linear-gradient(to right, ${colors.join(\", \")})`,\r\n animationDuration: `${animationSpeed}s`,\r\n };\r\n\r\n return (\r\n \r\n {showBorder && (\r\n \r\n \r\n \r\n )}\r\n \r\n {children}\r\n \r\n \r\n );\r\n}\r\n", "type": "registry:component" } ] diff --git a/public/r/hacker-background.json b/public/r/hacker-background.json index 8dc1e41..3c62499 100644 --- a/public/r/hacker-background.json +++ b/public/r/hacker-background.json @@ -10,12 +10,12 @@ "files": [ { "path": "./src/components/nurui/hacker-background-demo.tsx", - "content": "import HackerBackground from \"./hacker-background\";\n\nexport function HackerBackgroundDemo() {\n return (\n
\n \n
\n );\n}\n", + "content": "import HackerBackground from \"./hacker-background\";\r\n\r\nexport function HackerBackgroundDemo() {\r\n return (\r\n
\r\n \r\n
\r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/hacker-background.tsx", - "content": "\"use client\";\nimport React, { useEffect, useRef } from \"react\";\n\ninterface HackerBackgroundProps {\n color?: string;\n fontSize?: number;\n className?: string;\n speed?: number;\n}\n\nconst HackerBackground: React.FC = ({\n color = \"#3ca2fa\",\n fontSize = 15,\n className = \"\",\n speed = 1,\n}) => {\n const canvasRef = useRef(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n const resizeCanvas = () => {\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n };\n\n resizeCanvas();\n window.addEventListener(\"resize\", resizeCanvas);\n\n let animationFrameId: number;\n\n const columns = Math.floor(canvas.width / fontSize);\n const drops: number[] = new Array(columns).fill(1);\n\n const chars =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%^&*()_+\";\n\n let lastTime = 0;\n const interval = 33; // ~30 fps\n\n const draw = (currentTime: number) => {\n animationFrameId = requestAnimationFrame(draw);\n\n if (currentTime - lastTime < interval) return;\n lastTime = currentTime;\n\n ctx.fillStyle = \"rgba(0, 0, 0, 0.05)\";\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n\n ctx.fillStyle = color;\n ctx.font = `${fontSize}px monospace`;\n\n for (let i = 0; i < drops.length; i++) {\n const text = chars[Math.floor(Math.random() * chars.length)];\n ctx.fillText(text, i * fontSize, drops[i] * fontSize);\n\n if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) {\n drops[i] = 0;\n }\n drops[i] += speed; // Use the speed prop to control fall rate\n }\n };\n\n animationFrameId = requestAnimationFrame(draw);\n\n return () => {\n window.removeEventListener(\"resize\", resizeCanvas);\n cancelAnimationFrame(animationFrameId);\n };\n }, [color, fontSize, speed]);\n\n return (\n \n );\n};\n\nexport default HackerBackground;\n", + "content": "\"use client\";\r\nimport React, { useEffect, useRef } from \"react\";\r\n\r\ninterface HackerBackgroundProps {\r\n color?: string;\r\n fontSize?: number;\r\n className?: string;\r\n speed?: number;\r\n}\r\n\r\nconst HackerBackground: React.FC = ({\r\n color = \"#3ca2fa\",\r\n fontSize = 15,\r\n className = \"\",\r\n speed = 1,\r\n}) => {\r\n const canvasRef = useRef(null);\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) return;\r\n\r\n const resizeCanvas = () => {\r\n canvas.width = window.innerWidth;\r\n canvas.height = window.innerHeight;\r\n };\r\n\r\n resizeCanvas();\r\n window.addEventListener(\"resize\", resizeCanvas);\r\n\r\n let animationFrameId: number;\r\n\r\n const columns = Math.floor(canvas.width / fontSize);\r\n const drops: number[] = new Array(columns).fill(1);\r\n\r\n const chars =\r\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%^&*()_+\";\r\n\r\n let lastTime = 0;\r\n const interval = 33; // ~30 fps\r\n\r\n const draw = (currentTime: number) => {\r\n animationFrameId = requestAnimationFrame(draw);\r\n\r\n if (currentTime - lastTime < interval) return;\r\n lastTime = currentTime;\r\n\r\n ctx.fillStyle = \"rgba(0, 0, 0, 0.05)\";\r\n ctx.fillRect(0, 0, canvas.width, canvas.height);\r\n\r\n ctx.fillStyle = color;\r\n ctx.font = `${fontSize}px monospace`;\r\n\r\n for (let i = 0; i < drops.length; i++) {\r\n const text = chars[Math.floor(Math.random() * chars.length)];\r\n ctx.fillText(text, i * fontSize, drops[i] * fontSize);\r\n\r\n if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) {\r\n drops[i] = 0;\r\n }\r\n drops[i] += speed; // Use the speed prop to control fall rate\r\n }\r\n };\r\n\r\n animationFrameId = requestAnimationFrame(draw);\r\n\r\n return () => {\r\n window.removeEventListener(\"resize\", resizeCanvas);\r\n cancelAnimationFrame(animationFrameId);\r\n };\r\n }, [color, fontSize, speed]);\r\n\r\n return (\r\n \r\n );\r\n};\r\n\r\nexport default HackerBackground;\r\n", "type": "registry:component" } ] diff --git a/public/r/hacker-cursor.json b/public/r/hacker-cursor.json index 8158f0c..68cc480 100644 --- a/public/r/hacker-cursor.json +++ b/public/r/hacker-cursor.json @@ -8,12 +8,12 @@ "files": [ { "path": "./src/components/nurui/hacker-cursor-demo.tsx", - "content": "import HackerCursor from \"@/components/nurui/hacker-cursor\";\n\nexport default function HackerCursorDemo() {\n return (\n <>\n

\n Move cursor to see the effect.\n

\n \n \n );\n}\n", + "content": "import HackerCursor from \"@/components/nurui/hacker-cursor\";\r\n\r\nexport default function HackerCursorDemo() {\r\n return (\r\n <>\r\n

\r\n Move cursor to see the effect.\r\n

\r\n \r\n \r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/hacker-cursor.tsx", - "content": "\"use client\";\nimport React, { useRef, useEffect } from \"react\";\n\ninterface CursorTrailProps {\n trailColor?: string;\n dotSize?: number;\n fadeDuration?: number;\n className?: string;\n}\n\nconst HackerCursor: React.FC = ({\n trailColor = \"#D0FBB6\",\n dotSize = 4,\n fadeDuration = 600,\n className = \"fixed inset-0 w-full h-full pointer-events-none z-50\",\n}) => {\n const canvasRef = useRef(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n const resizeCanvas = () => {\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n };\n\n resizeCanvas();\n window.addEventListener(\"resize\", resizeCanvas);\n\n const hexToRGB = (hex: string) => {\n const num = parseInt(hex.replace(\"#\", \"\"), 16);\n return {\n r: (num >> 16) & 255,\n g: (num >> 8) & 255,\n b: num & 255,\n };\n };\n\n const { r, g, b } = hexToRGB(trailColor);\n\n const paintDot = (x: number, y: number) => {\n ctx.fillStyle = `rgba(${r}, ${g}, ${b}, 1)`;\n ctx.fillRect(x, y, dotSize, dotSize);\n };\n\n let lastTime = performance.now();\n\n const fade = () => {\n const now = performance.now();\n const delta = now - lastTime;\n lastTime = now;\n\n const fadeAlpha = delta / fadeDuration;\n\n ctx.fillStyle = `rgba(0, 0, 0, ${fadeAlpha})`;\n ctx.globalCompositeOperation = \"destination-out\";\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.globalCompositeOperation = \"source-over\";\n\n requestAnimationFrame(fade);\n };\n\n requestAnimationFrame(fade);\n\n const handleMouseMove = (e: MouseEvent) => {\n const x = Math.floor(e.clientX / dotSize) * dotSize;\n const y = Math.floor(e.clientY / dotSize) * dotSize;\n paintDot(x, y);\n };\n\n window.addEventListener(\"mousemove\", handleMouseMove);\n\n return () => {\n window.removeEventListener(\"mousemove\", handleMouseMove);\n window.removeEventListener(\"resize\", resizeCanvas);\n };\n }, [trailColor, dotSize, fadeDuration]);\n\n return ;\n};\n\nexport default HackerCursor;\n", + "content": "\"use client\";\r\nimport React, { useRef, useEffect } from \"react\";\r\n\r\ninterface CursorTrailProps {\r\n trailColor?: string;\r\n dotSize?: number;\r\n fadeDuration?: number;\r\n className?: string;\r\n}\r\n\r\nconst HackerCursor: React.FC = ({\r\n trailColor = \"#D0FBB6\",\r\n dotSize = 4,\r\n fadeDuration = 600,\r\n className = \"fixed inset-0 w-full h-full pointer-events-none z-50\",\r\n}) => {\r\n const canvasRef = useRef(null);\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) return;\r\n\r\n const resizeCanvas = () => {\r\n canvas.width = window.innerWidth;\r\n canvas.height = window.innerHeight;\r\n };\r\n\r\n resizeCanvas();\r\n window.addEventListener(\"resize\", resizeCanvas);\r\n\r\n const hexToRGB = (hex: string) => {\r\n const num = parseInt(hex.replace(\"#\", \"\"), 16);\r\n return {\r\n r: (num >> 16) & 255,\r\n g: (num >> 8) & 255,\r\n b: num & 255,\r\n };\r\n };\r\n\r\n const { r, g, b } = hexToRGB(trailColor);\r\n\r\n const paintDot = (x: number, y: number) => {\r\n ctx.fillStyle = `rgba(${r}, ${g}, ${b}, 1)`;\r\n ctx.fillRect(x, y, dotSize, dotSize);\r\n };\r\n\r\n let lastTime = performance.now();\r\n\r\n const fade = () => {\r\n const now = performance.now();\r\n const delta = now - lastTime;\r\n lastTime = now;\r\n\r\n const fadeAlpha = delta / fadeDuration;\r\n\r\n ctx.fillStyle = `rgba(0, 0, 0, ${fadeAlpha})`;\r\n ctx.globalCompositeOperation = \"destination-out\";\r\n ctx.fillRect(0, 0, canvas.width, canvas.height);\r\n ctx.globalCompositeOperation = \"source-over\";\r\n\r\n requestAnimationFrame(fade);\r\n };\r\n\r\n requestAnimationFrame(fade);\r\n\r\n const handleMouseMove = (e: MouseEvent) => {\r\n const x = Math.floor(e.clientX / dotSize) * dotSize;\r\n const y = Math.floor(e.clientY / dotSize) * dotSize;\r\n paintDot(x, y);\r\n };\r\n\r\n window.addEventListener(\"mousemove\", handleMouseMove);\r\n\r\n return () => {\r\n window.removeEventListener(\"mousemove\", handleMouseMove);\r\n window.removeEventListener(\"resize\", resizeCanvas);\r\n };\r\n }, [trailColor, dotSize, fadeDuration]);\r\n\r\n return ;\r\n};\r\n\r\nexport default HackerCursor;\r\n", "type": "registry:component" } ] diff --git a/public/r/hover-footer.json b/public/r/hover-footer.json index a66905c..f4a9071 100644 --- a/public/r/hover-footer.json +++ b/public/r/hover-footer.json @@ -13,17 +13,17 @@ "files": [ { "path": "./src/components/nurui/hover-footer.tsx", - "content": "import React from \"react\";\nimport {\n Mail,\n Phone,\n MapPin,\n Facebook,\n Instagram,\n Twitter,\n Dribbble,\n Globe,\n} from \"lucide-react\";\nimport FooterBackgroundGradient from \"./footer-background-gradient\";\nimport { TextHoverEffect } from \"./text-hover-effect\";\n\nfunction HoverFooter() {\n return (\n
\n
\n {/* Main grid for the footer content */}\n\n
\n {/* Section 1: Pollen brand and description */}\n\n
\n
\n \n ♥\n \n\n Nur/ui\n
\n\n

\n Nur UI is a modern React and Next.js based UI component library.\n

\n
\n\n {/* Section 2: About Us links */}\n\n
\n

About Us

\n\n \n
\n\n {/* Section 3: Helpful Links */}\n\n
\n

\n Helpful Links\n

\n\n \n
\n\n {/* Section 4: Contact Us */}\n\n
\n

\n Contact Us\n

\n\n
    \n
  • \n \n\n \n hello@nurui.com\n \n
  • \n\n
  • \n \n\n \n +91 86373 73116\n \n
  • \n\n
  • \n \n\n \n Sylhet, Bangladesh\n \n
  • \n
\n
\n
\n\n {/* Separator line */}\n\n
\n\n {/* Bottom section: social media and copyright */}\n\n
\n {/* Social Media Icons */}\n\n
\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n
\n\n {/* Copyright text */}\n\n
\n

© 2025 Nurui. All rights reserved.

\n
\n
\n
\n\n \n\n \n
\n );\n}\n\nexport default HoverFooter;\n", + "content": "import React from \"react\";\r\nimport {\r\n Mail,\r\n Phone,\r\n MapPin,\r\n Facebook,\r\n Instagram,\r\n Twitter,\r\n Dribbble,\r\n Globe,\r\n} from \"lucide-react\";\r\nimport FooterBackgroundGradient from \"./footer-background-gradient\";\r\nimport { TextHoverEffect } from \"./text-hover-effect\";\r\n\r\nfunction HoverFooter() {\r\n return (\r\n
\r\n
\r\n {/* Main grid for the footer content */}\r\n\r\n
\r\n {/* Section 1: Pollen brand and description */}\r\n\r\n
\r\n
\r\n \r\n ♥\r\n \r\n\r\n Nur/ui\r\n
\r\n\r\n

\r\n Nur UI is a modern React and Next.js based UI component library.\r\n

\r\n
\r\n\r\n {/* Section 2: About Us links */}\r\n\r\n
\r\n

About Us

\r\n\r\n \r\n
\r\n\r\n {/* Section 3: Helpful Links */}\r\n\r\n
\r\n

\r\n Helpful Links\r\n

\r\n\r\n \r\n
\r\n\r\n {/* Section 4: Contact Us */}\r\n\r\n
\r\n

\r\n Contact Us\r\n

\r\n\r\n
    \r\n
  • \r\n \r\n\r\n \r\n hello@nurui.com\r\n \r\n
  • \r\n\r\n
  • \r\n \r\n\r\n \r\n +91 86373 73116\r\n \r\n
  • \r\n\r\n
  • \r\n \r\n\r\n \r\n Sylhet, Bangladesh\r\n \r\n
  • \r\n
\r\n
\r\n
\r\n\r\n {/* Separator line */}\r\n\r\n
\r\n\r\n {/* Bottom section: social media and copyright */}\r\n\r\n
\r\n {/* Social Media Icons */}\r\n\r\n
\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n
\r\n\r\n {/* Copyright text */}\r\n\r\n
\r\n

© 2025 Nurui. All rights reserved.

\r\n
\r\n
\r\n
\r\n\r\n \r\n\r\n \r\n
\r\n );\r\n}\r\n\r\nexport default HoverFooter;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/text-hover-effect.tsx", - "content": "\"use client\";\nimport React, { useRef, useEffect, useState } from \"react\";\nimport { motion } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\nexport const TextHoverEffect = ({\n text,\n duration,\n className,\n}: {\n text: string;\n duration?: number;\n automatic?: boolean;\n className?: string;\n}) => {\n const svgRef = useRef(null);\n const [cursor, setCursor] = useState({ x: 0, y: 0 });\n const [hovered, setHovered] = useState(false);\n const [maskPosition, setMaskPosition] = useState({ cx: \"50%\", cy: \"50%\" });\n\n useEffect(() => {\n if (svgRef.current && cursor.x !== null && cursor.y !== null) {\n const svgRect = svgRef.current.getBoundingClientRect();\n const cxPercentage = ((cursor.x - svgRect.left) / svgRect.width) * 100;\n const cyPercentage = ((cursor.y - svgRect.top) / svgRect.height) * 100;\n setMaskPosition({\n cx: `${cxPercentage}%`,\n cy: `${cyPercentage}%`,\n });\n }\n }, [cursor]);\n\n return (\n setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n onMouseMove={(e) => setCursor({ x: e.clientX, y: e.clientY })}\n className={cn(\"select-none uppercase cursor-pointer\", className)}\n >\n \n \n {hovered && (\n <>\n \n \n \n \n \n \n )}\n \n\n \n \n \n \n \n \n \n \n \n {text}\n \n \n {text}\n \n \n {text}\n \n \n );\n};\n", + "content": "\"use client\";\r\nimport React, { useRef, useEffect, useState } from \"react\";\r\nimport { motion } from \"motion/react\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nexport const TextHoverEffect = ({\r\n text,\r\n duration,\r\n className,\r\n}: {\r\n text: string;\r\n duration?: number;\r\n automatic?: boolean;\r\n className?: string;\r\n}) => {\r\n const svgRef = useRef(null);\r\n const [cursor, setCursor] = useState({ x: 0, y: 0 });\r\n const [hovered, setHovered] = useState(false);\r\n const [maskPosition, setMaskPosition] = useState({ cx: \"50%\", cy: \"50%\" });\r\n\r\n useEffect(() => {\r\n if (svgRef.current && cursor.x !== null && cursor.y !== null) {\r\n const svgRect = svgRef.current.getBoundingClientRect();\r\n const cxPercentage = ((cursor.x - svgRect.left) / svgRect.width) * 100;\r\n const cyPercentage = ((cursor.y - svgRect.top) / svgRect.height) * 100;\r\n setMaskPosition({\r\n cx: `${cxPercentage}%`,\r\n cy: `${cyPercentage}%`,\r\n });\r\n }\r\n }, [cursor]);\r\n\r\n return (\r\n setHovered(true)}\r\n onMouseLeave={() => setHovered(false)}\r\n onMouseMove={(e) => setCursor({ x: e.clientX, y: e.clientY })}\r\n className={cn(\"select-none uppercase cursor-pointer\", className)}\r\n >\r\n \r\n \r\n {hovered && (\r\n <>\r\n \r\n \r\n \r\n \r\n \r\n \r\n )}\r\n \r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {text}\r\n \r\n \r\n {text}\r\n \r\n \r\n {text}\r\n \r\n \r\n );\r\n};\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/footer-background-gradient.tsx", - "content": "import React from \"react\";\n\nconst FooterBackgroundGradient = () => {\n return (\n \n );\n};\n\nexport default FooterBackgroundGradient;\n", + "content": "import React from \"react\";\r\n\r\nconst FooterBackgroundGradient = () => {\r\n return (\r\n \r\n );\r\n};\r\n\r\nexport default FooterBackgroundGradient;\r\n", "type": "registry:component" } ] diff --git a/public/r/info-card.json b/public/r/info-card.json index 9635e83..b891b3b 100644 --- a/public/r/info-card.json +++ b/public/r/info-card.json @@ -11,17 +11,17 @@ "files": [ { "path": "./src/components/nurui/info-card-demo.tsx", - "content": "import React from \"react\";\nimport { InfoCard } from \"@/components/nurui/info-card\";\n\nconst containerStyle: React.CSSProperties = {\n display: \"flex\",\n gap: 24,\n padding: 24,\n flexWrap: \"wrap\",\n justifyContent: \"center\",\n alignItems: \"flex-start\",\n background: \"none\",\n fontFamily: \"var(--font-family)\",\n margin: 0,\n};\n\nconst fileContainerStyle: React.CSSProperties = {\n width: 388,\n height: 378,\n borderRadius: \"1em\",\n position: \"relative\",\n overflow: \"hidden\",\n padding: 0,\n cursor: \"pointer\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n background: \"none\",\n boxSizing: \"border-box\",\n};\n\nexport const InfoCardDemo: React.FC = () => (\n
\n \n \n
\n \n);\n", + "content": "import React from \"react\";\r\nimport { InfoCard } from \"@/components/nurui/info-card\";\r\n\r\nconst containerStyle: React.CSSProperties = {\r\n display: \"flex\",\r\n gap: 24,\r\n padding: 24,\r\n flexWrap: \"wrap\",\r\n justifyContent: \"center\",\r\n alignItems: \"flex-start\",\r\n background: \"none\",\r\n fontFamily: \"var(--font-family)\",\r\n margin: 0,\r\n};\r\n\r\nconst fileContainerStyle: React.CSSProperties = {\r\n width: 388,\r\n height: 378,\r\n borderRadius: \"1em\",\r\n position: \"relative\",\r\n overflow: \"hidden\",\r\n padding: 0,\r\n cursor: \"pointer\",\r\n display: \"flex\",\r\n justifyContent: \"center\",\r\n alignItems: \"center\",\r\n background: \"none\",\r\n boxSizing: \"border-box\",\r\n};\r\n\r\nexport const InfoCardDemo: React.FC = () => (\r\n
\r\n \r\n \r\n
\r\n \r\n);\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/info-card.tsx", - "content": "\"use client\";\nimport React, { useRef, useState } from \"react\";\nimport \"./styles/info-card.css\"; // Ensure you have the CSS file for styles\n\n// RTL detection for Hebrew/Arabic\nfunction isRTL(text: string) {\n return /[\\u0590-\\u05FF\\u0600-\\u06FF\\u0700-\\u074F]/.test(text);\n}\n\nexport interface InfoCardProps {\n title: string;\n description: string;\n width?: number;\n height?: number;\n borderColor?: string;\n borderBgColor?: string;\n borderWidth?: number;\n borderPadding?: number;\n cardBgColor?: string;\n shadowColor?: string;\n patternColor1?: string;\n patternColor2?: string;\n textColor?: string;\n hoverTextColor?: string;\n fontFamily?: string;\n rtlFontFamily?: string;\n effectBgColor?: string;\n contentPadding?: string;\n}\n\nexport const InfoCard: React.FC = ({\n title,\n description,\n width = 388,\n height = 378,\n borderColor = \"#80eeb4\",\n borderBgColor = \"#242424\",\n borderWidth = 3,\n borderPadding = 14,\n cardBgColor = \"#000\",\n textColor = \"#f5f5f5\",\n hoverTextColor = \"#242424\",\n fontFamily = \"'Roboto Mono', monospace\",\n rtlFontFamily = \"'Montserrat', sans-serif\",\n effectBgColor = \"#80eeb4\",\n contentPadding = \"10px 16px\",\n}) => {\n const [hovered, setHovered] = useState(false);\n const borderRef = useRef(null);\n\n // Mouse movement for rotating border\n const handleMouseMove = (e: React.MouseEvent) => {\n const border = borderRef.current;\n if (!border) return;\n const rect = border.getBoundingClientRect();\n const x = e.clientX - rect.left - rect.width / 2;\n const y = e.clientY - rect.top - rect.height / 2;\n const angle = Math.atan2(y, x);\n border.style.setProperty(\"--rotation\", `${angle}rad`);\n };\n\n // RTL logic\n const rtl = isRTL(title) || isRTL(description);\n const effectiveFont = rtl ? rtlFontFamily : fontFamily;\n const titleDirection = isRTL(title) ? \"rtl\" : \"ltr\";\n const descDirection = isRTL(description) ? \"rtl\" : \"ltr\";\n\n // Sizes for inner card (matches .inner-container: 354x344)\n\n // Pattern background (unchanged, just colors are props)\n\n // Border gradient\n const borderGradient = `conic-gradient(from var(--rotation,0deg), ${borderColor} 0deg, ${borderColor} 90deg, ${borderBgColor} 90deg, ${borderBgColor} 360deg)`;\n\n return (\n setHovered(true)}\n onMouseLeave={() => {\n setHovered(false);\n if (borderRef.current)\n borderRef.current.style.setProperty(\"--rotation\", \"0deg\");\n }}\n style={\n {\n width,\n height,\n border: `${borderWidth}px solid transparent`,\n borderRadius: \"1em\",\n backgroundOrigin: \"border-box\",\n backgroundClip: \"padding-box, border-box\",\n backgroundImage: `linear-gradient(${cardBgColor}, ${cardBgColor}), ${borderGradient}`,\n padding: borderPadding,\n boxSizing: \"border-box\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n cursor: \"pointer\",\n userSelect: \"none\",\n transition: \"box-shadow 0.3s\",\n position: \"relative\",\n fontFamily: effectiveFont,\n } as React.CSSProperties\n }\n >\n \n \n \n {title}\n \n \n \n \n {description}\n

\n \n \n );\n};", + "content": "\"use client\";\r\nimport React, { useRef, useState } from \"react\";\r\nimport \"./styles/info-card.css\"; // Ensure you have the CSS file for styles\r\n\r\n// RTL detection for Hebrew/Arabic\r\nfunction isRTL(text: string) {\r\n return /[\\u0590-\\u05FF\\u0600-\\u06FF\\u0700-\\u074F]/.test(text);\r\n}\r\n\r\nexport interface InfoCardProps {\r\n title: string;\r\n description: string;\r\n width?: number;\r\n height?: number;\r\n borderColor?: string;\r\n borderBgColor?: string;\r\n borderWidth?: number;\r\n borderPadding?: number;\r\n cardBgColor?: string;\r\n shadowColor?: string;\r\n patternColor1?: string;\r\n patternColor2?: string;\r\n textColor?: string;\r\n hoverTextColor?: string;\r\n fontFamily?: string;\r\n rtlFontFamily?: string;\r\n effectBgColor?: string;\r\n contentPadding?: string;\r\n}\r\n\r\nexport const InfoCard: React.FC = ({\r\n title,\r\n description,\r\n width = 388,\r\n height = 378,\r\n borderColor = \"#80eeb4\",\r\n borderBgColor = \"#242424\",\r\n borderWidth = 3,\r\n borderPadding = 14,\r\n cardBgColor = \"#000\",\r\n textColor = \"#f5f5f5\",\r\n hoverTextColor = \"#242424\",\r\n fontFamily = \"'Roboto Mono', monospace\",\r\n rtlFontFamily = \"'Montserrat', sans-serif\",\r\n effectBgColor = \"#80eeb4\",\r\n contentPadding = \"10px 16px\",\r\n}) => {\r\n const [hovered, setHovered] = useState(false);\r\n const borderRef = useRef(null);\r\n\r\n // Mouse movement for rotating border\r\n const handleMouseMove = (e: React.MouseEvent) => {\r\n const border = borderRef.current;\r\n if (!border) return;\r\n const rect = border.getBoundingClientRect();\r\n const x = e.clientX - rect.left - rect.width / 2;\r\n const y = e.clientY - rect.top - rect.height / 2;\r\n const angle = Math.atan2(y, x);\r\n border.style.setProperty(\"--rotation\", `${angle}rad`);\r\n };\r\n\r\n // RTL logic\r\n const rtl = isRTL(title) || isRTL(description);\r\n const effectiveFont = rtl ? rtlFontFamily : fontFamily;\r\n const titleDirection = isRTL(title) ? \"rtl\" : \"ltr\";\r\n const descDirection = isRTL(description) ? \"rtl\" : \"ltr\";\r\n\r\n // Sizes for inner card (matches .inner-container: 354x344)\r\n\r\n // Pattern background (unchanged, just colors are props)\r\n\r\n // Border gradient\r\n const borderGradient = `conic-gradient(from var(--rotation,0deg), ${borderColor} 0deg, ${borderColor} 90deg, ${borderBgColor} 90deg, ${borderBgColor} 360deg)`;\r\n\r\n return (\r\n setHovered(true)}\r\n onMouseLeave={() => {\r\n setHovered(false);\r\n if (borderRef.current)\r\n borderRef.current.style.setProperty(\"--rotation\", \"0deg\");\r\n }}\r\n style={\r\n {\r\n width,\r\n height,\r\n border: `${borderWidth}px solid transparent`,\r\n borderRadius: \"1em\",\r\n backgroundOrigin: \"border-box\",\r\n backgroundClip: \"padding-box, border-box\",\r\n backgroundImage: `linear-gradient(${cardBgColor}, ${cardBgColor}), ${borderGradient}`,\r\n padding: borderPadding,\r\n boxSizing: \"border-box\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n cursor: \"pointer\",\r\n userSelect: \"none\",\r\n transition: \"box-shadow 0.3s\",\r\n position: \"relative\",\r\n fontFamily: effectiveFont,\r\n } as React.CSSProperties\r\n }\r\n >\r\n \r\n \r\n \r\n {title}\r\n \r\n \r\n \r\n \r\n {description}\r\n

\r\n \r\n \r\n );\r\n};", "type": "registry:component" }, { "path": "./src/components/nurui/styles/info-card.css", - "content": "@import \"tw-animate-css\";\n\n:root {\n --border-color-1: #ff5613;\n --border-color-2: #9f4eff;\n --border-color-3: #2196f3;\n --border-bg-color: #f5f5f5;\n --card-bg-color: #fff;\n --shadow-color: #e0e0e0;\n --text-color: #242424;\n --hover-text-color-1: #000;\n --hover-text-color-2: #fff;\n --hover-text-color-3: #fff;\n --font-family: \"Roboto Mono\", monospace;\n --rtl-font-family: \"Montserrat\", sans-serif;\n --pattern-color1: rgba(200, 200, 200, 0.1);\n --pattern-color2: rgba(220, 220, 220, 0.1);\n}\n\n.dark {\n --border-color-1: #daff3e;\n --border-color-2: #9f4eff;\n --border-color-3: #2196f3;\n --border-bg-color: #242424;\n --card-bg-color: #000;\n --shadow-color: #242424;\n --text-color: #f5f5f5;\n --hover-text-color-1: #242424;\n --hover-text-color-2: #000;\n --hover-text-color-3: #242424;\n --font-family: \"Roboto Mono\", monospace;\n --rtl-font-family: \"Montserrat\", sans-serif;\n --pattern-color1: rgba(230, 230, 230, 0.15);\n --pattern-color2: rgba(240, 240, 240, 0.15);\n}\n", + "content": "@import \"tw-animate-css\";\r\n\r\n:root {\r\n --border-color-1: #ff5613;\r\n --border-color-2: #9f4eff;\r\n --border-color-3: #2196f3;\r\n --border-bg-color: #f5f5f5;\r\n --card-bg-color: #fff;\r\n --shadow-color: #e0e0e0;\r\n --text-color: #242424;\r\n --hover-text-color-1: #000;\r\n --hover-text-color-2: #fff;\r\n --hover-text-color-3: #fff;\r\n --font-family: \"Roboto Mono\", monospace;\r\n --rtl-font-family: \"Montserrat\", sans-serif;\r\n --pattern-color1: rgba(200, 200, 200, 0.1);\r\n --pattern-color2: rgba(220, 220, 220, 0.1);\r\n}\r\n\r\n.dark {\r\n --border-color-1: #daff3e;\r\n --border-color-2: #9f4eff;\r\n --border-color-3: #2196f3;\r\n --border-bg-color: #242424;\r\n --card-bg-color: #000;\r\n --shadow-color: #242424;\r\n --text-color: #f5f5f5;\r\n --hover-text-color-1: #242424;\r\n --hover-text-color-2: #000;\r\n --hover-text-color-3: #242424;\r\n --font-family: \"Roboto Mono\", monospace;\r\n --rtl-font-family: \"Montserrat\", sans-serif;\r\n --pattern-color1: rgba(230, 230, 230, 0.15);\r\n --pattern-color2: rgba(240, 240, 240, 0.15);\r\n}\r\n", "type": "registry:component" } ] diff --git a/public/r/jump-background.json b/public/r/jump-background.json index c5299c7..26acdc5 100644 --- a/public/r/jump-background.json +++ b/public/r/jump-background.json @@ -12,12 +12,12 @@ "files": [ { "path": "./src/components/nurui/jump-background-demo.tsx", - "content": "import { JumpsBackground } from \"@/components/nurui/jump-background\";\n\nexport default function JumpBackgroundDemo() {\n return (\n
\n \n
\n

\n Jump Background\n

\n
\n
\n
\n );\n}\n", + "content": "import { JumpsBackground } from \"@/components/nurui/jump-background\";\r\n\r\nexport default function JumpBackgroundDemo() {\r\n return (\r\n
\r\n \r\n
\r\n

\r\n Jump Background\r\n

\r\n
\r\n
\r\n
\r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/jump-background.tsx", - "content": "\"use client\";\n\nimport type React from \"react\";\nimport { useMemo, type HTMLAttributes } from \"react\";\nimport { motion } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface JumpsBackgroundProps extends HTMLAttributes {\n children: React.ReactNode;\n speed?: number;\n color?: string;\n height?: number;\n line?: number;\n}\n\nconst WarpLine = ({\n angle,\n delay,\n color,\n height,\n duration,\n}: {\n angle: number;\n delay: number;\n color: string;\n height: number;\n duration: number;\n}) => {\n return (\n \n );\n};\n\nexport const JumpsBackground: React.FC = ({\n children,\n className,\n speed = 1,\n height = 1,\n color = \"#3ca2fa\",\n line = 50,\n ...props\n}) => {\n const warpLines = useMemo(() => {\n return Array.from({ length: line }, (_, i) => ({\n id: i,\n angle: (360 / line) * i + Math.random(),\n delay: Math.random() * speed * 2,\n duration: speed * 2 + Math.random(),\n }));\n }, [speed, line]);\n\n return (\n
\n {warpLines.map((line) => (\n \n ))}\n
{children}
\n
\n );\n};\n", + "content": "\"use client\";\r\n\r\nimport type React from \"react\";\r\nimport { useMemo, type HTMLAttributes } from \"react\";\r\nimport { motion } from \"motion/react\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\ninterface JumpsBackgroundProps extends HTMLAttributes {\r\n children: React.ReactNode;\r\n speed?: number;\r\n color?: string;\r\n height?: number;\r\n line?: number;\r\n}\r\n\r\nconst WarpLine = ({\r\n angle,\r\n delay,\r\n color,\r\n height,\r\n duration,\r\n}: {\r\n angle: number;\r\n delay: number;\r\n color: string;\r\n height: number;\r\n duration: number;\r\n}) => {\r\n return (\r\n \r\n );\r\n};\r\n\r\nexport const JumpsBackground: React.FC = ({\r\n children,\r\n className,\r\n speed = 1,\r\n height = 1,\r\n color = \"#3ca2fa\",\r\n line = 50,\r\n ...props\r\n}) => {\r\n const warpLines = useMemo(() => {\r\n return Array.from({ length: line }, (_, i) => ({\r\n id: i,\r\n angle: (360 / line) * i + Math.random(),\r\n delay: Math.random() * speed * 2,\r\n duration: speed * 2 + Math.random(),\r\n }));\r\n }, [speed, line]);\r\n\r\n return (\r\n
\r\n {warpLines.map((line) => (\r\n \r\n ))}\r\n
{children}
\r\n
\r\n );\r\n};\r\n", "type": "registry:component" } ] diff --git a/public/r/magnet-button.json b/public/r/magnet-button.json index 8f53305..4745b20 100644 --- a/public/r/magnet-button.json +++ b/public/r/magnet-button.json @@ -13,12 +13,12 @@ "files": [ { "path": "./src/components/nurui/magnet-button-demo.tsx", - "content": "import React from \"react\";\nimport MagnetButton from \"@/components/nurui/magnet-button\";\n\nconst MagnetButtonDemo = () => {\n return (\n
\n \n
\n );\n};\n\nexport default MagnetButtonDemo;\n", + "content": "import React from \"react\";\r\nimport MagnetButton from \"@/components/nurui/magnet-button\";\r\n\r\nconst MagnetButtonDemo = () => {\r\n return (\r\n
\r\n \r\n
\r\n );\r\n};\r\n\r\nexport default MagnetButtonDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/magnet-button.tsx", - "content": "\"use client\";\nimport { cn } from \"@/lib/utils\";\nimport { motion, useAnimation } from \"motion/react\";\nimport { Magnet } from \"lucide-react\";\nimport { useEffect, useState, useCallback } from \"react\";\nimport { Button } from \"@/components/ui/button\";\n\ninterface AttractButtonProps\n extends React.ButtonHTMLAttributes {\n particleCount?: number;\n attractRadius?: number;\n}\n\ninterface Particle {\n id: number;\n x: number;\n y: number;\n}\n\nexport default function MagnetButton({\n className,\n particleCount = 12,\n ...props\n}: AttractButtonProps) {\n const [isAttracting, setIsAttracting] = useState(false);\n const [particles, setParticles] = useState([]);\n const particlesControl = useAnimation();\n\n useEffect(() => {\n const newParticles = Array.from({ length: particleCount }, (_, i) => ({\n id: i,\n x: Math.random() * 360 - 180,\n y: Math.random() * 360 - 180,\n }));\n setParticles(newParticles);\n }, [particleCount]);\n\n const handleInteractionStart = useCallback(async () => {\n setIsAttracting(true);\n await particlesControl.start({\n x: 0,\n y: 0,\n transition: {\n type: \"spring\",\n stiffness: 50,\n damping: 10,\n },\n });\n }, [particlesControl]);\n\n const handleInteractionEnd = useCallback(async () => {\n setIsAttracting(false);\n await particlesControl.start((i) => ({\n x: particles[i].x,\n y: particles[i].y,\n transition: {\n type: \"spring\",\n stiffness: 100,\n damping: 15,\n },\n }));\n }, [particlesControl, particles]);\n\n return (\n \n {particles.map((_, index) => (\n \n ))}\n \n \n {isAttracting ? \"Attracting\" : \"Hover me\"}\n \n \n );\n}\n", + "content": "\"use client\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { motion, useAnimation } from \"motion/react\";\r\nimport { Magnet } from \"lucide-react\";\r\nimport { useEffect, useState, useCallback } from \"react\";\r\nimport { Button } from \"@/components/ui/button\";\r\n\r\ninterface AttractButtonProps\r\n extends React.ButtonHTMLAttributes {\r\n particleCount?: number;\r\n attractRadius?: number;\r\n}\r\n\r\ninterface Particle {\r\n id: number;\r\n x: number;\r\n y: number;\r\n}\r\n\r\nexport default function MagnetButton({\r\n className,\r\n particleCount = 12,\r\n ...props\r\n}: AttractButtonProps) {\r\n const [isAttracting, setIsAttracting] = useState(false);\r\n const [particles, setParticles] = useState([]);\r\n const particlesControl = useAnimation();\r\n\r\n useEffect(() => {\r\n const newParticles = Array.from({ length: particleCount }, (_, i) => ({\r\n id: i,\r\n x: Math.random() * 360 - 180,\r\n y: Math.random() * 360 - 180,\r\n }));\r\n setParticles(newParticles);\r\n }, [particleCount]);\r\n\r\n const handleInteractionStart = useCallback(async () => {\r\n setIsAttracting(true);\r\n await particlesControl.start({\r\n x: 0,\r\n y: 0,\r\n transition: {\r\n type: \"spring\",\r\n stiffness: 50,\r\n damping: 10,\r\n },\r\n });\r\n }, [particlesControl]);\r\n\r\n const handleInteractionEnd = useCallback(async () => {\r\n setIsAttracting(false);\r\n await particlesControl.start((i) => ({\r\n x: particles[i].x,\r\n y: particles[i].y,\r\n transition: {\r\n type: \"spring\",\r\n stiffness: 100,\r\n damping: 15,\r\n },\r\n }));\r\n }, [particlesControl, particles]);\r\n\r\n return (\r\n \r\n {particles.map((_, index) => (\r\n \r\n ))}\r\n \r\n \r\n {isAttracting ? \"Attracting\" : \"Hover me\"}\r\n \r\n \r\n );\r\n}\r\n", "type": "registry:component" } ] diff --git a/public/r/marquee-testimonial.json b/public/r/marquee-testimonial.json index 6e87c4f..4bae7a6 100644 --- a/public/r/marquee-testimonial.json +++ b/public/r/marquee-testimonial.json @@ -12,27 +12,27 @@ "files": [ { "path": "./src/components/nurui/marquee-testimonial-demo.tsx", - "content": "import { Marquee } from \"@/components/nurui/marque\";\nimport TestimonialCard from \"@/components/nurui/testimonial-card\";\nimport { testimonialData } from \"@/data/testimonial\";\n\nexport default function MarqueeTestimonialDemo() {\n return (\n \n {/* title */}\n \n\n {/* testimonial cards */}\n
\n {[\n { data: testimonialData?.slice(0, 10), reverse: true },\n { data: testimonialData?.slice(10, 20), reverse: false },\n // { data: data?.slice(20, data?.length), reverse: true },\n ].map((item, idx) => (\n
\n \n );\n}\n", + "content": "import { Marquee } from \"@/components/nurui/marque\";\r\nimport TestimonialCard from \"@/components/nurui/testimonial-card\";\r\nimport { testimonialData } from \"@/data/testimonial\";\r\n\r\nexport default function MarqueeTestimonialDemo() {\r\n return (\r\n \r\n {/* title */}\r\n \r\n\r\n {/* testimonial cards */}\r\n
\r\n {[\r\n { data: testimonialData?.slice(0, 10), reverse: true },\r\n { data: testimonialData?.slice(10, 20), reverse: false },\r\n // { data: data?.slice(20, data?.length), reverse: true },\r\n ].map((item, idx) => (\r\n
\r\n \r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/marque.tsx", - "content": "import { cn } from \"@/lib/utils\";\nimport { ComponentPropsWithoutRef } from \"react\";\n\ninterface MarqueeProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * Optional CSS class name to apply custom styles\n */\n className?: string;\n /**\n * Whether to reverse the animation direction\n * @default false\n */\n reverse?: boolean;\n /**\n * Whether to pause the animation on hover\n * @default false\n */\n pauseOnHover?: boolean;\n /**\n * Content to be displayed in the marquee\n */\n children: React.ReactNode;\n /**\n * Whether to animate vertically instead of horizontally\n * @default false\n */\n vertical?: boolean;\n /**\n * Number of times to repeat the content\n * @default 4\n */\n repeat?: number;\n itemsCenter?: string;\n}\n\nexport function Marquee({\n className,\n reverse = false,\n pauseOnHover = false,\n children,\n vertical = false,\n repeat = 4,\n itemsCenter = \"\",\n ...props\n}: MarqueeProps) {\n return (\n \n {Array(repeat)\n .fill(0)\n .map((_, i) => (\n \n {children}\n \n ))}\n \n );\n}\n", + "content": "import { cn } from \"@/lib/utils\";\r\nimport { ComponentPropsWithoutRef } from \"react\";\r\n\r\ninterface MarqueeProps extends ComponentPropsWithoutRef<\"div\"> {\r\n /**\r\n * Optional CSS class name to apply custom styles\r\n */\r\n className?: string;\r\n /**\r\n * Whether to reverse the animation direction\r\n * @default false\r\n */\r\n reverse?: boolean;\r\n /**\r\n * Whether to pause the animation on hover\r\n * @default false\r\n */\r\n pauseOnHover?: boolean;\r\n /**\r\n * Content to be displayed in the marquee\r\n */\r\n children: React.ReactNode;\r\n /**\r\n * Whether to animate vertically instead of horizontally\r\n * @default false\r\n */\r\n vertical?: boolean;\r\n /**\r\n * Number of times to repeat the content\r\n * @default 4\r\n */\r\n repeat?: number;\r\n itemsCenter?: string;\r\n}\r\n\r\nexport function Marquee({\r\n className,\r\n reverse = false,\r\n pauseOnHover = false,\r\n children,\r\n vertical = false,\r\n repeat = 4,\r\n itemsCenter = \"\",\r\n ...props\r\n}: MarqueeProps) {\r\n return (\r\n \r\n {Array(repeat)\r\n .fill(0)\r\n .map((_, i) => (\r\n \r\n {children}\r\n \r\n ))}\r\n \r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/testimonial-card.tsx", - "content": "import { FaQuoteLeft } from \"react-icons/fa6\";\nimport Image from \"next/image\";\nimport RatingStars from \"@/components/nurui/rating-star\";\n\ninterface IProps {\n position: string;\n name: string;\n review: string;\n marginTop?: string;\n src: string;\n}\n\nexport default function TestimonialCard({\n position,\n name,\n review,\n marginTop,\n src,\n}: IProps) {\n return (\n \n \n

\n {review}\n

\n\n
\n
\n \n
\n

{name}

\n

{position}

\n
\n
\n\n \n
\n \n \n );\n}\n", + "content": "import { FaQuoteLeft } from \"react-icons/fa6\";\r\nimport Image from \"next/image\";\r\nimport RatingStars from \"@/components/nurui/rating-star\";\r\n\r\ninterface IProps {\r\n position: string;\r\n name: string;\r\n review: string;\r\n marginTop?: string;\r\n src: string;\r\n}\r\n\r\nexport default function TestimonialCard({\r\n position,\r\n name,\r\n review,\r\n marginTop,\r\n src,\r\n}: IProps) {\r\n return (\r\n \r\n \r\n

\r\n {review}\r\n

\r\n\r\n
\r\n
\r\n \r\n
\r\n

{name}

\r\n

{position}

\r\n
\r\n
\r\n\r\n \r\n
\r\n \r\n \r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/rating-star.tsx", - "content": "import RatingIcon from \"@/components/nurui/rating-icon\";\n\nconst RatingStars = ({\n count = 5,\n size,\n}: {\n size?: string;\n count?: number;\n}) => (\n
\n {Array.from({ length: count }).map((_, i) => (\n \n ))}\n
\n);\n\nexport default RatingStars;\n", + "content": "import RatingIcon from \"@/components/nurui/rating-icon\";\r\n\r\nconst RatingStars = ({\r\n count = 5,\r\n size,\r\n}: {\r\n size?: string;\r\n count?: number;\r\n}) => (\r\n
\r\n {Array.from({ length: count }).map((_, i) => (\r\n \r\n ))}\r\n
\r\n);\r\n\r\nexport default RatingStars;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/rating-icon.tsx", - "content": "import React from \"react\";\n\nconst RatingIcon = ({\n rate,\n size = \"size-6\",\n}: {\n rate?: boolean;\n size?: string;\n}) => {\n return (\n \n \n \n );\n};\n\nexport default RatingIcon;", + "content": "import React from \"react\";\r\n\r\nconst RatingIcon = ({\r\n rate,\r\n size = \"size-6\",\r\n}: {\r\n rate?: boolean;\r\n size?: string;\r\n}) => {\r\n return (\r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport default RatingIcon;", "type": "registry:component" } ] diff --git a/public/r/matrix-cursor.json b/public/r/matrix-cursor.json index 84cdb2b..16b5afa 100644 --- a/public/r/matrix-cursor.json +++ b/public/r/matrix-cursor.json @@ -8,12 +8,12 @@ "files": [ { "path": "./src/components/nurui/matrix-cursor-demo.tsx", - "content": "import React from \"react\";\nimport MatrixCursor from \"@/components/nurui/matrix-cursor\";\n\nconst MatrixCursorDemo = () => {\n return (\n <>\n

\n Move cursor to see the effect.\n

\n \n \n );\n};\n\nexport default MatrixCursorDemo;\n", + "content": "import React from \"react\";\r\nimport MatrixCursor from \"@/components/nurui/matrix-cursor\";\r\n\r\nconst MatrixCursorDemo = () => {\r\n return (\r\n <>\r\n

\r\n Move cursor to see the effect.\r\n

\r\n \r\n \r\n );\r\n};\r\n\r\nexport default MatrixCursorDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/matrix-cursor.tsx", - "content": "\"use client\";\nimport React, { useEffect, useRef } from \"react\";\n\ninterface Particle {\n x: number;\n y: number;\n alpha: number;\n update: () => void;\n draw: () => void;\n}\n\nconst MatrixCursor = () => {\n const canvasRef = useRef(null);\n const particles = useRef([]); // useRef for persistent array\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n\n class Dot implements Particle {\n x: number;\n y: number;\n alpha: number;\n\n constructor(x: number, y: number) {\n this.x = x;\n this.y = y;\n this.alpha = 1;\n }\n\n update() {\n this.alpha -= 0.02;\n }\n\n draw() {\n if (!ctx) return;\n ctx.fillStyle = `rgba(0, 255, 0, ${this.alpha})`;\n ctx.fillRect(this.x, this.y, 4, 4);\n }\n }\n\n const animate = () => {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n particles.current = particles.current.filter((p) => {\n p.update();\n p.draw();\n return p.alpha > 0;\n });\n\n requestAnimationFrame(animate);\n };\n\n animate();\n\n const onMove = (e: MouseEvent) => {\n particles.current.push(new Dot(e.clientX, e.clientY));\n };\n\n window.addEventListener(\"mousemove\", onMove);\n return () => {\n window.removeEventListener(\"mousemove\", onMove);\n };\n }, []);\n\n return (\n \n );\n};\n\nexport default MatrixCursor;\n", + "content": "\"use client\";\r\nimport React, { useEffect, useRef } from \"react\";\r\n\r\ninterface Particle {\r\n x: number;\r\n y: number;\r\n alpha: number;\r\n update: () => void;\r\n draw: () => void;\r\n}\r\n\r\nconst MatrixCursor = () => {\r\n const canvasRef = useRef(null);\r\n const particles = useRef([]); // useRef for persistent array\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) return;\r\n\r\n canvas.width = window.innerWidth;\r\n canvas.height = window.innerHeight;\r\n\r\n class Dot implements Particle {\r\n x: number;\r\n y: number;\r\n alpha: number;\r\n\r\n constructor(x: number, y: number) {\r\n this.x = x;\r\n this.y = y;\r\n this.alpha = 1;\r\n }\r\n\r\n update() {\r\n this.alpha -= 0.02;\r\n }\r\n\r\n draw() {\r\n if (!ctx) return;\r\n ctx.fillStyle = `rgba(0, 255, 0, ${this.alpha})`;\r\n ctx.fillRect(this.x, this.y, 4, 4);\r\n }\r\n }\r\n\r\n const animate = () => {\r\n ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n\r\n particles.current = particles.current.filter((p) => {\r\n p.update();\r\n p.draw();\r\n return p.alpha > 0;\r\n });\r\n\r\n requestAnimationFrame(animate);\r\n };\r\n\r\n animate();\r\n\r\n const onMove = (e: MouseEvent) => {\r\n particles.current.push(new Dot(e.clientX, e.clientY));\r\n };\r\n\r\n window.addEventListener(\"mousemove\", onMove);\r\n return () => {\r\n window.removeEventListener(\"mousemove\", onMove);\r\n };\r\n }, []);\r\n\r\n return (\r\n \r\n );\r\n};\r\n\r\nexport default MatrixCursor;\r\n", "type": "registry:component" } ] diff --git a/public/r/money-cursor.json b/public/r/money-cursor.json index a5a5c37..b5c9b72 100644 --- a/public/r/money-cursor.json +++ b/public/r/money-cursor.json @@ -8,12 +8,12 @@ "files": [ { "path": "./src/components/nurui/money-cursor-demo.tsx", - "content": "import MoneyCursor from \"@/components/nurui/money-cursor\";\n\nconst MoneyCursorDemo = () => {\n return (\n <>\n

\n Move cursor to see the effect.\n

\n \n \n );\n};\n\nexport default MoneyCursorDemo;\n", + "content": "import MoneyCursor from \"@/components/nurui/money-cursor\";\r\n\r\nconst MoneyCursorDemo = () => {\r\n return (\r\n <>\r\n

\r\n Move cursor to see the effect.\r\n

\r\n \r\n \r\n );\r\n};\r\n\r\nexport default MoneyCursorDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/money-cursor.tsx", - "content": "\"use client\";\nimport React, { useEffect, useRef } from \"react\";\n\nconst symbols = [\"$\", \"๐Ÿ’ธ\", \"๐Ÿช™\", \"๐Ÿ’ต\"];\n\nclass MoneyParticle {\n x: number;\n y: number;\n alpha: number;\n char: string;\n\n constructor(x: number, y: number) {\n this.x = x;\n this.y = y;\n this.alpha = 1;\n this.char = symbols[Math.floor(Math.random() * symbols.length)];\n }\n\n update() {\n this.y -= 0.3;\n this.alpha -= 0.02;\n }\n\n draw(ctx: CanvasRenderingContext2D) {\n ctx.fillStyle = `rgba(255, 255, 0, ${this.alpha})`;\n ctx.font = \"18px sans-serif\";\n ctx.fillText(this.char, this.x, this.y);\n }\n}\n\nconst MoneyCursor: React.FC = () => {\n const canvasRef = useRef(null);\n const particlesRef = useRef([]);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n\n const particles = particlesRef.current;\n\n const animate = () => {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n for (let i = particles.length - 1; i >= 0; i--) {\n const p = particles[i];\n p.update();\n p.draw(ctx);\n if (p.alpha <= 0) particles.splice(i, 1);\n }\n requestAnimationFrame(animate);\n };\n\n animate();\n\n const handleMove = (e: MouseEvent) => {\n for (let i = 0; i < 2; i++) {\n particles.push(new MoneyParticle(e.clientX, e.clientY));\n }\n };\n\n window.addEventListener(\"mousemove\", handleMove);\n\n return () => {\n window.removeEventListener(\"mousemove\", handleMove);\n };\n }, []);\n\n return (\n \n );\n};\n\nexport default MoneyCursor;", + "content": "\"use client\";\r\nimport React, { useEffect, useRef } from \"react\";\r\n\r\nconst symbols = [\"$\", \"๐Ÿ’ธ\", \"๐Ÿช™\", \"๐Ÿ’ต\"];\r\n\r\nclass MoneyParticle {\r\n x: number;\r\n y: number;\r\n alpha: number;\r\n char: string;\r\n\r\n constructor(x: number, y: number) {\r\n this.x = x;\r\n this.y = y;\r\n this.alpha = 1;\r\n this.char = symbols[Math.floor(Math.random() * symbols.length)];\r\n }\r\n\r\n update() {\r\n this.y -= 0.3;\r\n this.alpha -= 0.02;\r\n }\r\n\r\n draw(ctx: CanvasRenderingContext2D) {\r\n ctx.fillStyle = `rgba(255, 255, 0, ${this.alpha})`;\r\n ctx.font = \"18px sans-serif\";\r\n ctx.fillText(this.char, this.x, this.y);\r\n }\r\n}\r\n\r\nconst MoneyCursor: React.FC = () => {\r\n const canvasRef = useRef(null);\r\n const particlesRef = useRef([]);\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) return;\r\n\r\n canvas.width = window.innerWidth;\r\n canvas.height = window.innerHeight;\r\n\r\n const particles = particlesRef.current;\r\n\r\n const animate = () => {\r\n ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n for (let i = particles.length - 1; i >= 0; i--) {\r\n const p = particles[i];\r\n p.update();\r\n p.draw(ctx);\r\n if (p.alpha <= 0) particles.splice(i, 1);\r\n }\r\n requestAnimationFrame(animate);\r\n };\r\n\r\n animate();\r\n\r\n const handleMove = (e: MouseEvent) => {\r\n for (let i = 0; i < 2; i++) {\r\n particles.push(new MoneyParticle(e.clientX, e.clientY));\r\n }\r\n };\r\n\r\n window.addEventListener(\"mousemove\", handleMove);\r\n\r\n return () => {\r\n window.removeEventListener(\"mousemove\", handleMove);\r\n };\r\n }, []);\r\n\r\n return (\r\n \r\n );\r\n};\r\n\r\nexport default MoneyCursor;", "type": "registry:component" } ] diff --git a/public/r/neobrutalism-faq.json b/public/r/neobrutalism-faq.json index 5554e9e..0dae7e8 100644 --- a/public/r/neobrutalism-faq.json +++ b/public/r/neobrutalism-faq.json @@ -20,9 +20,7 @@ }, { "path": "./src/components/nurui/question-answer.tsx", - "content": "import { FC } from \"react\";\r\nimport { SlArrowDown, SlArrowUp } from \"react-icons/sl\";\r\n\r\ninterface PropsType {\r\n question: string;\r\n answer: string;\r\n questionNumber: number;\r\n setOpenKey: () => void;\r\n openKey: number;\r\n}\r\n\r\nconst QuestionAnswer: FC = ({\r\n question,\r\n answer,\r\n questionNumber,\r\n openKey,\r\n setOpenKey,\r\n}) => {\r\n return (\r\n
\r\n setOpenKey()}\r\n >\r\n
\r\n {questionNumber}. {question}\r\n
\r\n \r\n
\r\n {openKey === questionNumber && (\r\n

\r\n {answer}\r\n

\r\n )}\r\n \r\n );\r\n};\r\n\r\nexport default QuestionAnswer;\r\n", - "type": "registry:component" } ] diff --git a/public/r/neural-background.json b/public/r/neural-background.json index 9128ff7..f9ccefe 100644 --- a/public/r/neural-background.json +++ b/public/r/neural-background.json @@ -10,7 +10,7 @@ "files": [ { "path": "./src/components/nurui/hacker-background.tsx", - "content": "\"use client\";\nimport React, { useEffect, useRef } from \"react\";\n\ninterface HackerBackgroundProps {\n color?: string;\n fontSize?: number;\n className?: string;\n speed?: number;\n}\n\nconst HackerBackground: React.FC = ({\n color = \"#3ca2fa\",\n fontSize = 15,\n className = \"\",\n speed = 1,\n}) => {\n const canvasRef = useRef(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n\n const resizeCanvas = () => {\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n };\n\n resizeCanvas();\n window.addEventListener(\"resize\", resizeCanvas);\n\n let animationFrameId: number;\n\n const columns = Math.floor(canvas.width / fontSize);\n const drops: number[] = new Array(columns).fill(1);\n\n const chars =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%^&*()_+\";\n\n let lastTime = 0;\n const interval = 33; // ~30 fps\n\n const draw = (currentTime: number) => {\n animationFrameId = requestAnimationFrame(draw);\n\n if (currentTime - lastTime < interval) return;\n lastTime = currentTime;\n\n ctx.fillStyle = \"rgba(0, 0, 0, 0.05)\";\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n\n ctx.fillStyle = color;\n ctx.font = `${fontSize}px monospace`;\n\n for (let i = 0; i < drops.length; i++) {\n const text = chars[Math.floor(Math.random() * chars.length)];\n ctx.fillText(text, i * fontSize, drops[i] * fontSize);\n\n if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) {\n drops[i] = 0;\n }\n drops[i] += speed; // Use the speed prop to control fall rate\n }\n };\n\n animationFrameId = requestAnimationFrame(draw);\n\n return () => {\n window.removeEventListener(\"resize\", resizeCanvas);\n cancelAnimationFrame(animationFrameId);\n };\n }, [color, fontSize, speed]);\n\n return (\n \n );\n};\n\nexport default HackerBackground;\n", + "content": "\"use client\";\r\nimport React, { useEffect, useRef } from \"react\";\r\n\r\ninterface HackerBackgroundProps {\r\n color?: string;\r\n fontSize?: number;\r\n className?: string;\r\n speed?: number;\r\n}\r\n\r\nconst HackerBackground: React.FC = ({\r\n color = \"#3ca2fa\",\r\n fontSize = 15,\r\n className = \"\",\r\n speed = 1,\r\n}) => {\r\n const canvasRef = useRef(null);\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) return;\r\n\r\n const resizeCanvas = () => {\r\n canvas.width = window.innerWidth;\r\n canvas.height = window.innerHeight;\r\n };\r\n\r\n resizeCanvas();\r\n window.addEventListener(\"resize\", resizeCanvas);\r\n\r\n let animationFrameId: number;\r\n\r\n const columns = Math.floor(canvas.width / fontSize);\r\n const drops: number[] = new Array(columns).fill(1);\r\n\r\n const chars =\r\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%^&*()_+\";\r\n\r\n let lastTime = 0;\r\n const interval = 33; // ~30 fps\r\n\r\n const draw = (currentTime: number) => {\r\n animationFrameId = requestAnimationFrame(draw);\r\n\r\n if (currentTime - lastTime < interval) return;\r\n lastTime = currentTime;\r\n\r\n ctx.fillStyle = \"rgba(0, 0, 0, 0.05)\";\r\n ctx.fillRect(0, 0, canvas.width, canvas.height);\r\n\r\n ctx.fillStyle = color;\r\n ctx.font = `${fontSize}px monospace`;\r\n\r\n for (let i = 0; i < drops.length; i++) {\r\n const text = chars[Math.floor(Math.random() * chars.length)];\r\n ctx.fillText(text, i * fontSize, drops[i] * fontSize);\r\n\r\n if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) {\r\n drops[i] = 0;\r\n }\r\n drops[i] += speed; // Use the speed prop to control fall rate\r\n }\r\n };\r\n\r\n animationFrameId = requestAnimationFrame(draw);\r\n\r\n return () => {\r\n window.removeEventListener(\"resize\", resizeCanvas);\r\n cancelAnimationFrame(animationFrameId);\r\n };\r\n }, [color, fontSize, speed]);\r\n\r\n return (\r\n \r\n );\r\n};\r\n\r\nexport default HackerBackground;\r\n", "type": "registry:component" } ] diff --git a/public/r/playing-card.json b/public/r/playing-card.json index 922750e..8fedfbe 100644 --- a/public/r/playing-card.json +++ b/public/r/playing-card.json @@ -13,17 +13,17 @@ "files": [ { "path": "./src/components/nurui/playing-card-demo.tsx", - "content": "\"use client\";\nimport React, { useState } from \"react\";\nimport PlayingCard from \"@/components/nurui/playing-card\";\n\n// Helper to parse the CSS variable string into an array of RGB arrays\nfunction parseCanvasColors(cssVar: string): number[][] {\n const raw = cssVar.replace(/['\"]/g, \"\");\n return raw\n .split(\";\")\n .map((group) => group.split(\",\").map((n) => parseInt(n.trim(), 10)))\n .filter((arr) => arr.length === 3);\n}\n\n// Get CSS variable as string\nfunction getCssVar(varName: string) {\n return getComputedStyle(document.documentElement)\n .getPropertyValue(varName)\n .trim();\n}\n\nexport default function PlayingCardDemo() {\n const [\n revealCanvasForPlayingCard,\n setRevealCanvasForPlayingCardForPlayingCard,\n ] = useState(false);\n\n // Read and parse color variables\n const [canvasColors, setCanvasColors] = useState(() =>\n parseCanvasColors(getCssVar(\"--playingcard-canvas-colors\")),\n );\n const [canvasBg, setCanvasBg] = useState(() =>\n getCssVar(\"--playingcard-canvas-bg\"),\n );\n\n // Optional: Update on theme change if your theme system triggers an event\n React.useEffect(() => {\n const update = () => {\n setCanvasColors(\n parseCanvasColors(getCssVar(\"--playingcard-canvas-colors\")),\n );\n setCanvasBg(getCssVar(\"--playingcard-canvas-bg\"));\n };\n window.addEventListener(\"themechange\", update);\n return () => window.removeEventListener(\"themechange\", update);\n }, []);\n\n return (\n
\n
\n Click on the card to show/hide dynamic background\n
\n \n setRevealCanvasForPlayingCardForPlayingCard((prev) => !prev)\n }\n textColorTransitionDelay=\"1s\"\n textColorTransitionDuration=\"2.4s\"\n revealCanvasBackgroundColor={canvasBg}\n revealCanvasColors={canvasColors}\n inscriptionColor=\"var(--playingcard-inscription-color)\"\n inscriptionColorHovered=\"var(--playingcard-inscription-color-hover)\"\n />\n
\n );\n}\n", + "content": "\"use client\";\r\nimport React, { useState } from \"react\";\r\nimport PlayingCard from \"@/components/nurui/playing-card\";\r\n\r\n// Helper to parse the CSS variable string into an array of RGB arrays\r\nfunction parseCanvasColors(cssVar: string): number[][] {\r\n const raw = cssVar.replace(/['\"]/g, \"\");\r\n return raw\r\n .split(\";\")\r\n .map((group) => group.split(\",\").map((n) => parseInt(n.trim(), 10)))\r\n .filter((arr) => arr.length === 3);\r\n}\r\n\r\n// Get CSS variable as string\r\nfunction getCssVar(varName: string) {\r\n return getComputedStyle(document.documentElement)\r\n .getPropertyValue(varName)\r\n .trim();\r\n}\r\n\r\nexport default function PlayingCardDemo() {\r\n const [\r\n revealCanvasForPlayingCard,\r\n setRevealCanvasForPlayingCardForPlayingCard,\r\n ] = useState(false);\r\n\r\n // Read and parse color variables\r\n const [canvasColors, setCanvasColors] = useState(() =>\r\n parseCanvasColors(getCssVar(\"--playingcard-canvas-colors\")),\r\n );\r\n const [canvasBg, setCanvasBg] = useState(() =>\r\n getCssVar(\"--playingcard-canvas-bg\"),\r\n );\r\n\r\n // Optional: Update on theme change if your theme system triggers an event\r\n React.useEffect(() => {\r\n const update = () => {\r\n setCanvasColors(\r\n parseCanvasColors(getCssVar(\"--playingcard-canvas-colors\")),\r\n );\r\n setCanvasBg(getCssVar(\"--playingcard-canvas-bg\"));\r\n };\r\n window.addEventListener(\"themechange\", update);\r\n return () => window.removeEventListener(\"themechange\", update);\r\n }, []);\r\n\r\n return (\r\n
\r\n
\r\n Click on the card to show/hide dynamic background\r\n
\r\n \r\n setRevealCanvasForPlayingCardForPlayingCard((prev) => !prev)\r\n }\r\n textColorTransitionDelay=\"1s\"\r\n textColorTransitionDuration=\"2.4s\"\r\n revealCanvasBackgroundColor={canvasBg}\r\n revealCanvasColors={canvasColors}\r\n inscriptionColor=\"var(--playingcard-inscription-color)\"\r\n inscriptionColorHovered=\"var(--playingcard-inscription-color-hover)\"\r\n />\r\n
\r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/playing-card.tsx", - "content": "\"use client\";\nimport React, { useRef, useEffect, useState, useMemo } from \"react\";\nimport { Canvas, useFrame, useThree } from \"@react-three/fiber\";\nimport * as THREE from \"three\";\nimport { cn } from \"@/lib/utils\";\nimport \"./styles/playing-card.css\";\n\n// --- CanvasRevealEffect and Shader Logic ---\n\ninterface CanvasRevealEffectProps {\n animationSpeed?: number;\n opacities?: number[];\n colors?: number[][];\n containerClassName?: string;\n dotSize?: number;\n showGradient?: boolean;\n replaceBackground?: boolean;\n backgroundColor?: string;\n}\n\nconst CanvasRevealEffect: React.FC = ({\n animationSpeed = 0.4,\n opacities = [0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.8, 0.8, 0.8, 1],\n colors = [[0, 255, 255]],\n containerClassName,\n dotSize,\n showGradient = true,\n replaceBackground = false,\n backgroundColor,\n}) => (\n \n \n {showGradient && (\n <>\n \n \n \n )}\n \n);\n\ninterface DotMatrixProps {\n colors?: number[][];\n opacities?: number[];\n totalSize?: number;\n dotSize?: number;\n shader?: string;\n center?: (\"x\" | \"y\")[];\n}\n\nconst DotMatrix: React.FC = ({\n colors = [[0, 0, 0]],\n opacities = [0.04, 0.04, 0.04, 0.04, 0.04, 0.08, 0.08, 0.08, 0.08, 0.14],\n totalSize = 4,\n dotSize = 2,\n shader = \"\",\n center = [\"x\", \"y\"],\n}) => {\n const uniforms = useMemo(() => {\n let colorsArray = [\n colors[0],\n colors[0],\n colors[0],\n colors[0],\n colors[0],\n colors[0],\n ];\n if (colors.length === 2) {\n colorsArray = [\n colors[0],\n colors[0],\n colors[0],\n colors[1],\n colors[1],\n colors[1],\n ];\n } else if (colors.length === 3) {\n colorsArray = [\n colors[0],\n colors[0],\n colors[1],\n colors[1],\n colors[2],\n colors[2],\n ];\n }\n return {\n u_colors: {\n value: colorsArray\n .filter(\n (color) =>\n Array.isArray(color) &&\n color.length === 3 &&\n color.every((c) => typeof c === \"number\"),\n )\n .map((color) => [color[0] / 255, color[1] / 255, color[2] / 255]),\n type: \"uniform3fv\",\n },\n u_opacities: {\n value: opacities,\n type: \"uniform1fv\",\n },\n u_total_size: {\n value: totalSize,\n type: \"uniform1f\",\n },\n u_dot_size: {\n value: dotSize,\n type: \"uniform1f\",\n },\n };\n }, [colors, opacities, totalSize, dotSize]);\n\n return (\n \n );\n};\n\ntype Uniforms = {\n [key: string]: {\n value: number | number[] | number[][];\n type: string;\n };\n};\n\ninterface ShaderProps {\n source: string;\n uniforms: Uniforms;\n maxFps?: number;\n}\n\nconst ShaderMaterialComponent: React.FC<{\n source: string;\n uniforms: Uniforms;\n maxFps?: number;\n}> = ({ source, uniforms, maxFps = 60 }) => {\n const { size } = useThree();\n const ref = useRef(null);\n const lastFrameTime = useRef(0);\n\n useFrame((state: { clock: THREE.Clock }) => {\n if (!ref.current) return;\n const timestamp = state.clock.getElapsedTime();\n if (timestamp - lastFrameTime.current < 1 / maxFps) {\n return;\n }\n lastFrameTime.current = timestamp;\n const material = ref.current.material as THREE.ShaderMaterial;\n const timeLocation = material.uniforms.u_time;\n if (timeLocation) {\n timeLocation.value = timestamp;\n }\n });\n\n const getUniforms = () => {\n const preparedUniforms: Record =\n {};\n for (const uniformName in uniforms) {\n const uniform = uniforms[uniformName];\n switch (uniform.type) {\n case \"uniform1f\":\n preparedUniforms[uniformName] = { value: uniform.value as number };\n break;\n case \"uniform3f\":\n preparedUniforms[uniformName] = {\n value: new THREE.Vector3().fromArray(uniform.value as number[]),\n };\n break;\n case \"uniform1fv\":\n preparedUniforms[uniformName] = { value: uniform.value as number[] };\n break;\n case \"uniform3fv\":\n preparedUniforms[uniformName] = {\n value: (uniform.value as number[][]).map((v) =>\n new THREE.Vector3().fromArray(v),\n ),\n };\n break;\n case \"uniform2f\":\n preparedUniforms[uniformName] = {\n value: new THREE.Vector2().fromArray(uniform.value as number[]),\n };\n break;\n default:\n console.error(`Invalid uniform type for '${uniformName}'.`);\n break;\n }\n }\n preparedUniforms[\"u_time\"] = { value: 0 };\n preparedUniforms[\"u_resolution\"] = {\n value: new THREE.Vector2(size.width * 2, size.height * 2),\n };\n return preparedUniforms;\n };\n\n const material = useMemo(() => {\n return new THREE.ShaderMaterial({\n vertexShader: `\n precision mediump float;\n in vec2 coordinates;\n uniform vec2 u_resolution;\n out vec2 fragCoord;\n void main(){\n float x = position.x;\n float y = position.y;\n gl_Position = vec4(x, y, 0.0, 1.0);\n fragCoord = (position.xy + vec2(1.0)) * 0.5 * u_resolution;\n fragCoord.y = u_resolution.y - fragCoord.y;\n }\n `,\n fragmentShader: source,\n uniforms: getUniforms(),\n glslVersion: THREE.GLSL3,\n blending: THREE.CustomBlending,\n blendSrc: THREE.SrcAlphaFactor,\n blendDst: THREE.OneFactor,\n });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [size.width, size.height, source]);\n\n return (\n \n \n \n \n );\n};\n\nconst Shader: React.FC = ({ source, uniforms, maxFps = 60 }) => {\n return (\n \n \n \n );\n};\n\n// --- useDynamicTextLayout Hook ---\n\nfunction useDynamicTextLayout(\n containerRef: React.RefObject,\n textArray: string[],\n minWidth: number,\n maxWidth: number,\n minTextSize: number,\n maxTextSize: number,\n manualLetterSpacing: number | undefined,\n componentId: string,\n) {\n const [textSize, setTextSize] = useState(maxTextSize);\n const [letterSpacing, setLetterSpacing] = useState(manualLetterSpacing ?? 0);\n\n useEffect(() => {\n const updateTextSize = () => {\n if (containerRef.current) {\n const width = containerRef.current.offsetWidth;\n const calculatedTextSize =\n ((maxTextSize - minTextSize) / (maxWidth - minWidth)) *\n (width - minWidth) +\n minTextSize;\n const cappedTextSize = Math.min(calculatedTextSize, maxTextSize);\n setTextSize(cappedTextSize);\n }\n };\n const handleResize = () => {\n setTimeout(updateTextSize, 500);\n };\n const resizeObserver = new ResizeObserver(handleResize);\n const observedNode = containerRef.current;\n if (observedNode) {\n resizeObserver.observe(observedNode);\n }\n updateTextSize();\n return () => {\n if (observedNode) {\n resizeObserver.unobserve(observedNode);\n }\n };\n }, [minWidth, maxWidth, minTextSize, maxTextSize, containerRef]);\n\n useEffect(() => {\n if (manualLetterSpacing !== undefined) {\n setLetterSpacing(manualLetterSpacing);\n return;\n }\n const textElement = containerRef.current?.querySelector(\n `#${componentId}-text`,\n );\n if (!textElement) return;\n const letterHeight =\n (textElement as HTMLElement).clientHeight / textArray.length;\n setLetterSpacing(letterHeight);\n }, [textArray, textSize, manualLetterSpacing, componentId, containerRef]);\n\n return { textSize, letterSpacing };\n}\n\n// --- PlayingCard Component ---\n\nexport interface PlayingCardProps {\n componentWidth?: string;\n aspectRatio?: string;\n outerRounding?: string;\n innerRounding?: string;\n backgroundColor?: string;\n foregroundColor?: string;\n outlineColor?: string;\n hoverOutlineColor?: string;\n textArray: string[];\n minWidth: number;\n maxWidth: number;\n minTextSize: number;\n maxTextSize: number;\n verticalPadding?: string;\n horizontalPadding?: string;\n manualLetterSpacing?: number;\n componentId?: string;\n onCardClicked: () => void;\n revealCanvas?: boolean;\n textColorTransitionDelay?: string;\n textColorTransitionDuration?: string;\n revealCanvasBackgroundColor?: string;\n revealCanvasColors?: number[][];\n inscriptionColor?: string; // new\n inscriptionColorHovered?: string; // new\n}\n\nconst PlayingCard: React.FC = ({\n componentWidth = \"400px\",\n aspectRatio = \"9/16\",\n outerRounding = \"24px\",\n innerRounding = \"16px\",\n backgroundColor = \"#FFF\",\n foregroundColor = \"#000\",\n outlineColor = \"#E879F9\",\n hoverOutlineColor = \"#6366F1\",\n textArray,\n minWidth,\n maxWidth,\n minTextSize,\n maxTextSize,\n verticalPadding = \"20px\",\n horizontalPadding = \"20px\",\n manualLetterSpacing,\n componentId = \"card-1\",\n onCardClicked,\n revealCanvas = false,\n textColorTransitionDelay = \"1s\",\n textColorTransitionDuration = \"2s\",\n revealCanvasBackgroundColor,\n revealCanvasColors,\n inscriptionColor, // new\n inscriptionColorHovered, // new\n}) => {\n const containerRef = useRef(null);\n const [isHovered, setIsHovered] = useState(false);\n\n // Use the concise hook for text layout\n const { textSize, letterSpacing } = useDynamicTextLayout(\n containerRef,\n textArray,\n minWidth,\n maxWidth,\n minTextSize,\n maxTextSize,\n manualLetterSpacing,\n componentId,\n );\n\n // Style for text color transition\n const textTransition = `color ${textColorTransitionDuration} ease-in-out ${textColorTransitionDelay}`;\n\n // Border color logic for revealCanvas\n const borderColor = revealCanvas ? \"#2f2f2f\" : outlineColor;\n const borderHoverColor = revealCanvas ? \"#3a3a3a\" : hoverOutlineColor;\n\n // Card background logic for revealCanvas\n const cardBg = revealCanvas ? \"#000\" : backgroundColor;\n\n // Inscription color logic (main/mirror)\n const mainInscriptionColor = isHovered\n ? inscriptionColorHovered || \"#f12b30\"\n : inscriptionColor || \"#3662f4\";\n\n return (\n \n setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n \n {/* Reveal Canvas Effect - fills the card, always at the back */}\n {revealCanvas && (\n \n )}\n {/* Optional: mask for vignette effect */}\n {revealCanvas && (\n \n )}\n\n {/* Main Text */}\n \n {textArray.map((letter, index) => (\n 0\n ? `translateY(${letterSpacing * index}px)`\n : \"none\",\n marginBottom:\n letterSpacing >= 0 ? `${Math.abs(letterSpacing)}px` : \"0\",\n letterSpacing: `${letterSpacing}px`,\n }}\n >\n {letter}\n \n ))}\n \n {/* Mirrored Text */}\n \n {textArray.map((letter, index) => (\n 0\n ? `translateY(${letterSpacing * index}px)`\n : \"none\",\n marginBottom:\n letterSpacing >= 0 ? `${Math.abs(letterSpacing)}px` : \"0\",\n letterSpacing: `${letterSpacing}px`,\n }}\n >\n {letter}\n \n ))}\n \n \n \n \n );\n};\n\nexport default PlayingCard;\n", + "content": "\"use client\";\r\nimport React, { useRef, useEffect, useState, useMemo } from \"react\";\r\nimport { Canvas, useFrame, useThree } from \"@react-three/fiber\";\r\nimport * as THREE from \"three\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport \"./styles/playing-card.css\";\r\n\r\n// --- CanvasRevealEffect and Shader Logic ---\r\n\r\ninterface CanvasRevealEffectProps {\r\n animationSpeed?: number;\r\n opacities?: number[];\r\n colors?: number[][];\r\n containerClassName?: string;\r\n dotSize?: number;\r\n showGradient?: boolean;\r\n replaceBackground?: boolean;\r\n backgroundColor?: string;\r\n}\r\n\r\nconst CanvasRevealEffect: React.FC = ({\r\n animationSpeed = 0.4,\r\n opacities = [0.3, 0.3, 0.3, 0.5, 0.5, 0.5, 0.8, 0.8, 0.8, 1],\r\n colors = [[0, 255, 255]],\r\n containerClassName,\r\n dotSize,\r\n showGradient = true,\r\n replaceBackground = false,\r\n backgroundColor,\r\n}) => (\r\n \r\n \r\n {showGradient && (\r\n <>\r\n \r\n \r\n \r\n )}\r\n \r\n);\r\n\r\ninterface DotMatrixProps {\r\n colors?: number[][];\r\n opacities?: number[];\r\n totalSize?: number;\r\n dotSize?: number;\r\n shader?: string;\r\n center?: (\"x\" | \"y\")[];\r\n}\r\n\r\nconst DotMatrix: React.FC = ({\r\n colors = [[0, 0, 0]],\r\n opacities = [0.04, 0.04, 0.04, 0.04, 0.04, 0.08, 0.08, 0.08, 0.08, 0.14],\r\n totalSize = 4,\r\n dotSize = 2,\r\n shader = \"\",\r\n center = [\"x\", \"y\"],\r\n}) => {\r\n const uniforms = useMemo(() => {\r\n let colorsArray = [\r\n colors[0],\r\n colors[0],\r\n colors[0],\r\n colors[0],\r\n colors[0],\r\n colors[0],\r\n ];\r\n if (colors.length === 2) {\r\n colorsArray = [\r\n colors[0],\r\n colors[0],\r\n colors[0],\r\n colors[1],\r\n colors[1],\r\n colors[1],\r\n ];\r\n } else if (colors.length === 3) {\r\n colorsArray = [\r\n colors[0],\r\n colors[0],\r\n colors[1],\r\n colors[1],\r\n colors[2],\r\n colors[2],\r\n ];\r\n }\r\n return {\r\n u_colors: {\r\n value: colorsArray\r\n .filter(\r\n (color) =>\r\n Array.isArray(color) &&\r\n color.length === 3 &&\r\n color.every((c) => typeof c === \"number\"),\r\n )\r\n .map((color) => [color[0] / 255, color[1] / 255, color[2] / 255]),\r\n type: \"uniform3fv\",\r\n },\r\n u_opacities: {\r\n value: opacities,\r\n type: \"uniform1fv\",\r\n },\r\n u_total_size: {\r\n value: totalSize,\r\n type: \"uniform1f\",\r\n },\r\n u_dot_size: {\r\n value: dotSize,\r\n type: \"uniform1f\",\r\n },\r\n };\r\n }, [colors, opacities, totalSize, dotSize]);\r\n\r\n return (\r\n \r\n );\r\n};\r\n\r\ntype Uniforms = {\r\n [key: string]: {\r\n value: number | number[] | number[][];\r\n type: string;\r\n };\r\n};\r\n\r\ninterface ShaderProps {\r\n source: string;\r\n uniforms: Uniforms;\r\n maxFps?: number;\r\n}\r\n\r\nconst ShaderMaterialComponent: React.FC<{\r\n source: string;\r\n uniforms: Uniforms;\r\n maxFps?: number;\r\n}> = ({ source, uniforms, maxFps = 60 }) => {\r\n const { size } = useThree();\r\n const ref = useRef(null);\r\n const lastFrameTime = useRef(0);\r\n\r\n useFrame((state: { clock: THREE.Clock }) => {\r\n if (!ref.current) return;\r\n const timestamp = state.clock.getElapsedTime();\r\n if (timestamp - lastFrameTime.current < 1 / maxFps) {\r\n return;\r\n }\r\n lastFrameTime.current = timestamp;\r\n const material = ref.current.material as THREE.ShaderMaterial;\r\n const timeLocation = material.uniforms.u_time;\r\n if (timeLocation) {\r\n timeLocation.value = timestamp;\r\n }\r\n });\r\n\r\n const getUniforms = () => {\r\n const preparedUniforms: Record =\r\n {};\r\n for (const uniformName in uniforms) {\r\n const uniform = uniforms[uniformName];\r\n switch (uniform.type) {\r\n case \"uniform1f\":\r\n preparedUniforms[uniformName] = { value: uniform.value as number };\r\n break;\r\n case \"uniform3f\":\r\n preparedUniforms[uniformName] = {\r\n value: new THREE.Vector3().fromArray(uniform.value as number[]),\r\n };\r\n break;\r\n case \"uniform1fv\":\r\n preparedUniforms[uniformName] = { value: uniform.value as number[] };\r\n break;\r\n case \"uniform3fv\":\r\n preparedUniforms[uniformName] = {\r\n value: (uniform.value as number[][]).map((v) =>\r\n new THREE.Vector3().fromArray(v),\r\n ),\r\n };\r\n break;\r\n case \"uniform2f\":\r\n preparedUniforms[uniformName] = {\r\n value: new THREE.Vector2().fromArray(uniform.value as number[]),\r\n };\r\n break;\r\n default:\r\n console.error(`Invalid uniform type for '${uniformName}'.`);\r\n break;\r\n }\r\n }\r\n preparedUniforms[\"u_time\"] = { value: 0 };\r\n preparedUniforms[\"u_resolution\"] = {\r\n value: new THREE.Vector2(size.width * 2, size.height * 2),\r\n };\r\n return preparedUniforms;\r\n };\r\n\r\n const material = useMemo(() => {\r\n return new THREE.ShaderMaterial({\r\n vertexShader: `\r\n precision mediump float;\r\n in vec2 coordinates;\r\n uniform vec2 u_resolution;\r\n out vec2 fragCoord;\r\n void main(){\r\n float x = position.x;\r\n float y = position.y;\r\n gl_Position = vec4(x, y, 0.0, 1.0);\r\n fragCoord = (position.xy + vec2(1.0)) * 0.5 * u_resolution;\r\n fragCoord.y = u_resolution.y - fragCoord.y;\r\n }\r\n `,\r\n fragmentShader: source,\r\n uniforms: getUniforms(),\r\n glslVersion: THREE.GLSL3,\r\n blending: THREE.CustomBlending,\r\n blendSrc: THREE.SrcAlphaFactor,\r\n blendDst: THREE.OneFactor,\r\n });\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [size.width, size.height, source]);\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nconst Shader: React.FC = ({ source, uniforms, maxFps = 60 }) => {\r\n return (\r\n \r\n \r\n \r\n );\r\n};\r\n\r\n// --- useDynamicTextLayout Hook ---\r\n\r\nfunction useDynamicTextLayout(\r\n containerRef: React.RefObject,\r\n textArray: string[],\r\n minWidth: number,\r\n maxWidth: number,\r\n minTextSize: number,\r\n maxTextSize: number,\r\n manualLetterSpacing: number | undefined,\r\n componentId: string,\r\n) {\r\n const [textSize, setTextSize] = useState(maxTextSize);\r\n const [letterSpacing, setLetterSpacing] = useState(manualLetterSpacing ?? 0);\r\n\r\n useEffect(() => {\r\n const updateTextSize = () => {\r\n if (containerRef.current) {\r\n const width = containerRef.current.offsetWidth;\r\n const calculatedTextSize =\r\n ((maxTextSize - minTextSize) / (maxWidth - minWidth)) *\r\n (width - minWidth) +\r\n minTextSize;\r\n const cappedTextSize = Math.min(calculatedTextSize, maxTextSize);\r\n setTextSize(cappedTextSize);\r\n }\r\n };\r\n const handleResize = () => {\r\n setTimeout(updateTextSize, 500);\r\n };\r\n const resizeObserver = new ResizeObserver(handleResize);\r\n const observedNode = containerRef.current;\r\n if (observedNode) {\r\n resizeObserver.observe(observedNode);\r\n }\r\n updateTextSize();\r\n return () => {\r\n if (observedNode) {\r\n resizeObserver.unobserve(observedNode);\r\n }\r\n };\r\n }, [minWidth, maxWidth, minTextSize, maxTextSize, containerRef]);\r\n\r\n useEffect(() => {\r\n if (manualLetterSpacing !== undefined) {\r\n setLetterSpacing(manualLetterSpacing);\r\n return;\r\n }\r\n const textElement = containerRef.current?.querySelector(\r\n `#${componentId}-text`,\r\n );\r\n if (!textElement) return;\r\n const letterHeight =\r\n (textElement as HTMLElement).clientHeight / textArray.length;\r\n setLetterSpacing(letterHeight);\r\n }, [textArray, textSize, manualLetterSpacing, componentId, containerRef]);\r\n\r\n return { textSize, letterSpacing };\r\n}\r\n\r\n// --- PlayingCard Component ---\r\n\r\nexport interface PlayingCardProps {\r\n componentWidth?: string;\r\n aspectRatio?: string;\r\n outerRounding?: string;\r\n innerRounding?: string;\r\n backgroundColor?: string;\r\n foregroundColor?: string;\r\n outlineColor?: string;\r\n hoverOutlineColor?: string;\r\n textArray: string[];\r\n minWidth: number;\r\n maxWidth: number;\r\n minTextSize: number;\r\n maxTextSize: number;\r\n verticalPadding?: string;\r\n horizontalPadding?: string;\r\n manualLetterSpacing?: number;\r\n componentId?: string;\r\n onCardClicked: () => void;\r\n revealCanvas?: boolean;\r\n textColorTransitionDelay?: string;\r\n textColorTransitionDuration?: string;\r\n revealCanvasBackgroundColor?: string;\r\n revealCanvasColors?: number[][];\r\n inscriptionColor?: string; // new\r\n inscriptionColorHovered?: string; // new\r\n}\r\n\r\nconst PlayingCard: React.FC = ({\r\n componentWidth = \"400px\",\r\n aspectRatio = \"9/16\",\r\n outerRounding = \"24px\",\r\n innerRounding = \"16px\",\r\n backgroundColor = \"#FFF\",\r\n foregroundColor = \"#000\",\r\n outlineColor = \"#E879F9\",\r\n hoverOutlineColor = \"#6366F1\",\r\n textArray,\r\n minWidth,\r\n maxWidth,\r\n minTextSize,\r\n maxTextSize,\r\n verticalPadding = \"20px\",\r\n horizontalPadding = \"20px\",\r\n manualLetterSpacing,\r\n componentId = \"card-1\",\r\n onCardClicked,\r\n revealCanvas = false,\r\n textColorTransitionDelay = \"1s\",\r\n textColorTransitionDuration = \"2s\",\r\n revealCanvasBackgroundColor,\r\n revealCanvasColors,\r\n inscriptionColor, // new\r\n inscriptionColorHovered, // new\r\n}) => {\r\n const containerRef = useRef(null);\r\n const [isHovered, setIsHovered] = useState(false);\r\n\r\n // Use the concise hook for text layout\r\n const { textSize, letterSpacing } = useDynamicTextLayout(\r\n containerRef,\r\n textArray,\r\n minWidth,\r\n maxWidth,\r\n minTextSize,\r\n maxTextSize,\r\n manualLetterSpacing,\r\n componentId,\r\n );\r\n\r\n // Style for text color transition\r\n const textTransition = `color ${textColorTransitionDuration} ease-in-out ${textColorTransitionDelay}`;\r\n\r\n // Border color logic for revealCanvas\r\n const borderColor = revealCanvas ? \"#2f2f2f\" : outlineColor;\r\n const borderHoverColor = revealCanvas ? \"#3a3a3a\" : hoverOutlineColor;\r\n\r\n // Card background logic for revealCanvas\r\n const cardBg = revealCanvas ? \"#000\" : backgroundColor;\r\n\r\n // Inscription color logic (main/mirror)\r\n const mainInscriptionColor = isHovered\r\n ? inscriptionColorHovered || \"#f12b30\"\r\n : inscriptionColor || \"#3662f4\";\r\n\r\n return (\r\n \r\n setIsHovered(true)}\r\n onMouseLeave={() => setIsHovered(false)}\r\n >\r\n \r\n {/* Reveal Canvas Effect - fills the card, always at the back */}\r\n {revealCanvas && (\r\n \r\n )}\r\n {/* Optional: mask for vignette effect */}\r\n {revealCanvas && (\r\n \r\n )}\r\n\r\n {/* Main Text */}\r\n \r\n {textArray.map((letter, index) => (\r\n 0\r\n ? `translateY(${letterSpacing * index}px)`\r\n : \"none\",\r\n marginBottom:\r\n letterSpacing >= 0 ? `${Math.abs(letterSpacing)}px` : \"0\",\r\n letterSpacing: `${letterSpacing}px`,\r\n }}\r\n >\r\n {letter}\r\n \r\n ))}\r\n \r\n {/* Mirrored Text */}\r\n \r\n {textArray.map((letter, index) => (\r\n 0\r\n ? `translateY(${letterSpacing * index}px)`\r\n : \"none\",\r\n marginBottom:\r\n letterSpacing >= 0 ? `${Math.abs(letterSpacing)}px` : \"0\",\r\n letterSpacing: `${letterSpacing}px`,\r\n }}\r\n >\r\n {letter}\r\n \r\n ))}\r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nexport default PlayingCard;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/styles/playing-card.css", - "content": "@import \"tw-animate-css\";\n\n:root {\n --playingcard-bg: #18192b;\n --playingcard-fg: #f8fafc;\n --playingcard-outline-color: #3d3759;\n --playingcard-hover-outline-color: #7c6ee6;\n --playingcard-canvas-bg: #23244a;\n --playingcard-canvas-colors: \"143,108,255;99,102,241;80,115,184\";\n --playingcard-inscription-color: #00a9fe;\n --playingcard-inscription-color-hover: #8F04A7;\n}\n\n.dark {\n --playingcard-bg: #f8fafc;\n --playingcard-fg: #0f172a;\n --playingcard-outline-color: #ddd;\n --playingcard-hover-outline-color: #aaa;\n --playingcard-canvas-bg: #f8fafc;\n --playingcard-canvas-colors: \"236,72,153;232,121,249\";\n --playingcard-inscription-color: #3662f4;\n --playingcard-inscription-color-hover: #f12b30;\n}", + "content": "@import \"tw-animate-css\";\r\n\r\n:root {\r\n --playingcard-bg: #18192b;\r\n --playingcard-fg: #f8fafc;\r\n --playingcard-outline-color: #3d3759;\r\n --playingcard-hover-outline-color: #7c6ee6;\r\n --playingcard-canvas-bg: #23244a;\r\n --playingcard-canvas-colors: \"143,108,255;99,102,241;80,115,184\";\r\n --playingcard-inscription-color: #00a9fe;\r\n --playingcard-inscription-color-hover: #8F04A7;\r\n}\r\n\r\n.dark {\r\n --playingcard-bg: #f8fafc;\r\n --playingcard-fg: #0f172a;\r\n --playingcard-outline-color: #ddd;\r\n --playingcard-hover-outline-color: #aaa;\r\n --playingcard-canvas-bg: #f8fafc;\r\n --playingcard-canvas-colors: \"236,72,153;232,121,249\";\r\n --playingcard-inscription-color: #3662f4;\r\n --playingcard-inscription-color-hover: #f12b30;\r\n}", "type": "registry:component" } ] diff --git a/public/r/premium-testimonial.json b/public/r/premium-testimonial.json index 34d8a26..f0415d7 100644 --- a/public/r/premium-testimonial.json +++ b/public/r/premium-testimonial.json @@ -14,12 +14,12 @@ "files": [ { "path": "./src/components/nurui/premium-testimonial-demo.tsx", - "content": "import React from \"react\";\nimport { PremiumTestimonial } from \"@/components/nurui/premium-testimonial\";\n\nconst premiumTestimonialDemo = () => {\n return (\n
\n \n
\n );\n};\n\nexport default premiumTestimonialDemo;\n", + "content": "import React from \"react\";\r\nimport { PremiumTestimonial } from \"@/components/nurui/premium-testimonial\";\r\n\r\nconst premiumTestimonialDemo = () => {\r\n return (\r\n
\r\n \r\n
\r\n );\r\n};\r\n\r\nexport default premiumTestimonialDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/premium-testimonial.tsx", - "content": "\"use client\";\nimport { motion, AnimatePresence, cubicBezier } from \"framer-motion\";\nimport { useState, useEffect, useRef, FC } from \"react\";\nimport { Quote, Star, ArrowLeft, ArrowRight, Sparkles } from \"lucide-react\";\nimport Image from \"next/image\";\n\nconst testimonials = [\n {\n name: \"Sarah Chen\",\n role: \"CEO, TechFlow Solutions\",\n company: \"TechFlow\",\n avatar:\n \"https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=150&h=150&fit=crop&crop=face\",\n rating: 5,\n text: \"Lord AI transformed our entire operation. We've seen a 300% increase in efficiency and saved over $2M in operational costs. The autonomous agents work flawlessly.\",\n results: [\n \"300% efficiency increase\",\n \"$2M cost savings\",\n \"24/7 automation\",\n ],\n },\n {\n name: \"Marcus Johnson\",\n role: \"CTO, DataDrive Inc\",\n company: \"DataDrive\",\n avatar:\n \"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&h=150&fit=crop&crop=face\",\n rating: 5,\n text: \"The AI voice agents are revolutionary. Our customer satisfaction increased by 40% while reducing response time from hours to seconds. Incredible ROI.\",\n results: [\n \"40% satisfaction boost\",\n \"Instant responses\",\n \"Seamless integration\",\n ],\n },\n {\n name: \"Elena Rodriguez\",\n role: \"VP Operations, ScaleUp Co\",\n company: \"ScaleUp\",\n avatar:\n \"https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=150&h=150&fit=crop&crop=face\",\n rating: 5,\n text: \"From workflow automation to social media management, Lord AI handles everything. Our team can finally focus on strategy instead of repetitive tasks.\",\n results: [\"Full automation\", \"Strategic focus\", \"Team productivity\"],\n },\n {\n name: \"David Kim\",\n role: \"Founder, GrowthLab\",\n company: \"GrowthLab\",\n avatar:\n \"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop&crop=face\",\n rating: 5,\n text: \"The custom AI systems delivered results beyond our expectations. Revenue increased 150% while operational overhead decreased significantly.\",\n results: [\"150% revenue growth\", \"Reduced overhead\", \"Scalable systems\"],\n },\n {\n name: \"Lisa Thompson\",\n role: \"Director, InnovateCorp\",\n company: \"InnovateCorp\",\n avatar:\n \"https://images.unsplash.com/photo-1517841905240-472988babdf9?w=150&h=150&fit=crop&crop=face\",\n rating: 5,\n text: \"Exceptional AI solutions that actually work. The implementation was smooth, and the results were immediate. Best investment we've made.\",\n results: [\"Immediate results\", \"Smooth integration\", \"High ROI\"],\n },\n];\n\ntype PremiumTestimonialProps = {\n title?: string;\n subTitle?: string;\n tagline?: string;\n des?: string;\n};\n\nexport const PremiumTestimonial: FC = ({\n title = \"Trusted by\",\n subTitle = \"Industry Leaders\",\n tagline = \"Client Success Stories\",\n des = \"Join thousands of businesses already transforming their operations with our premium AI solutions.\",\n}) => {\n const [currentIndex, setCurrentIndex] = useState(0);\n const [direction, setDirection] = useState(0);\n const containerRef = useRef(null);\n\n useEffect(() => {\n const timer = setInterval(() => {\n setDirection(1);\n setCurrentIndex((prev) => (prev + 1) % testimonials.length);\n }, 6000);\n\n return () => clearInterval(timer);\n }, []);\n\n const slideVariants = {\n enter: (direction: number) => ({\n x: direction > 0 ? 1000 : -1000,\n opacity: 0,\n scale: 0.8,\n rotateY: direction > 0 ? 45 : -45,\n }),\n center: {\n zIndex: 1,\n x: 0,\n opacity: 1,\n scale: 1,\n rotateY: 0,\n },\n exit: (direction: number) => ({\n zIndex: 0,\n x: direction < 0 ? 1000 : -1000,\n opacity: 0,\n scale: 0.8,\n rotateY: direction < 0 ? 45 : -45,\n }),\n };\n\n const fadeInUp = {\n hidden: { opacity: 0, y: 60 },\n visible: {\n opacity: 1,\n y: 0,\n transition: {\n duration: 0.8,\n ease: cubicBezier(0.23, 0.86, 0.39, 0.96),\n },\n },\n };\n\n const staggerContainer = {\n hidden: { opacity: 0 },\n visible: {\n opacity: 1,\n transition: {\n staggerChildren: 0.1,\n delayChildren: 0.3,\n },\n },\n };\n\n const nextTestimonial = () => {\n setDirection(1);\n setCurrentIndex((prev) => (prev + 1) % testimonials.length);\n };\n\n const prevTestimonial = () => {\n setDirection(-1);\n setCurrentIndex(\n (prev) => (prev - 1 + testimonials.length) % testimonials.length,\n );\n };\n\n return (\n \n {/* Enhanced Background Effects */}\n
\n {/* Animated gradient mesh */}\n \n\n {/* Moving light orbs */}\n \n \n\n {/* Floating particles */}\n {[...Array(12)].map((_, i) => (\n \n ))}\n
\n\n \n {/* Header */}\n \n \n \n \n \n \n โœจ {tagline}\n \n
\n \n\n \n \n {title}\n \n
\n \n {subTitle}\n \n \n\n \n {des}\n \n \n\n {/* Main Testimonial Display */}\n
\n
\n \n \n
\n {/* Animated background gradient */}\n \n\n {/* Quote icon */}\n \n \n \n\n
\n {/* User Info */}\n
\n \n
\n \n \n
\n\n {/* Floating ring animation */}\n \n \n\n

\n {testimonials[currentIndex].name}\n

\n

\n {testimonials[currentIndex].role}\n

\n

\n {testimonials[currentIndex].company}\n

\n\n {/* Star Rating */}\n
\n {[...Array(testimonials[currentIndex].rating)].map(\n (_, i) => (\n \n \n \n ),\n )}\n
\n
\n\n {/* Content */}\n
\n \n {testimonials[currentIndex].text}\n \n\n {/* Results */}\n
\n {testimonials[currentIndex].results.map((result, i) => (\n \n \n {result}\n \n \n ))}\n
\n
\n
\n
\n \n
\n
\n\n {/* Navigation Controls */}\n
\n \n \n \n\n {/* Dots Indicator */}\n
\n {testimonials.map((_, index) => (\n {\n setDirection(index > currentIndex ? 1 : -1);\n setCurrentIndex(index);\n }}\n className={`w-3 h-3 rounded-full transition-all ${\n index === currentIndex\n ? \"bg-indigo-400 scale-125\"\n : \"bg-white/30 hover:bg-white/50\"\n }`}\n whileHover={{ scale: 1.2 }}\n whileTap={{ scale: 0.9 }}\n />\n ))}\n
\n\n \n \n \n
\n
\n\n {/* Stats Section */}\n \n {[\n { number: \"500+\", label: \"Happy Clients\" },\n { number: \"98%\", label: \"Satisfaction Rate\" },\n { number: \"$10M+\", label: \"Cost Savings\" },\n { number: \"99.9%\", label: \"Uptime SLA\" },\n ].map((stat, index) => (\n \n \n {stat.number}\n \n
\n {stat.label}\n
\n \n ))}\n \n \n \n );\n};\n", + "content": "\"use client\";\r\nimport { motion, AnimatePresence, cubicBezier } from \"framer-motion\";\r\nimport { useState, useEffect, useRef, FC } from \"react\";\r\nimport { Quote, Star, ArrowLeft, ArrowRight, Sparkles } from \"lucide-react\";\r\nimport Image from \"next/image\";\r\n\r\nconst testimonials = [\r\n {\r\n name: \"Sarah Chen\",\r\n role: \"CEO, TechFlow Solutions\",\r\n company: \"TechFlow\",\r\n avatar:\r\n \"https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=150&h=150&fit=crop&crop=face\",\r\n rating: 5,\r\n text: \"Lord AI transformed our entire operation. We've seen a 300% increase in efficiency and saved over $2M in operational costs. The autonomous agents work flawlessly.\",\r\n results: [\r\n \"300% efficiency increase\",\r\n \"$2M cost savings\",\r\n \"24/7 automation\",\r\n ],\r\n },\r\n {\r\n name: \"Marcus Johnson\",\r\n role: \"CTO, DataDrive Inc\",\r\n company: \"DataDrive\",\r\n avatar:\r\n \"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150&h=150&fit=crop&crop=face\",\r\n rating: 5,\r\n text: \"The AI voice agents are revolutionary. Our customer satisfaction increased by 40% while reducing response time from hours to seconds. Incredible ROI.\",\r\n results: [\r\n \"40% satisfaction boost\",\r\n \"Instant responses\",\r\n \"Seamless integration\",\r\n ],\r\n },\r\n {\r\n name: \"Elena Rodriguez\",\r\n role: \"VP Operations, ScaleUp Co\",\r\n company: \"ScaleUp\",\r\n avatar:\r\n \"https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=150&h=150&fit=crop&crop=face\",\r\n rating: 5,\r\n text: \"From workflow automation to social media management, Lord AI handles everything. Our team can finally focus on strategy instead of repetitive tasks.\",\r\n results: [\"Full automation\", \"Strategic focus\", \"Team productivity\"],\r\n },\r\n {\r\n name: \"David Kim\",\r\n role: \"Founder, GrowthLab\",\r\n company: \"GrowthLab\",\r\n avatar:\r\n \"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150&h=150&fit=crop&crop=face\",\r\n rating: 5,\r\n text: \"The custom AI systems delivered results beyond our expectations. Revenue increased 150% while operational overhead decreased significantly.\",\r\n results: [\"150% revenue growth\", \"Reduced overhead\", \"Scalable systems\"],\r\n },\r\n {\r\n name: \"Lisa Thompson\",\r\n role: \"Director, InnovateCorp\",\r\n company: \"InnovateCorp\",\r\n avatar:\r\n \"https://images.unsplash.com/photo-1517841905240-472988babdf9?w=150&h=150&fit=crop&crop=face\",\r\n rating: 5,\r\n text: \"Exceptional AI solutions that actually work. The implementation was smooth, and the results were immediate. Best investment we've made.\",\r\n results: [\"Immediate results\", \"Smooth integration\", \"High ROI\"],\r\n },\r\n];\r\n\r\ntype PremiumTestimonialProps = {\r\n title?: string;\r\n subTitle?: string;\r\n tagline?: string;\r\n des?: string;\r\n};\r\n\r\nexport const PremiumTestimonial: FC = ({\r\n title = \"Trusted by\",\r\n subTitle = \"Industry Leaders\",\r\n tagline = \"Client Success Stories\",\r\n des = \"Join thousands of businesses already transforming their operations with our premium AI solutions.\",\r\n}) => {\r\n const [currentIndex, setCurrentIndex] = useState(0);\r\n const [direction, setDirection] = useState(0);\r\n const containerRef = useRef(null);\r\n\r\n useEffect(() => {\r\n const timer = setInterval(() => {\r\n setDirection(1);\r\n setCurrentIndex((prev) => (prev + 1) % testimonials.length);\r\n }, 6000);\r\n\r\n return () => clearInterval(timer);\r\n }, []);\r\n\r\n const slideVariants = {\r\n enter: (direction: number) => ({\r\n x: direction > 0 ? 1000 : -1000,\r\n opacity: 0,\r\n scale: 0.8,\r\n rotateY: direction > 0 ? 45 : -45,\r\n }),\r\n center: {\r\n zIndex: 1,\r\n x: 0,\r\n opacity: 1,\r\n scale: 1,\r\n rotateY: 0,\r\n },\r\n exit: (direction: number) => ({\r\n zIndex: 0,\r\n x: direction < 0 ? 1000 : -1000,\r\n opacity: 0,\r\n scale: 0.8,\r\n rotateY: direction < 0 ? 45 : -45,\r\n }),\r\n };\r\n\r\n const fadeInUp = {\r\n hidden: { opacity: 0, y: 60 },\r\n visible: {\r\n opacity: 1,\r\n y: 0,\r\n transition: {\r\n duration: 0.8,\r\n ease: cubicBezier(0.23, 0.86, 0.39, 0.96),\r\n },\r\n },\r\n };\r\n\r\n const staggerContainer = {\r\n hidden: { opacity: 0 },\r\n visible: {\r\n opacity: 1,\r\n transition: {\r\n staggerChildren: 0.1,\r\n delayChildren: 0.3,\r\n },\r\n },\r\n };\r\n\r\n const nextTestimonial = () => {\r\n setDirection(1);\r\n setCurrentIndex((prev) => (prev + 1) % testimonials.length);\r\n };\r\n\r\n const prevTestimonial = () => {\r\n setDirection(-1);\r\n setCurrentIndex(\r\n (prev) => (prev - 1 + testimonials.length) % testimonials.length,\r\n );\r\n };\r\n\r\n return (\r\n \r\n {/* Enhanced Background Effects */}\r\n
\r\n {/* Animated gradient mesh */}\r\n \r\n\r\n {/* Moving light orbs */}\r\n \r\n \r\n\r\n {/* Floating particles */}\r\n {[...Array(12)].map((_, i) => (\r\n \r\n ))}\r\n
\r\n\r\n \r\n {/* Header */}\r\n \r\n \r\n \r\n \r\n \r\n \r\n โœจ {tagline}\r\n \r\n
\r\n \r\n\r\n \r\n \r\n {title}\r\n \r\n
\r\n \r\n {subTitle}\r\n \r\n \r\n\r\n \r\n {des}\r\n \r\n \r\n\r\n {/* Main Testimonial Display */}\r\n
\r\n
\r\n \r\n \r\n
\r\n {/* Animated background gradient */}\r\n \r\n\r\n {/* Quote icon */}\r\n \r\n \r\n \r\n\r\n
\r\n {/* User Info */}\r\n
\r\n \r\n
\r\n \r\n \r\n
\r\n\r\n {/* Floating ring animation */}\r\n \r\n \r\n\r\n

\r\n {testimonials[currentIndex].name}\r\n

\r\n

\r\n {testimonials[currentIndex].role}\r\n

\r\n

\r\n {testimonials[currentIndex].company}\r\n

\r\n\r\n {/* Star Rating */}\r\n
\r\n {[...Array(testimonials[currentIndex].rating)].map(\r\n (_, i) => (\r\n \r\n \r\n \r\n ),\r\n )}\r\n
\r\n
\r\n\r\n {/* Content */}\r\n
\r\n \r\n {testimonials[currentIndex].text}\r\n \r\n\r\n {/* Results */}\r\n
\r\n {testimonials[currentIndex].results.map((result, i) => (\r\n \r\n \r\n {result}\r\n \r\n \r\n ))}\r\n
\r\n
\r\n
\r\n
\r\n \r\n
\r\n
\r\n\r\n {/* Navigation Controls */}\r\n
\r\n \r\n \r\n \r\n\r\n {/* Dots Indicator */}\r\n
\r\n {testimonials.map((_, index) => (\r\n {\r\n setDirection(index > currentIndex ? 1 : -1);\r\n setCurrentIndex(index);\r\n }}\r\n className={`w-3 h-3 rounded-full transition-all ${\r\n index === currentIndex\r\n ? \"bg-indigo-400 scale-125\"\r\n : \"bg-white/30 hover:bg-white/50\"\r\n }`}\r\n whileHover={{ scale: 1.2 }}\r\n whileTap={{ scale: 0.9 }}\r\n />\r\n ))}\r\n
\r\n\r\n \r\n \r\n \r\n
\r\n
\r\n\r\n {/* Stats Section */}\r\n \r\n {[\r\n { number: \"500+\", label: \"Happy Clients\" },\r\n { number: \"98%\", label: \"Satisfaction Rate\" },\r\n { number: \"$10M+\", label: \"Cost Savings\" },\r\n { number: \"99.9%\", label: \"Uptime SLA\" },\r\n ].map((stat, index) => (\r\n \r\n \r\n {stat.number}\r\n \r\n
\r\n {stat.label}\r\n
\r\n \r\n ))}\r\n \r\n \r\n \r\n );\r\n};\r\n", "type": "registry:component" } ] diff --git a/public/r/progress-bar.json b/public/r/progress-bar.json index 6de724a..608dae8 100644 --- a/public/r/progress-bar.json +++ b/public/r/progress-bar.json @@ -11,12 +11,12 @@ "files": [ { "path": "./src/components/nurui/progress-bar-demo.tsx", - "content": "\"use client\";\nimport { useEffect, useState } from \"react\";\nimport { ProgressBar } from \"@/components/nurui/progress-bar\";\n\nexport function ProgressBarDemo({ className }: { className?: string }) {\n const [value, setValue] = useState(0);\n\n useEffect(() => {\n const handleIncrement = (prev: number) => {\n if (prev === 100) {\n return 0;\n }\n return prev + 10;\n };\n setValue(handleIncrement);\n const interval = setInterval(() => setValue(handleIncrement), 2000);\n return () => clearInterval(interval);\n }, []);\n\n return (\n \n );\n}\n", + "content": "\"use client\";\r\nimport { useEffect, useState } from \"react\";\r\nimport { ProgressBar } from \"@/components/nurui/progress-bar\";\r\n\r\nexport function ProgressBarDemo({ className }: { className?: string }) {\r\n const [value, setValue] = useState(0);\r\n\r\n useEffect(() => {\r\n const handleIncrement = (prev: number) => {\r\n if (prev === 100) {\r\n return 0;\r\n }\r\n return prev + 10;\r\n };\r\n setValue(handleIncrement);\r\n const interval = setInterval(() => setValue(handleIncrement), 2000);\r\n return () => clearInterval(interval);\r\n }, []);\r\n\r\n return (\r\n \r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/progress-bar.tsx", - "content": "import { cn } from \"@/lib/utils\";\n\ninterface ProgressBarProps {\n max: number;\n value: number;\n min: number;\n gaugePrimaryColor: string;\n gaugeSecondaryColor: string;\n className?: string;\n}\n\nexport function ProgressBar({\n max = 100,\n min = 0,\n value = 0,\n gaugePrimaryColor,\n gaugeSecondaryColor,\n className,\n}: ProgressBarProps) {\n const circumference = 2 * Math.PI * 45;\n const percentPx = circumference / 100;\n const currentPercent = Math.round(((value - min) / (max - min)) * 100);\n\n return (\n \n \n {currentPercent <= 90 && currentPercent >= 0 && (\n \n )}\n \n \n \n {currentPercent}\n \n
\n );\n}\n", + "content": "import { cn } from \"@/lib/utils\";\r\n\r\ninterface ProgressBarProps {\r\n max: number;\r\n value: number;\r\n min: number;\r\n gaugePrimaryColor: string;\r\n gaugeSecondaryColor: string;\r\n className?: string;\r\n}\r\n\r\nexport function ProgressBar({\r\n max = 100,\r\n min = 0,\r\n value = 0,\r\n gaugePrimaryColor,\r\n gaugeSecondaryColor,\r\n className,\r\n}: ProgressBarProps) {\r\n const circumference = 2 * Math.PI * 45;\r\n const percentPx = circumference / 100;\r\n const currentPercent = Math.round(((value - min) / (max - min)) * 100);\r\n\r\n return (\r\n \r\n \r\n {currentPercent <= 90 && currentPercent >= 0 && (\r\n \r\n )}\r\n \r\n \r\n \r\n {currentPercent}\r\n \r\n
\r\n );\r\n}\r\n", "type": "registry:component" } ] diff --git a/public/r/project-showcase.json b/public/r/project-showcase.json index f66cb9c..a7c65e2 100644 --- a/public/r/project-showcase.json +++ b/public/r/project-showcase.json @@ -13,17 +13,17 @@ "files": [ { "path": "./src/components/nurui/project-showcase-demo.tsx", - "content": "\"use client\";\nimport React from \"react\";\nimport { ProjectShowCase } from \"@/components/nurui/project-showcase\";\n\nconst ProjectShowCaseDemo = () => {\n function openInNewTab(link: string) {\n window.open(link, \"_blank\", \"noopener,noreferrer\");\n }\n\n return (\n
\n \n \n
\n \n );\n};\n\nexport default ProjectShowCaseDemo;\n", + "content": "\"use client\";\r\nimport React from \"react\";\r\nimport { ProjectShowCase } from \"@/components/nurui/project-showcase\";\r\n\r\nconst ProjectShowCaseDemo = () => {\r\n function openInNewTab(link: string) {\r\n window.open(link, \"_blank\", \"noopener,noreferrer\");\r\n }\r\n\r\n return (\r\n
\r\n \r\n \r\n
\r\n \r\n );\r\n};\r\n\r\nexport default ProjectShowCaseDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/project-showcase.tsx", - "content": "\"use client\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport Image from \"next/image\";\nimport { useEffect, useState, useCallback, useRef } from \"react\";\nimport { HalomotButton } from \"@/components/nurui/halomot-button\";\n\ntype Testimonial = {\n quote: string;\n name: string;\n designation: string;\n src: string;\n link?: string;\n};\n\ntype ProjectShowcaseProps = {\n testimonials: Testimonial[];\n autoplay?: boolean;\n colors?: { name?: string; position?: string; testimony?: string };\n fontSizes?: { name?: string; position?: string; testimony?: string };\n spacing?: {\n top?: string;\n bottom?: string;\n lineHeight?: string;\n nameTop?: string;\n nameBottom?: string;\n positionTop?: string;\n positionBottom?: string;\n testimonyTop?: string;\n testimonyBottom?: string;\n };\n desktopVersionBottomThreshold?: number;\n maxImageWidth?: number;\n imageWidthPercentage?: number;\n mobile?: {\n fontSizes?: { name?: string; position?: string; testimony?: string };\n spacing?: {\n top?: string;\n bottom?: string;\n lineHeight?: string;\n nameTop?: string;\n nameBottom?: string;\n positionTop?: string;\n positionBottom?: string;\n testimonyTop?: string;\n testimonyBottom?: string;\n };\n };\n imageAspectRatio?: number;\n isRTL?: boolean;\n onItemClick?: (link: string) => void;\n outerRounding?: string;\n innerRounding?: string;\n outlineColor?: string;\n hoverOutlineColor?: string;\n buttonInscriptions?: {\n previousButton: string;\n nextButton: string;\n openWebAppButton: string;\n };\n halomotButtonGradient?: string;\n halomotButtonBackground?: string;\n halomotButtonTextColor?: string;\n halomotButtonOuterBorderRadius?: string;\n halomotButtonInnerBorderRadius?: string;\n halomotButtonHoverTextColor?: string;\n};\n\nexport const ProjectShowCase = ({\n testimonials,\n autoplay = false,\n colors = { name: \"#fff\", position: \"gray-500\", testimony: \"gray-500\" },\n fontSizes = { name: \"2xl\", position: \"sm\", testimony: \"lg\" },\n spacing = {\n top: \"20\",\n bottom: \"20\",\n lineHeight: \"1.5\",\n nameTop: \"0\",\n nameBottom: \"0.5em\",\n positionTop: \"0\",\n positionBottom: \"0.25em\",\n testimonyTop: \"1em\",\n testimonyBottom: \"1em\"\n },\n desktopVersionBottomThreshold = 1024,\n mobile = {},\n imageAspectRatio = 1.37,\n isRTL = false,\n onItemClick,\n outerRounding = \"18.2px\",\n innerRounding = \"18px\",\n outlineColor = \"#33313d\",\n hoverOutlineColor = \"#403d4d\",\n buttonInscriptions = {\n previousButton: \"Previous\",\n nextButton: \"Next\",\n openWebAppButton: \"Open Web App\"\n },\n halomotButtonGradient = \"linear-gradient(to right, #a123f4, #603dec)\",\n halomotButtonBackground = \"#111014\",\n halomotButtonTextColor = \"#fff\",\n halomotButtonOuterBorderRadius = \"6.34px\",\n halomotButtonInnerBorderRadius = \"6px\",\n halomotButtonHoverTextColor\n}: ProjectShowcaseProps) => {\n const [active, setActive] = useState(0);\n const [isMobileView, setIsMobileView] = useState(false);\n const [componentWidth, setComponentWidth] = useState(0);\n const componentRef = useRef(null);\n\n // Use Mobile Config (with defaults)\n const currentFontSizes =\n isMobileView && mobile.fontSizes ? mobile.fontSizes : fontSizes;\n const currentSpacing = {\n ...spacing,\n ...(isMobileView && mobile.spacing ? mobile.spacing : {})\n };\n\n const handleNext = useCallback(() => {\n setActive((prev) => (prev + 1) % testimonials.length);\n }, [testimonials.length]);\n\n const handlePrev = () => {\n setActive((prev) => (prev - 1 + testimonials.length) % testimonials.length);\n };\n\n const isActive = (index: number) => {\n return index === active;\n };\n\n useEffect(() => {\n if (autoplay) {\n const interval = setInterval(handleNext, 5000);\n return () => clearInterval(interval);\n }\n }, [autoplay, handleNext]);\n\n const handleResize = useCallback(() => {\n if (componentRef.current) {\n setComponentWidth(componentRef.current.offsetWidth);\n setIsMobileView(\n componentRef.current.offsetWidth < desktopVersionBottomThreshold\n );\n }\n }, [desktopVersionBottomThreshold]);\n\n useEffect(() => {\n const node = componentRef.current;\n const resizeObserver = new ResizeObserver(handleResize);\n if (node) {\n resizeObserver.observe(node);\n }\n handleResize(); // Initial check\n return () => {\n if (node) {\n resizeObserver.unobserve(node);\n }\n };\n }, [handleResize]);\n\n const randomRotateY = () => {\n return Math.floor(Math.random() * 21) - 10;\n };\n\n const calculateGap = (width: number) => {\n const minWidth = 1024;\n const maxWidth = 1456;\n const minGap = 60;\n const maxGap = 86;\n if (width <= minWidth) return minGap;\n if (width >= maxWidth)\n return Math.max(minGap, maxGap + 0.06018 * (width - maxWidth));\n return (\n minGap + (maxGap - minGap) * ((width - minWidth) / (maxWidth - minWidth))\n );\n };\n\n return (\n \n \n {isRTL && !isMobileView ? (\n <>\n
\n \n \n {testimonials.map((testimonial, index) => (\n \n \n \n ))}\n \n
\n \n
\n \n \n {testimonials[active].name}\n \n \n {testimonials[active].designation}\n

\n \n {testimonials[active].quote.split(\" \").map((word, index) => (\n \n {word} \n \n ))}\n \n \n \n \n \n \n onItemClick && onItemClick(testimonials[active].link || \"\")\n }\n fillWidth\n gradient={halomotButtonGradient}\n backgroundColor={halomotButtonBackground}\n textColor={halomotButtonTextColor}\n innerBorderRadius={halomotButtonInnerBorderRadius}\n outerBorderRadius={halomotButtonOuterBorderRadius}\n {...(halomotButtonHoverTextColor\n ? { hoverTextColor: halomotButtonHoverTextColor }\n : {})}\n href={testimonials[active].link}\n />\n
\n \n \n ) : (\n <>\n
\n \n \n {testimonials.map((testimonial, index) => (\n \n \n \n ))}\n \n
\n \n
\n \n \n {testimonials[active].name}\n \n \n {testimonials[active].designation}\n

\n \n {testimonials[active].quote.split(\" \").map((word, index) => (\n \n {word} \n \n ))}\n \n \n \n \n \n \n onItemClick && onItemClick(testimonials[active].link || \"\")\n }\n fillWidth\n gradient={halomotButtonGradient}\n backgroundColor={halomotButtonBackground}\n textColor={halomotButtonTextColor}\n innerBorderRadius={halomotButtonInnerBorderRadius}\n outerBorderRadius={halomotButtonOuterBorderRadius}\n {...(halomotButtonHoverTextColor\n ? { hoverTextColor: halomotButtonHoverTextColor }\n : {})}\n href={testimonials[active].link}\n />\n
\n \n \n )}\n \n \n );\n};\n\ntype ImageContainerProps = {\n src: string;\n alt: string;\n outerRounding: string;\n innerRounding: string;\n outlineColor: string;\n hoverOutlineColor: string;\n};\n\nconst ImageContainer = ({\n src,\n alt,\n outerRounding,\n innerRounding,\n outlineColor,\n hoverOutlineColor\n}: ImageContainerProps) => (\n \n \n \n \n \n \n);\n", + "content": "\"use client\";\r\nimport { motion, AnimatePresence } from \"framer-motion\";\r\nimport Image from \"next/image\";\r\nimport { useEffect, useState, useCallback, useRef } from \"react\";\r\nimport { HalomotButton } from \"@/components/nurui/halomot-button\";\r\n\r\ntype Testimonial = {\r\n quote: string;\r\n name: string;\r\n designation: string;\r\n src: string;\r\n link?: string;\r\n};\r\n\r\ntype ProjectShowcaseProps = {\r\n testimonials: Testimonial[];\r\n autoplay?: boolean;\r\n colors?: { name?: string; position?: string; testimony?: string };\r\n fontSizes?: { name?: string; position?: string; testimony?: string };\r\n spacing?: {\r\n top?: string;\r\n bottom?: string;\r\n lineHeight?: string;\r\n nameTop?: string;\r\n nameBottom?: string;\r\n positionTop?: string;\r\n positionBottom?: string;\r\n testimonyTop?: string;\r\n testimonyBottom?: string;\r\n };\r\n desktopVersionBottomThreshold?: number;\r\n maxImageWidth?: number;\r\n imageWidthPercentage?: number;\r\n mobile?: {\r\n fontSizes?: { name?: string; position?: string; testimony?: string };\r\n spacing?: {\r\n top?: string;\r\n bottom?: string;\r\n lineHeight?: string;\r\n nameTop?: string;\r\n nameBottom?: string;\r\n positionTop?: string;\r\n positionBottom?: string;\r\n testimonyTop?: string;\r\n testimonyBottom?: string;\r\n };\r\n };\r\n imageAspectRatio?: number;\r\n isRTL?: boolean;\r\n onItemClick?: (link: string) => void;\r\n outerRounding?: string;\r\n innerRounding?: string;\r\n outlineColor?: string;\r\n hoverOutlineColor?: string;\r\n buttonInscriptions?: {\r\n previousButton: string;\r\n nextButton: string;\r\n openWebAppButton: string;\r\n };\r\n halomotButtonGradient?: string;\r\n halomotButtonBackground?: string;\r\n halomotButtonTextColor?: string;\r\n halomotButtonOuterBorderRadius?: string;\r\n halomotButtonInnerBorderRadius?: string;\r\n halomotButtonHoverTextColor?: string;\r\n};\r\n\r\nexport const ProjectShowCase = ({\r\n testimonials,\r\n autoplay = false,\r\n colors = { name: \"#fff\", position: \"gray-500\", testimony: \"gray-500\" },\r\n fontSizes = { name: \"2xl\", position: \"sm\", testimony: \"lg\" },\r\n spacing = {\r\n top: \"20\",\r\n bottom: \"20\",\r\n lineHeight: \"1.5\",\r\n nameTop: \"0\",\r\n nameBottom: \"0.5em\",\r\n positionTop: \"0\",\r\n positionBottom: \"0.25em\",\r\n testimonyTop: \"1em\",\r\n testimonyBottom: \"1em\"\r\n },\r\n desktopVersionBottomThreshold = 1024,\r\n mobile = {},\r\n imageAspectRatio = 1.37,\r\n isRTL = false,\r\n onItemClick,\r\n outerRounding = \"18.2px\",\r\n innerRounding = \"18px\",\r\n outlineColor = \"#33313d\",\r\n hoverOutlineColor = \"#403d4d\",\r\n buttonInscriptions = {\r\n previousButton: \"Previous\",\r\n nextButton: \"Next\",\r\n openWebAppButton: \"Open Web App\"\r\n },\r\n halomotButtonGradient = \"linear-gradient(to right, #a123f4, #603dec)\",\r\n halomotButtonBackground = \"#111014\",\r\n halomotButtonTextColor = \"#fff\",\r\n halomotButtonOuterBorderRadius = \"6.34px\",\r\n halomotButtonInnerBorderRadius = \"6px\",\r\n halomotButtonHoverTextColor\r\n}: ProjectShowcaseProps) => {\r\n const [active, setActive] = useState(0);\r\n const [isMobileView, setIsMobileView] = useState(false);\r\n const [componentWidth, setComponentWidth] = useState(0);\r\n const componentRef = useRef(null);\r\n\r\n // Use Mobile Config (with defaults)\r\n const currentFontSizes =\r\n isMobileView && mobile.fontSizes ? mobile.fontSizes : fontSizes;\r\n const currentSpacing = {\r\n ...spacing,\r\n ...(isMobileView && mobile.spacing ? mobile.spacing : {})\r\n };\r\n\r\n const handleNext = useCallback(() => {\r\n setActive((prev) => (prev + 1) % testimonials.length);\r\n }, [testimonials.length]);\r\n\r\n const handlePrev = () => {\r\n setActive((prev) => (prev - 1 + testimonials.length) % testimonials.length);\r\n };\r\n\r\n const isActive = (index: number) => {\r\n return index === active;\r\n };\r\n\r\n useEffect(() => {\r\n if (autoplay) {\r\n const interval = setInterval(handleNext, 5000);\r\n return () => clearInterval(interval);\r\n }\r\n }, [autoplay, handleNext]);\r\n\r\n const handleResize = useCallback(() => {\r\n if (componentRef.current) {\r\n setComponentWidth(componentRef.current.offsetWidth);\r\n setIsMobileView(\r\n componentRef.current.offsetWidth < desktopVersionBottomThreshold\r\n );\r\n }\r\n }, [desktopVersionBottomThreshold]);\r\n\r\n useEffect(() => {\r\n const node = componentRef.current;\r\n const resizeObserver = new ResizeObserver(handleResize);\r\n if (node) {\r\n resizeObserver.observe(node);\r\n }\r\n handleResize(); // Initial check\r\n return () => {\r\n if (node) {\r\n resizeObserver.unobserve(node);\r\n }\r\n };\r\n }, [handleResize]);\r\n\r\n const randomRotateY = () => {\r\n return Math.floor(Math.random() * 21) - 10;\r\n };\r\n\r\n const calculateGap = (width: number) => {\r\n const minWidth = 1024;\r\n const maxWidth = 1456;\r\n const minGap = 60;\r\n const maxGap = 86;\r\n if (width <= minWidth) return minGap;\r\n if (width >= maxWidth)\r\n return Math.max(minGap, maxGap + 0.06018 * (width - maxWidth));\r\n return (\r\n minGap + (maxGap - minGap) * ((width - minWidth) / (maxWidth - minWidth))\r\n );\r\n };\r\n\r\n return (\r\n \r\n \r\n {isRTL && !isMobileView ? (\r\n <>\r\n
\r\n \r\n \r\n {testimonials.map((testimonial, index) => (\r\n \r\n \r\n \r\n ))}\r\n \r\n
\r\n \r\n
\r\n \r\n \r\n {testimonials[active].name}\r\n \r\n \r\n {testimonials[active].designation}\r\n

\r\n \r\n {testimonials[active].quote.split(\" \").map((word, index) => (\r\n \r\n {word} \r\n \r\n ))}\r\n \r\n \r\n \r\n \r\n \r\n \r\n onItemClick && onItemClick(testimonials[active].link || \"\")\r\n }\r\n fillWidth\r\n gradient={halomotButtonGradient}\r\n backgroundColor={halomotButtonBackground}\r\n textColor={halomotButtonTextColor}\r\n innerBorderRadius={halomotButtonInnerBorderRadius}\r\n outerBorderRadius={halomotButtonOuterBorderRadius}\r\n {...(halomotButtonHoverTextColor\r\n ? { hoverTextColor: halomotButtonHoverTextColor }\r\n : {})}\r\n href={testimonials[active].link}\r\n />\r\n
\r\n \r\n \r\n ) : (\r\n <>\r\n
\r\n \r\n \r\n {testimonials.map((testimonial, index) => (\r\n \r\n \r\n \r\n ))}\r\n \r\n
\r\n \r\n
\r\n \r\n \r\n {testimonials[active].name}\r\n \r\n \r\n {testimonials[active].designation}\r\n

\r\n \r\n {testimonials[active].quote.split(\" \").map((word, index) => (\r\n \r\n {word} \r\n \r\n ))}\r\n \r\n \r\n \r\n \r\n \r\n \r\n onItemClick && onItemClick(testimonials[active].link || \"\")\r\n }\r\n fillWidth\r\n gradient={halomotButtonGradient}\r\n backgroundColor={halomotButtonBackground}\r\n textColor={halomotButtonTextColor}\r\n innerBorderRadius={halomotButtonInnerBorderRadius}\r\n outerBorderRadius={halomotButtonOuterBorderRadius}\r\n {...(halomotButtonHoverTextColor\r\n ? { hoverTextColor: halomotButtonHoverTextColor }\r\n : {})}\r\n href={testimonials[active].link}\r\n />\r\n
\r\n \r\n \r\n )}\r\n \r\n \r\n );\r\n};\r\n\r\ntype ImageContainerProps = {\r\n src: string;\r\n alt: string;\r\n outerRounding: string;\r\n innerRounding: string;\r\n outlineColor: string;\r\n hoverOutlineColor: string;\r\n};\r\n\r\nconst ImageContainer = ({\r\n src,\r\n alt,\r\n outerRounding,\r\n innerRounding,\r\n outlineColor,\r\n hoverOutlineColor\r\n}: ImageContainerProps) => (\r\n \r\n \r\n \r\n \r\n \r\n \r\n);\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/halomot-button.tsx", - "content": "\"use client\";\nimport React, { useState } from \"react\";\n\ninterface HalomotButtonProps {\n gradient?: string; // Gradient for the button border/background\n inscription: string; // Button text\n onClick: () => void;\n fillWidth?: boolean;\n fixedWidth?: string;\n href?: string;\n backgroundColor?: string; // Solid color for the inner button (not gradient)\n icon?: React.ReactElement>;\n borderWidth?: string; // Controls the padding (thickness of the gradient border)\n padding?: string; // Custom padding for the inner button, e.g., \"1rem 4rem\"\n outerBorderRadius?: string; // Border radius for the gradient outer border\n innerBorderRadius?: string; // Border radius for the inner button\n textColor?: string; // Text color for the button, default #fff\n hoverTextColor?: string;\n}\n\nexport const HalomotButton: React.FC = ({\n gradient = \"linear-gradient(135deg, #4776cb, #a19fe5, #6cc606)\",\n inscription,\n onClick,\n fillWidth = false,\n fixedWidth,\n href,\n backgroundColor = \"#fff\",\n icon,\n borderWidth = \"1px\",\n padding,\n outerBorderRadius = \"6.34px\",\n innerBorderRadius = \"6px\",\n textColor = \"#000\",\n hoverTextColor,\n}) => {\n const [isHovered, setIsHovered] = useState(false);\n const [delayedColor, setDelayedColor] = useState(\n undefined,\n );\n\n // Container style for fixed width\n const containerStyle: React.CSSProperties = fixedWidth\n ? { width: fixedWidth, display: \"inline-block\" }\n : {};\n\n // Outer button style (gradient border)\n const buttonStyle: React.CSSProperties = {\n margin: fillWidth || fixedWidth ? \"0\" : \"auto\",\n padding: borderWidth,\n background: gradient,\n border: \"0\",\n borderRadius: outerBorderRadius,\n color: textColor,\n fontWeight: \"bold\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n textDecoration: \"none\",\n userSelect: \"none\",\n WebkitUserSelect: \"none\",\n whiteSpace: \"nowrap\",\n transition: \"all .3s\",\n width: fillWidth || fixedWidth ? \"100%\" : \"fit-content\",\n flexDirection: \"row\",\n boxSizing: \"border-box\",\n };\n\n // Inner span style (actual clickable area)\n const spanStyle: React.CSSProperties = {\n background: isHovered ? \"none\" : backgroundColor,\n padding: padding ?? (fillWidth || fixedWidth ? \"1rem 0\" : \"1rem 4rem\"),\n border: \"0\",\n borderRadius: innerBorderRadius,\n width: \"100%\",\n height: \"100%\",\n transition: hoverTextColor\n ? \"color 0.3s, background 300ms\"\n : \"background 300ms\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n fontWeight: \"bold\",\n color: delayedColor ? delayedColor : textColor,\n whiteSpace: \"nowrap\",\n fontFamily: \"inherit\",\n fontSize: \"1rem\",\n gap: icon ? \"0.5em\" : 0,\n flexDirection: \"row\",\n boxSizing: \"border-box\",\n cursor: \"pointer\",\n };\n\n // Icon style\n const iconStyle: React.CSSProperties = {\n display: \"inline-flex\",\n alignItems: \"center\",\n height: \"1em\",\n width: \"1em\",\n fontSize: \"1.1em\",\n verticalAlign: \"middle\",\n flexShrink: 0,\n };\n\n // No delay, just set color immediately\n const handleMouseEnter = () => {\n setIsHovered(true);\n if (hoverTextColor) {\n setDelayedColor(hoverTextColor);\n }\n };\n\n const handleMouseLeave = () => {\n setIsHovered(false);\n setDelayedColor(undefined);\n };\n\n const handleClick = (\n e: React.MouseEvent,\n ) => {\n e.preventDefault();\n onClick();\n };\n\n const ButtonContent = (\n \n {icon && React.cloneElement(icon, { style: iconStyle })}\n {inscription}\n \n );\n\n const ButtonElement = href ? (\n \n {ButtonContent}\n \n ) : (\n \n );\n\n return fixedWidth ? (\n
{ButtonElement}
\n ) : (\n ButtonElement\n );\n};\n", + "content": "\"use client\";\r\nimport React, { useState } from \"react\";\r\n\r\ninterface HalomotButtonProps {\r\n gradient?: string; // Gradient for the button border/background\r\n inscription: string; // Button text\r\n onClick: () => void;\r\n fillWidth?: boolean;\r\n fixedWidth?: string;\r\n href?: string;\r\n backgroundColor?: string; // Solid color for the inner button (not gradient)\r\n icon?: React.ReactElement>;\r\n borderWidth?: string; // Controls the padding (thickness of the gradient border)\r\n padding?: string; // Custom padding for the inner button, e.g., \"1rem 4rem\"\r\n outerBorderRadius?: string; // Border radius for the gradient outer border\r\n innerBorderRadius?: string; // Border radius for the inner button\r\n textColor?: string; // Text color for the button, default #fff\r\n hoverTextColor?: string;\r\n}\r\n\r\nexport const HalomotButton: React.FC = ({\r\n gradient = \"linear-gradient(135deg, #4776cb, #a19fe5, #6cc606)\",\r\n inscription,\r\n onClick,\r\n fillWidth = false,\r\n fixedWidth,\r\n href,\r\n backgroundColor = \"#fff\",\r\n icon,\r\n borderWidth = \"1px\",\r\n padding,\r\n outerBorderRadius = \"6.34px\",\r\n innerBorderRadius = \"6px\",\r\n textColor = \"#000\",\r\n hoverTextColor,\r\n}) => {\r\n const [isHovered, setIsHovered] = useState(false);\r\n const [delayedColor, setDelayedColor] = useState(\r\n undefined,\r\n );\r\n\r\n // Container style for fixed width\r\n const containerStyle: React.CSSProperties = fixedWidth\r\n ? { width: fixedWidth, display: \"inline-block\" }\r\n : {};\r\n\r\n // Outer button style (gradient border)\r\n const buttonStyle: React.CSSProperties = {\r\n margin: fillWidth || fixedWidth ? \"0\" : \"auto\",\r\n padding: borderWidth,\r\n background: gradient,\r\n border: \"0\",\r\n borderRadius: outerBorderRadius,\r\n color: textColor,\r\n fontWeight: \"bold\",\r\n display: \"flex\",\r\n justifyContent: \"center\",\r\n alignItems: \"center\",\r\n textDecoration: \"none\",\r\n userSelect: \"none\",\r\n WebkitUserSelect: \"none\",\r\n whiteSpace: \"nowrap\",\r\n transition: \"all .3s\",\r\n width: fillWidth || fixedWidth ? \"100%\" : \"fit-content\",\r\n flexDirection: \"row\",\r\n boxSizing: \"border-box\",\r\n };\r\n\r\n // Inner span style (actual clickable area)\r\n const spanStyle: React.CSSProperties = {\r\n background: isHovered ? \"none\" : backgroundColor,\r\n padding: padding ?? (fillWidth || fixedWidth ? \"1rem 0\" : \"1rem 4rem\"),\r\n border: \"0\",\r\n borderRadius: innerBorderRadius,\r\n width: \"100%\",\r\n height: \"100%\",\r\n transition: hoverTextColor\r\n ? \"color 0.3s, background 300ms\"\r\n : \"background 300ms\",\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n fontWeight: \"bold\",\r\n color: delayedColor ? delayedColor : textColor,\r\n whiteSpace: \"nowrap\",\r\n fontFamily: \"inherit\",\r\n fontSize: \"1rem\",\r\n gap: icon ? \"0.5em\" : 0,\r\n flexDirection: \"row\",\r\n boxSizing: \"border-box\",\r\n cursor: \"pointer\",\r\n };\r\n\r\n // Icon style\r\n const iconStyle: React.CSSProperties = {\r\n display: \"inline-flex\",\r\n alignItems: \"center\",\r\n height: \"1em\",\r\n width: \"1em\",\r\n fontSize: \"1.1em\",\r\n verticalAlign: \"middle\",\r\n flexShrink: 0,\r\n };\r\n\r\n // No delay, just set color immediately\r\n const handleMouseEnter = () => {\r\n setIsHovered(true);\r\n if (hoverTextColor) {\r\n setDelayedColor(hoverTextColor);\r\n }\r\n };\r\n\r\n const handleMouseLeave = () => {\r\n setIsHovered(false);\r\n setDelayedColor(undefined);\r\n };\r\n\r\n const handleClick = (\r\n e: React.MouseEvent,\r\n ) => {\r\n e.preventDefault();\r\n onClick();\r\n };\r\n\r\n const ButtonContent = (\r\n \r\n {icon && React.cloneElement(icon, { style: iconStyle })}\r\n {inscription}\r\n \r\n );\r\n\r\n const ButtonElement = href ? (\r\n \r\n {ButtonContent}\r\n \r\n ) : (\r\n \r\n );\r\n\r\n return fixedWidth ? (\r\n
{ButtonElement}
\r\n ) : (\r\n ButtonElement\r\n );\r\n};\r\n", "type": "registry:component" } ] diff --git a/public/r/quad-ring-loader.json b/public/r/quad-ring-loader.json new file mode 100644 index 0000000..2fc9ae3 --- /dev/null +++ b/public/r/quad-ring-loader.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "quad-ring-loader", + "type": "registry:component", + "dependencies": [ + "styled-components" + ], + "devDependencies": [], + "registryDependencies": [], + "files": [ + { + "path": "./src/components/nurui/quad-ring-loader-demo.tsx", + "content": "import React from 'react'\r\nimport QuadRingLoader from './quad-ring-loader'\r\n\r\nconst QuadRingLoaderDemo = () => {\r\n return (\r\n
\r\n \r\n
\r\n )\r\n}\r\n\r\nexport default QuadRingLoaderDemo", + "type": "registry:component" + }, + { + "path": "./src/components/nurui/quad-ring-loader.tsx", + "content": "\"use client\";\r\nimport React from 'react';\r\nimport styled from 'styled-components';\r\n\r\nconst QuadRingLoader = () => {\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n );\r\n};\r\n\r\nconst StyledWrapper = styled.div`\r\n .pl {\r\n width: 6em;\r\n height: 6em;\r\n }\r\n\r\n .pl__ring {\r\n animation: ringA 2s linear infinite;\r\n }\r\n\r\n /* Neon Pop Colors */\r\n .pl__ring--a {\r\n stroke: #ff0080; /* Neon Pink */\r\n }\r\n\r\n .pl__ring--b {\r\n animation-name: ringB;\r\n stroke: #7928ca; /* Neon Purple */\r\n }\r\n\r\n .pl__ring--c {\r\n animation-name: ringC;\r\n stroke: #00f5ff; /* Neon Cyan */\r\n }\r\n\r\n .pl__ring--d {\r\n animation-name: ringD;\r\n stroke: #00ff85; /* Neon Green */\r\n }\r\n\r\n /* Animations */\r\n @keyframes ringA {\r\n from, 4% {\r\n stroke-dasharray: 0 660;\r\n stroke-width: 20;\r\n stroke-dashoffset: -330;\r\n }\r\n\r\n 12% {\r\n stroke-dasharray: 60 600;\r\n stroke-width: 30;\r\n stroke-dashoffset: -335;\r\n }\r\n\r\n 32% {\r\n stroke-dasharray: 60 600;\r\n stroke-width: 30;\r\n stroke-dashoffset: -595;\r\n }\r\n\r\n 40%, 54% {\r\n stroke-dasharray: 0 660;\r\n stroke-width: 20;\r\n stroke-dashoffset: -660;\r\n }\r\n\r\n 62% {\r\n stroke-dasharray: 60 600;\r\n stroke-width: 30;\r\n stroke-dashoffset: -665;\r\n }\r\n\r\n 82% {\r\n stroke-dasharray: 60 600;\r\n stroke-width: 30;\r\n stroke-dashoffset: -925;\r\n }\r\n\r\n 90%, to {\r\n stroke-dasharray: 0 660;\r\n stroke-width: 20;\r\n stroke-dashoffset: -990;\r\n }\r\n }\r\n\r\n @keyframes ringB {\r\n from, 12% {\r\n stroke-dasharray: 0 220;\r\n stroke-width: 20;\r\n stroke-dashoffset: -110;\r\n }\r\n\r\n 20% {\r\n stroke-dasharray: 20 200;\r\n stroke-width: 30;\r\n stroke-dashoffset: -115;\r\n }\r\n\r\n 40% {\r\n stroke-dasharray: 20 200;\r\n stroke-width: 30;\r\n stroke-dashoffset: -195;\r\n }\r\n\r\n 48%, 62% {\r\n stroke-dasharray: 0 220;\r\n stroke-width: 20;\r\n stroke-dashoffset: -220;\r\n }\r\n\r\n 70% {\r\n stroke-dasharray: 20 200;\r\n stroke-width: 30;\r\n stroke-dashoffset: -225;\r\n }\r\n\r\n 90% {\r\n stroke-dasharray: 20 200;\r\n stroke-width: 30;\r\n stroke-dashoffset: -305;\r\n }\r\n\r\n 98%, to {\r\n stroke-dasharray: 0 220;\r\n stroke-width: 20;\r\n stroke-dashoffset: -330;\r\n }\r\n }\r\n\r\n @keyframes ringC {\r\n from {\r\n stroke-dasharray: 0 440;\r\n stroke-width: 20;\r\n stroke-dashoffset: 0;\r\n }\r\n\r\n 8% {\r\n stroke-dasharray: 40 400;\r\n stroke-width: 30;\r\n stroke-dashoffset: -5;\r\n }\r\n\r\n 28% {\r\n stroke-dasharray: 40 400;\r\n stroke-width: 30;\r\n stroke-dashoffset: -175;\r\n }\r\n\r\n 36%, 58% {\r\n stroke-dasharray: 0 440;\r\n stroke-width: 20;\r\n stroke-dashoffset: -220;\r\n }\r\n\r\n 66% {\r\n stroke-dasharray: 40 400;\r\n stroke-width: 30;\r\n stroke-dashoffset: -225;\r\n }\r\n\r\n 86% {\r\n stroke-dasharray: 40 400;\r\n stroke-width: 30;\r\n stroke-dashoffset: -395;\r\n }\r\n\r\n 94%, to {\r\n stroke-dasharray: 0 440;\r\n stroke-width: 20;\r\n stroke-dashoffset: -440;\r\n }\r\n }\r\n\r\n @keyframes ringD {\r\n from, 8% {\r\n stroke-dasharray: 0 440;\r\n stroke-width: 20;\r\n stroke-dashoffset: 0;\r\n }\r\n\r\n 16% {\r\n stroke-dasharray: 40 400;\r\n stroke-width: 30;\r\n stroke-dashoffset: -5;\r\n }\r\n\r\n 36% {\r\n stroke-dasharray: 40 400;\r\n stroke-width: 30;\r\n stroke-dashoffset: -175;\r\n }\r\n\r\n 44%, 50% {\r\n stroke-dasharray: 0 440;\r\n stroke-width: 20;\r\n stroke-dashoffset: -220;\r\n }\r\n\r\n 58% {\r\n stroke-dasharray: 40 400;\r\n stroke-width: 30;\r\n stroke-dashoffset: -225;\r\n }\r\n\r\n 78% {\r\n stroke-dasharray: 40 400;\r\n stroke-width: 30;\r\n stroke-dashoffset: -395;\r\n }\r\n\r\n 86%, to {\r\n stroke-dasharray: 0 440;\r\n stroke-width: 20;\r\n stroke-dashoffset: -440;\r\n }\r\n }\r\n`;\r\n\r\nexport default QuadRingLoader;\r\n", + "type": "registry:component" + } + ] +} \ No newline at end of file diff --git a/public/r/registry.json b/public/r/registry.json index cc0eb3e..267d051 100644 --- a/public/r/registry.json +++ b/public/r/registry.json @@ -1319,7 +1319,6 @@ "type": "registry:component" } ] - }, { "name": "counter-loading", @@ -1337,7 +1336,25 @@ { "path": "./src/components/nurui/counter-loading.tsx", "type": "registry:component" - + } + ] +}, + { + "name": "quad-ring-loader", + "type": "registry:component", + "devDependencies": [], + "dependencies": [ + "styled-components" + ], + "registryDependencies": [], + "files": [ + { + "path": "./src/components/nurui/quad-ring-loader-demo.tsx", + "type": "registry:component" + }, + { + "path": "./src/components/nurui/quad-ring-loader.tsx", + "type": "registry:component" } ] } diff --git a/public/r/research-hero.json b/public/r/research-hero.json index 4b83021..ff5a570 100644 --- a/public/r/research-hero.json +++ b/public/r/research-hero.json @@ -15,32 +15,32 @@ "files": [ { "path": "./src/components/nurui/research-hero-demo.tsx", - "content": "import ResearchHero from \"@/components/nurui/research-hero\";\nimport { SparklesCore } from \"@/components/nurui/sparkles\";\n\nexport default function ResearchHeroDemo() {\n return (\n
\n {/* Ambient background with moving particles */}\n
\n \n
\n\n
\n \n
\n
\n );\n}\n", + "content": "import ResearchHero from \"@/components/nurui/research-hero\";\r\nimport { SparklesCore } from \"@/components/nurui/sparkles\";\r\n\r\nexport default function ResearchHeroDemo() {\r\n return (\r\n
\r\n {/* Ambient background with moving particles */}\r\n
\r\n \r\n
\r\n\r\n
\r\n \r\n
\r\n
\r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/research-hero.tsx", - "content": "\"use client\";\nimport { Button } from \"@/components/ui/button\";\nimport { motion } from \"framer-motion\";\nimport { FileText, Sparkles } from \"lucide-react\";\nimport { FloatingPaper } from \"@/components/nurui/floating-paper\";\nimport { RoboAnimation } from \"@/components/nurui/robo-animation\";\n\nexport default function ResearchHero() {\n return (\n
\n {/* Floating papers background */}\n
\n \n
\n\n
\n
\n \n

\n Transform Your Research with\n \n {\" \"}\n AI Power\n \n

\n \n\n \n Upload your research papers and let our AI transform them into\n engaging presentations, podcasts, and visual content.\n \n\n \n \n \n Upload Paper\n \n \n \n See Examples\n \n \n
\n
\n\n {/* Animated robot */}\n
\n \n
\n
\n );\n}\n", + "content": "\"use client\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { motion } from \"framer-motion\";\r\nimport { FileText, Sparkles } from \"lucide-react\";\r\nimport { FloatingPaper } from \"@/components/nurui/floating-paper\";\r\nimport { RoboAnimation } from \"@/components/nurui/robo-animation\";\r\n\r\nexport default function ResearchHero() {\r\n return (\r\n
\r\n {/* Floating papers background */}\r\n
\r\n \r\n
\r\n\r\n
\r\n
\r\n \r\n

\r\n Transform Your Research with\r\n \r\n {\" \"}\r\n AI Power\r\n \r\n

\r\n \r\n\r\n \r\n Upload your research papers and let our AI transform them into\r\n engaging presentations, podcasts, and visual content.\r\n \r\n\r\n \r\n \r\n \r\n Upload Paper\r\n \r\n \r\n \r\n See Examples\r\n \r\n \r\n
\r\n
\r\n\r\n {/* Animated robot */}\r\n
\r\n \r\n
\r\n
\r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/sparkles.tsx", - "content": "\"use client\"\n\nimport { useMousePosition } from \"@/utils/use-mouse-position\"\nimport { useEffect, useRef, useState } from \"react\"\n\ninterface SparklesProps {\n id?: string\n background?: string\n minSize?: number\n maxSize?: number\n particleDensity?: number\n className?: string\n particleColor?: string\n}\n\nexport const SparklesCore = ({\n id = \"tsparticles\",\n background = \"transparent\",\n minSize = 0.6,\n maxSize = 1.4,\n particleDensity = 100,\n className = \"h-full w-full\",\n particleColor = \"#FFFFFF\",\n}: SparklesProps) => {\n const canvasRef = useRef(null)\n const mousePosition = useMousePosition()\n const [dimensions, setDimensions] = useState({ width: 1200, height: 800 })\n\n useEffect(() => {\n if (typeof window === \"undefined\") return\n\n setDimensions({\n width: window.innerWidth,\n height: window.innerHeight,\n })\n\n const canvas = canvasRef.current\n if (!canvas) return\n\n const ctx = canvas.getContext(\"2d\")\n if (!ctx) return\n\n let particles: Particle[] = []\n let animationFrameId: number\n\n canvas.width = window.innerWidth\n canvas.height = window.innerHeight\n\n class Particle {\n x: number\n y: number\n size: number\n speedX: number\n speedY: number\n\n constructor() {\n this.x = Math.random() * canvas!.width\n this.y = Math.random() * canvas!.height\n this.size = Math.random() * (maxSize - minSize) + minSize\n this.speedX = Math.random() * 0.5 - 0.25\n this.speedY = Math.random() * 0.5 - 0.25\n }\n\n update() {\n this.x += this.speedX\n this.y += this.speedY\n\n if (canvas) {\n if (this.x > canvas.width) this.x = 0\n if (this.x < 0) this.x = canvas.width\n if (this.y > canvas.height) this.y = 0\n if (this.y < 0) this.y = canvas.height\n }\n\n // Mouse interaction\n const dx = mousePosition.x - this.x\n const dy = mousePosition.y - this.y\n const distance = Math.sqrt(dx * dx + dy * dy)\n if (distance < 100) {\n const angle = Math.atan2(dy, dx)\n this.x -= Math.cos(angle) * 1\n this.y -= Math.sin(angle) * 1\n }\n }\n\n draw() {\n if (!ctx) return\n ctx.fillStyle = particleColor\n ctx.beginPath()\n ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2)\n ctx.fill()\n }\n }\n\n const init = () => {\n particles = []\n for (let i = 0; i < particleDensity; i++) {\n particles.push(new Particle())\n }\n }\n\n const animate = () => {\n if (!ctx) return\n ctx.clearRect(0, 0, canvas.width, canvas.height)\n\n particles.forEach((particle) => {\n particle.update()\n particle.draw()\n })\n\n animationFrameId = requestAnimationFrame(animate)\n }\n\n init()\n animate()\n\n const handleResize = () => {\n if (typeof window === \"undefined\") return\n\n canvas.width = window.innerWidth\n canvas.height = window.innerHeight\n setDimensions({\n width: window.innerWidth,\n height: window.innerHeight,\n })\n init()\n }\n\n window.addEventListener(\"resize\", handleResize)\n\n return () => {\n window.removeEventListener(\"resize\", handleResize)\n cancelAnimationFrame(animationFrameId)\n }\n }, [maxSize, minSize, particleColor, particleDensity, mousePosition.x, mousePosition.y])\n\n return (\n \n )\n}\n", + "content": "\"use client\"\r\n\r\nimport { useMousePosition } from \"@/utils/use-mouse-position\"\r\nimport { useEffect, useRef, useState } from \"react\"\r\n\r\ninterface SparklesProps {\r\n id?: string\r\n background?: string\r\n minSize?: number\r\n maxSize?: number\r\n particleDensity?: number\r\n className?: string\r\n particleColor?: string\r\n}\r\n\r\nexport const SparklesCore = ({\r\n id = \"tsparticles\",\r\n background = \"transparent\",\r\n minSize = 0.6,\r\n maxSize = 1.4,\r\n particleDensity = 100,\r\n className = \"h-full w-full\",\r\n particleColor = \"#FFFFFF\",\r\n}: SparklesProps) => {\r\n const canvasRef = useRef(null)\r\n const mousePosition = useMousePosition()\r\n const [dimensions, setDimensions] = useState({ width: 1200, height: 800 })\r\n\r\n useEffect(() => {\r\n if (typeof window === \"undefined\") return\r\n\r\n setDimensions({\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n })\r\n\r\n const canvas = canvasRef.current\r\n if (!canvas) return\r\n\r\n const ctx = canvas.getContext(\"2d\")\r\n if (!ctx) return\r\n\r\n let particles: Particle[] = []\r\n let animationFrameId: number\r\n\r\n canvas.width = window.innerWidth\r\n canvas.height = window.innerHeight\r\n\r\n class Particle {\r\n x: number\r\n y: number\r\n size: number\r\n speedX: number\r\n speedY: number\r\n\r\n constructor() {\r\n this.x = Math.random() * canvas!.width\r\n this.y = Math.random() * canvas!.height\r\n this.size = Math.random() * (maxSize - minSize) + minSize\r\n this.speedX = Math.random() * 0.5 - 0.25\r\n this.speedY = Math.random() * 0.5 - 0.25\r\n }\r\n\r\n update() {\r\n this.x += this.speedX\r\n this.y += this.speedY\r\n\r\n if (canvas) {\r\n if (this.x > canvas.width) this.x = 0\r\n if (this.x < 0) this.x = canvas.width\r\n if (this.y > canvas.height) this.y = 0\r\n if (this.y < 0) this.y = canvas.height\r\n }\r\n\r\n // Mouse interaction\r\n const dx = mousePosition.x - this.x\r\n const dy = mousePosition.y - this.y\r\n const distance = Math.sqrt(dx * dx + dy * dy)\r\n if (distance < 100) {\r\n const angle = Math.atan2(dy, dx)\r\n this.x -= Math.cos(angle) * 1\r\n this.y -= Math.sin(angle) * 1\r\n }\r\n }\r\n\r\n draw() {\r\n if (!ctx) return\r\n ctx.fillStyle = particleColor\r\n ctx.beginPath()\r\n ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2)\r\n ctx.fill()\r\n }\r\n }\r\n\r\n const init = () => {\r\n particles = []\r\n for (let i = 0; i < particleDensity; i++) {\r\n particles.push(new Particle())\r\n }\r\n }\r\n\r\n const animate = () => {\r\n if (!ctx) return\r\n ctx.clearRect(0, 0, canvas.width, canvas.height)\r\n\r\n particles.forEach((particle) => {\r\n particle.update()\r\n particle.draw()\r\n })\r\n\r\n animationFrameId = requestAnimationFrame(animate)\r\n }\r\n\r\n init()\r\n animate()\r\n\r\n const handleResize = () => {\r\n if (typeof window === \"undefined\") return\r\n\r\n canvas.width = window.innerWidth\r\n canvas.height = window.innerHeight\r\n setDimensions({\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n })\r\n init()\r\n }\r\n\r\n window.addEventListener(\"resize\", handleResize)\r\n\r\n return () => {\r\n window.removeEventListener(\"resize\", handleResize)\r\n cancelAnimationFrame(animationFrameId)\r\n }\r\n }, [maxSize, minSize, particleColor, particleDensity, mousePosition.x, mousePosition.y])\r\n\r\n return (\r\n \r\n )\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/floating-paper.tsx", - "content": "\"use client\"\n\nimport { useEffect, useState } from \"react\"\nimport { motion } from \"framer-motion\"\nimport { FileText } from \"lucide-react\"\n\nexport function FloatingPaper({ count = 5 }) {\n const [dimensions, setDimensions] = useState({ width: 1200, height: 800 })\n\n useEffect(() => {\n // Update dimensions only on client side\n setDimensions({\n width: window.innerWidth,\n height: window.innerHeight,\n })\n\n const handleResize = () => {\n setDimensions({\n width: window.innerWidth,\n height: window.innerHeight,\n })\n }\n\n window.addEventListener(\"resize\", handleResize)\n return () => window.removeEventListener(\"resize\", handleResize)\n }, [])\n\n return (\n
\n {Array.from({ length: count }).map((_, i) => (\n \n
\n \n
\n \n ))}\n
\n )\n}\n", + "content": "\"use client\"\r\n\r\nimport { useEffect, useState } from \"react\"\r\nimport { motion } from \"framer-motion\"\r\nimport { FileText } from \"lucide-react\"\r\n\r\nexport function FloatingPaper({ count = 5 }) {\r\n const [dimensions, setDimensions] = useState({ width: 1200, height: 800 })\r\n\r\n useEffect(() => {\r\n // Update dimensions only on client side\r\n setDimensions({\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n })\r\n\r\n const handleResize = () => {\r\n setDimensions({\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n })\r\n }\r\n\r\n window.addEventListener(\"resize\", handleResize)\r\n return () => window.removeEventListener(\"resize\", handleResize)\r\n }, [])\r\n\r\n return (\r\n
\r\n {Array.from({ length: count }).map((_, i) => (\r\n \r\n
\r\n \r\n
\r\n \r\n ))}\r\n
\r\n )\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/robo-animation.tsx", - "content": "\"use client\"\n\nimport { motion } from \"framer-motion\"\nimport { Bot } from \"lucide-react\"\n\nexport function RoboAnimation() {\n return (\n
\n \n
\n \n \n
\n \n
\n )\n}\n", + "content": "\"use client\"\r\n\r\nimport { motion } from \"framer-motion\"\r\nimport { Bot } from \"lucide-react\"\r\n\r\nexport function RoboAnimation() {\r\n return (\r\n
\r\n \r\n
\r\n \r\n \r\n
\r\n \r\n
\r\n )\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/button.tsx", - "content": "import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline:\n \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n)\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes,\n VariantProps {\n asChild?: boolean\n}\n\nconst Button = React.forwardRef(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n return (\n \n )\n },\n)\nButton.displayName = \"Button\"\n\nexport { Button, buttonVariants }\n", + "content": "import * as React from \"react\"\r\nimport { Slot } from \"@radix-ui/react-slot\"\r\nimport { cva, type VariantProps } from \"class-variance-authority\"\r\n\r\nimport { cn } from \"@/lib/utils\"\r\n\r\nconst buttonVariants = cva(\r\n \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\r\n {\r\n variants: {\r\n variant: {\r\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\r\n destructive:\r\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\r\n outline:\r\n \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\r\n secondary:\r\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\r\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\r\n link: \"text-primary underline-offset-4 hover:underline\",\r\n },\r\n size: {\r\n default: \"h-10 px-4 py-2\",\r\n sm: \"h-9 rounded-md px-3\",\r\n lg: \"h-11 rounded-md px-8\",\r\n icon: \"h-10 w-10\",\r\n },\r\n },\r\n defaultVariants: {\r\n variant: \"default\",\r\n size: \"default\",\r\n },\r\n },\r\n)\r\n\r\nexport interface ButtonProps\r\n extends React.ButtonHTMLAttributes,\r\n VariantProps {\r\n asChild?: boolean\r\n}\r\n\r\nconst Button = React.forwardRef(\r\n ({ className, variant, size, asChild = false, ...props }, ref) => {\r\n const Comp = asChild ? Slot : \"button\"\r\n return (\r\n \r\n )\r\n },\r\n)\r\nButton.displayName = \"Button\"\r\n\r\nexport { Button, buttonVariants }\r\n", "type": "registry:component" } ] diff --git a/public/r/retro-cursor.json b/public/r/retro-cursor.json index c99ada4..77b357d 100644 --- a/public/r/retro-cursor.json +++ b/public/r/retro-cursor.json @@ -10,12 +10,12 @@ "files": [ { "path": "./src/components/nurui/retro-cursor-demo.tsx", - "content": "\"use client\";\nimport { RetroCursor } from \"@/components/nurui/retro-cursor\";\nimport { useMousePosition } from \"@/utils/use-mouse-position\";\n\nconst RetroCursorDemo = () => {\n const mousePosition = useMousePosition();\n return (\n <>\n

\n Move cursor to see the effect.\n

\n {\" \"}\n \n );\n};\n\nexport default RetroCursorDemo;\n", + "content": "\"use client\";\r\nimport { RetroCursor } from \"@/components/nurui/retro-cursor\";\r\nimport { useMousePosition } from \"@/utils/use-mouse-position\";\r\n\r\nconst RetroCursorDemo = () => {\r\n const mousePosition = useMousePosition();\r\n return (\r\n <>\r\n

\r\n Move cursor to see the effect.\r\n

\r\n {\" \"}\r\n \r\n );\r\n};\r\n\r\nexport default RetroCursorDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/retro-cursor.tsx", - "content": "\"use client\";\nimport { useEffect, useState } from \"react\";\nimport { motion } from \"framer-motion\";\n\ninterface RetroCursorProps {\n position: { x: number; y: number };\n}\n\nexport function RetroCursor({ position }: RetroCursorProps) {\n const [trail, setTrail] = useState<\n Array<{ x: number; y: number; id: number }>\n >([]);\n const [isActive, setIsActive] = useState(false);\n\n useEffect(() => {\n if (position.x === 0 && position.y === 0) return;\n\n // Only start tracking after the mouse has moved\n if (!isActive && (position.x !== 0 || position.y !== 0)) {\n setIsActive(true);\n }\n\n if (isActive) {\n setTrail((prevTrail) => {\n const newTrail = [\n { x: position.x, y: position.y, id: Date.now() },\n ...prevTrail.slice(0, 5), // Keep only 6 trail positions\n ];\n return newTrail;\n });\n }\n }, [position, isActive]);\n\n // Hide real cursor\n useEffect(() => {\n document.body.style.cursor = \"none\";\n return () => {\n document.body.style.cursor = \"auto\";\n };\n }, []);\n\n // Return if mouse hasn't moved yet\n if (!isActive) return null;\n\n return (\n
\n {/* Trail effects */}\n {trail.map((pos, index) => (\n \n ))}\n\n {/* Main cursor */}\n \n
\n );\n}", + "content": "\"use client\";\r\nimport { useEffect, useState } from \"react\";\r\nimport { motion } from \"framer-motion\";\r\n\r\ninterface RetroCursorProps {\r\n position: { x: number; y: number };\r\n}\r\n\r\nexport function RetroCursor({ position }: RetroCursorProps) {\r\n const [trail, setTrail] = useState<\r\n Array<{ x: number; y: number; id: number }>\r\n >([]);\r\n const [isActive, setIsActive] = useState(false);\r\n\r\n useEffect(() => {\r\n if (position.x === 0 && position.y === 0) return;\r\n\r\n // Only start tracking after the mouse has moved\r\n if (!isActive && (position.x !== 0 || position.y !== 0)) {\r\n setIsActive(true);\r\n }\r\n\r\n if (isActive) {\r\n setTrail((prevTrail) => {\r\n const newTrail = [\r\n { x: position.x, y: position.y, id: Date.now() },\r\n ...prevTrail.slice(0, 5), // Keep only 6 trail positions\r\n ];\r\n return newTrail;\r\n });\r\n }\r\n }, [position, isActive]);\r\n\r\n // Hide real cursor\r\n useEffect(() => {\r\n document.body.style.cursor = \"none\";\r\n return () => {\r\n document.body.style.cursor = \"auto\";\r\n };\r\n }, []);\r\n\r\n // Return if mouse hasn't moved yet\r\n if (!isActive) return null;\r\n\r\n return (\r\n
\r\n {/* Trail effects */}\r\n {trail.map((pos, index) => (\r\n \r\n ))}\r\n\r\n {/* Main cursor */}\r\n \r\n
\r\n );\r\n}", "type": "registry:component" } ] diff --git a/public/r/shadow-button.json b/public/r/shadow-button.json index 2391cea..bb72a01 100644 --- a/public/r/shadow-button.json +++ b/public/r/shadow-button.json @@ -12,12 +12,12 @@ "files": [ { "path": "./src/components/nurui/shadow-button-demo.tsx", - "content": "import React from \"react\";\nimport HoverShadowButton from \"@/components/nurui/shadow-button\";\n\nconst BorderAnimationButtonDemo = () => {\n return (\n
\n \n
\n );\n};\n\nexport default BorderAnimationButtonDemo;\n", + "content": "import React from \"react\";\r\nimport HoverShadowButton from \"@/components/nurui/shadow-button\";\r\n\r\nconst BorderAnimationButtonDemo = () => {\r\n return (\r\n
\r\n \r\n
\r\n );\r\n};\r\n\r\nexport default BorderAnimationButtonDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/shadow-button.tsx", - "content": "import React from \"react\";\nimport { MdDone } from \"react-icons/md\";\n\nconst HoverShadowButton = ({ text }: { text: string }) => {\n return (\n \n );\n};\n\nexport default HoverShadowButton;\n", + "content": "import React from \"react\";\r\nimport { MdDone } from \"react-icons/md\";\r\n\r\nconst HoverShadowButton = ({ text }: { text: string }) => {\r\n return (\r\n \r\n );\r\n};\r\n\r\nexport default HoverShadowButton;\r\n", "type": "registry:component" } ] diff --git a/public/r/shiny-card.json b/public/r/shiny-card.json index 9f9eda1..7710f93 100644 --- a/public/r/shiny-card.json +++ b/public/r/shiny-card.json @@ -10,17 +10,17 @@ "files": [ { "path": "./src/components/nurui/shiny-card-demo.tsx", - "content": "import React from \"react\";\nimport { FaRocket } from \"react-icons/fa6\";\nimport ShinyCard from \"@/components/nurui/shiny-card\";\n\nexport default function ShinyCardDemo() {\n return (\n
\n {data.map((feature, index) => (\n \n ))}\n
\n );\n}\n\nconst data = [\n {\n featureName: \"Fast Performance\",\n featureItems: [\n \"Optimized for speed\",\n \"Low latency\",\n \"Lightweight and efficient\",\n ],\n icon: ,\n },\n];\n", + "content": "import React from \"react\";\r\nimport { FaRocket } from \"react-icons/fa6\";\r\nimport ShinyCard from \"@/components/nurui/shiny-card\";\r\n\r\nexport default function ShinyCardDemo() {\r\n return (\r\n
\r\n {data.map((feature, index) => (\r\n \r\n ))}\r\n
\r\n );\r\n}\r\n\r\nconst data = [\r\n {\r\n featureName: \"Fast Performance\",\r\n featureItems: [\r\n \"Optimized for speed\",\r\n \"Low latency\",\r\n \"Lightweight and efficient\",\r\n ],\r\n icon: ,\r\n },\r\n];\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/shiny-card.tsx", - "content": "import { ReactNode } from \"react\";\nimport { MdDone } from \"react-icons/md\";\nimport \"./styles/shiny-card.css\";\n\ninterface ShinyCardProps {\n featureName: string;\n featureItems: string[];\n icon: ReactNode | string;\n}\n\nconst ShinyCard: React.FC = ({\n featureName,\n featureItems,\n icon,\n}) => {\n return (\n
\n

{icon}

\n

{featureName}

\n
    \n {featureItems.map((item, index) => (\n \n \n \n \n {item} \n \n ))}\n
\n
\n );\n};\n\nexport default ShinyCard;\n", + "content": "import { ReactNode } from \"react\";\r\nimport { MdDone } from \"react-icons/md\";\r\nimport \"./styles/shiny-card.css\";\r\n\r\ninterface ShinyCardProps {\r\n featureName: string;\r\n featureItems: string[];\r\n icon: ReactNode | string;\r\n}\r\n\r\nconst ShinyCard: React.FC = ({\r\n featureName,\r\n featureItems,\r\n icon,\r\n}) => {\r\n return (\r\n
\r\n

{icon}

\r\n

{featureName}

\r\n
    \r\n {featureItems.map((item, index) => (\r\n \r\n \r\n \r\n \r\n {item} \r\n \r\n ))}\r\n
\r\n
\r\n );\r\n};\r\n\r\nexport default ShinyCard;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/styles/shiny-card.css", - "content": ".shiny-card {\n --white: hsl(0, 0%, 100%);\n --black: hsl(240, 15%, 9%);\n --paragraph: hsl(0, 0%, 83%);\n --line: hsl(240, 9%, 17%);\n --primary: var(--primary-color);\n gap: 1rem;\n padding: 1rem;\n background-color: hsla(240, 15%, 9%, 1);\n background-image:\n radial-gradient(at 88% 40%, hsla(240, 15%, 9%, 1) 0px, transparent 85%),\n radial-gradient(at 49% 30%, hsla(240, 15%, 9%, 1) 0px, transparent 85%),\n radial-gradient(at 14% 26%, hsla(240, 15%, 9%, 1) 0px, transparent 85%),\n radial-gradient(at 0% 64%, hsl(189, 99%, 26%) 0px, transparent 85%),\n radial-gradient(at 41% 94%, hsl(189, 97%, 36%) 0px, transparent 85%),\n radial-gradient(at 100% 99%, hsl(188, 94%, 13%) 0px, transparent 85%);\n\n border-radius: 1rem;\n box-shadow: 0px -16px 24px 0px rgba(255, 255, 255, 0.25) inset;\n\n border: 2px solid var(--primary);\n animation: border-color-change 3s linear infinite;\n}\n\n@keyframes border-color-change {\n 0% {\n border-color: var(--primary);\n }\n\n 50% {\n border-color: var(--line);\n }\n\n 100% {\n border-color: var(--primary);\n }\n}\n", + "content": ".shiny-card {\r\n --white: hsl(0, 0%, 100%);\r\n --black: hsl(240, 15%, 9%);\r\n --paragraph: hsl(0, 0%, 83%);\r\n --line: hsl(240, 9%, 17%);\r\n --primary: var(--primary-color);\r\n gap: 1rem;\r\n padding: 1rem;\r\n background-color: hsla(240, 15%, 9%, 1);\r\n background-image:\r\n radial-gradient(at 88% 40%, hsla(240, 15%, 9%, 1) 0px, transparent 85%),\r\n radial-gradient(at 49% 30%, hsla(240, 15%, 9%, 1) 0px, transparent 85%),\r\n radial-gradient(at 14% 26%, hsla(240, 15%, 9%, 1) 0px, transparent 85%),\r\n radial-gradient(at 0% 64%, hsl(189, 99%, 26%) 0px, transparent 85%),\r\n radial-gradient(at 41% 94%, hsl(189, 97%, 36%) 0px, transparent 85%),\r\n radial-gradient(at 100% 99%, hsl(188, 94%, 13%) 0px, transparent 85%);\r\n\r\n border-radius: 1rem;\r\n box-shadow: 0px -16px 24px 0px rgba(255, 255, 255, 0.25) inset;\r\n\r\n border: 2px solid var(--primary);\r\n animation: border-color-change 3s linear infinite;\r\n}\r\n\r\n@keyframes border-color-change {\r\n 0% {\r\n border-color: var(--primary);\r\n }\r\n\r\n 50% {\r\n border-color: var(--line);\r\n }\r\n\r\n 100% {\r\n border-color: var(--primary);\r\n }\r\n}\r\n", "type": "registry:component" } ] diff --git a/public/r/singin-form.json b/public/r/singin-form.json index 0c8cde8..e7f496e 100644 --- a/public/r/singin-form.json +++ b/public/r/singin-form.json @@ -13,12 +13,12 @@ "files": [ { "path": "./src/components/nurui/singin-form-demo.tsx", - "content": "import { SingInForm } from \"@/components/nurui/singin-form\";\n\nconst SingInFormDemo = () => {\n return (\n
\n \n
\n );\n};\n\nexport { SingInFormDemo };\n", + "content": "import { SingInForm } from \"@/components/nurui/singin-form\";\r\n\r\nconst SingInFormDemo = () => {\r\n return (\r\n
\r\n \r\n
\r\n );\r\n};\r\n\r\nexport { SingInFormDemo };\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/singin-form.tsx", - "content": "\"use client\";\nimport React, { useState } from \"react\";\nimport Link from \"next/link\";\nimport {\n motion,\n AnimatePresence,\n useMotionValue,\n useTransform,\n} from \"framer-motion\";\nimport { Mail, Lock, Eye, EyeClosed, ArrowRight } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n return (\n \n );\n}\n\nexport function SingInForm() {\n const [showPassword, setShowPassword] = useState(false);\n const [email, setEmail] = useState(\"\");\n const [password, setPassword] = useState(\"\");\n const [isLoading, setIsLoading] = useState(false);\n const [focusedInput, setFocusedInput] = useState<\"email\" | \"password\" | null>(\n null,\n );\n const [rememberMe, setRememberMe] = useState(false);\n const [, setMousePosition] = useState({ x: 0, y: 0 });\n\n // For 3D card effect - increased rotation range for more pronounced 3D effect\n const mouseX = useMotionValue(0);\n const mouseY = useMotionValue(0);\n const rotateX = useTransform(mouseY, [-300, 300], [10, -10]); // Increased from 5/-5 to 10/-10\n const rotateY = useTransform(mouseX, [-300, 300], [-10, 10]); // Increased from -5/5 to -10/10\n\n const handleMouseMove = (e: React.MouseEvent) => {\n const rect = e.currentTarget.getBoundingClientRect();\n mouseX.set(e.clientX - rect.left - rect.width / 2);\n mouseY.set(e.clientY - rect.top - rect.height / 2);\n setMousePosition({ x: e.clientX, y: e.clientY });\n };\n\n const handleMouseLeave = () => {\n mouseX.set(0);\n mouseY.set(0);\n };\n\n return (\n
\n {/* Background gradient effect - matches the purple OnlyPipe style */}\n
\n\n {/* Subtle noise texture overlay */}\n \n\n {/* Top radial glow */}\n
\n \n \n\n {/* Animated glow spots */}\n
\n
\n\n \n \n
\n {/* Card glow effect - reduced intensity */}\n \n\n {/* Traveling light beam effect - reduced opacity */}\n
\n {/* Top light beam - enhanced glow */}\n \n\n {/* Right light beam - enhanced glow */}\n \n\n {/* Bottom light beam - enhanced glow */}\n \n\n {/* Left light beam - enhanced glow */}\n \n\n {/* Subtle corner glow spots - reduced opacity */}\n \n \n \n \n
\n\n {/* Card border glow - reduced opacity */}\n
\n\n {/* Glass card background */}\n
\n {/* Subtle card inner patterns */}\n \n\n {/* Logo and header */}\n
\n \n {/* Logo placeholder - would be an SVG in practice */}\n {/* */}\n \n S\n \n\n {/* Inner lighting effect */}\n
\n \n\n \n Welcome Back\n \n\n \n Sign in to continue to StyleMe\n \n
\n\n {/* Login form */}\n {\n e.preventDefault();\n setIsLoading(true);\n setTimeout(() => setIsLoading(false), 2000);\n }}\n className=\"space-y-4\"\n >\n \n {/* Email input */}\n \n
\n\n
\n \n\n setEmail(e.target.value)}\n onFocus={() => setFocusedInput(\"email\")}\n onBlur={() => setFocusedInput(null)}\n className=\"w-full bg-white/5 border-transparent focus:border-white/20 text-white placeholder:text-white/30 h-10 transition-all duration-300 pl-10 pr-3 focus:bg-white/10\"\n />\n\n {/* Input highlight effect */}\n {focusedInput === \"email\" && (\n \n )}\n
\n \n\n {/* Password input */}\n \n
\n\n
\n \n\n setPassword(e.target.value)}\n onFocus={() => setFocusedInput(\"password\")}\n onBlur={() => setFocusedInput(null)}\n className=\"w-full bg-white/5 border-transparent focus:border-white/20 text-white placeholder:text-white/30 h-10 transition-all duration-300 pl-10 pr-10 focus:bg-white/10\"\n />\n\n {/* Toggle password visibility */}\n setShowPassword(!showPassword)}\n className=\"absolute right-3 cursor-pointer\"\n >\n {showPassword ? (\n \n ) : (\n \n )}\n
\n\n {/* Input highlight effect */}\n {focusedInput === \"password\" && (\n \n )}\n
\n \n \n\n {/* Remember me & Forgot password */}\n
\n
\n
\n setRememberMe(!rememberMe)}\n className=\"appearance-none h-4 w-4 rounded border border-white/20 bg-white/5 checked:bg-white checked:border-white focus:outline-none focus:ring-1 focus:ring-white/30 transition-all duration-200\"\n />\n {rememberMe && (\n \n {/* */}\n \n \n \n \n )}\n
\n \n Remember me\n \n
\n\n
\n \n Forgot password?\n \n
\n
\n\n {/* Sign in button */}\n \n {/* Button glow effect - reduced intensity */}\n
\n\n
\n {/* Button background animation */}\n \n\n \n {isLoading ? (\n \n
\n \n ) : (\n \n Sign In\n \n \n )}\n \n
\n \n\n {/* Minimal Divider */}\n
\n
\n \n or\n \n
\n
\n\n {/* Google Sign In */}\n \n
\n\n
\n {/* */}\n
\n G\n
\n\n \n Sign in with Google\n \n\n {/* Button hover effect */}\n \n
\n \n\n {/* Sign up link */}\n \n Don't have an account?{\" \"}\n \n \n Sign up\n \n \n \n \n \n
\n
\n \n \n
\n );\n}\n", + "content": "\"use client\";\r\nimport React, { useState } from \"react\";\r\nimport Link from \"next/link\";\r\nimport {\r\n motion,\r\n AnimatePresence,\r\n useMotionValue,\r\n useTransform,\r\n} from \"framer-motion\";\r\nimport { Mail, Lock, Eye, EyeClosed, ArrowRight } from \"lucide-react\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\r\n return (\r\n \r\n );\r\n}\r\n\r\nexport function SingInForm() {\r\n const [showPassword, setShowPassword] = useState(false);\r\n const [email, setEmail] = useState(\"\");\r\n const [password, setPassword] = useState(\"\");\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [focusedInput, setFocusedInput] = useState<\"email\" | \"password\" | null>(\r\n null,\r\n );\r\n const [rememberMe, setRememberMe] = useState(false);\r\n const [, setMousePosition] = useState({ x: 0, y: 0 });\r\n\r\n // For 3D card effect - increased rotation range for more pronounced 3D effect\r\n const mouseX = useMotionValue(0);\r\n const mouseY = useMotionValue(0);\r\n const rotateX = useTransform(mouseY, [-300, 300], [10, -10]); // Increased from 5/-5 to 10/-10\r\n const rotateY = useTransform(mouseX, [-300, 300], [-10, 10]); // Increased from -5/5 to -10/10\r\n\r\n const handleMouseMove = (e: React.MouseEvent) => {\r\n const rect = e.currentTarget.getBoundingClientRect();\r\n mouseX.set(e.clientX - rect.left - rect.width / 2);\r\n mouseY.set(e.clientY - rect.top - rect.height / 2);\r\n setMousePosition({ x: e.clientX, y: e.clientY });\r\n };\r\n\r\n const handleMouseLeave = () => {\r\n mouseX.set(0);\r\n mouseY.set(0);\r\n };\r\n\r\n return (\r\n
\r\n {/* Background gradient effect - matches the purple OnlyPipe style */}\r\n
\r\n\r\n {/* Subtle noise texture overlay */}\r\n \r\n\r\n {/* Top radial glow */}\r\n
\r\n \r\n \r\n\r\n {/* Animated glow spots */}\r\n
\r\n
\r\n\r\n \r\n \r\n
\r\n {/* Card glow effect - reduced intensity */}\r\n \r\n\r\n {/* Traveling light beam effect - reduced opacity */}\r\n
\r\n {/* Top light beam - enhanced glow */}\r\n \r\n\r\n {/* Right light beam - enhanced glow */}\r\n \r\n\r\n {/* Bottom light beam - enhanced glow */}\r\n \r\n\r\n {/* Left light beam - enhanced glow */}\r\n \r\n\r\n {/* Subtle corner glow spots - reduced opacity */}\r\n \r\n \r\n \r\n \r\n
\r\n\r\n {/* Card border glow - reduced opacity */}\r\n
\r\n\r\n {/* Glass card background */}\r\n
\r\n {/* Subtle card inner patterns */}\r\n \r\n\r\n {/* Logo and header */}\r\n
\r\n \r\n {/* Logo placeholder - would be an SVG in practice */}\r\n {/* */}\r\n \r\n S\r\n \r\n\r\n {/* Inner lighting effect */}\r\n
\r\n \r\n\r\n \r\n Welcome Back\r\n \r\n\r\n \r\n Sign in to continue to StyleMe\r\n \r\n
\r\n\r\n {/* Login form */}\r\n {\r\n e.preventDefault();\r\n setIsLoading(true);\r\n setTimeout(() => setIsLoading(false), 2000);\r\n }}\r\n className=\"space-y-4\"\r\n >\r\n \r\n {/* Email input */}\r\n \r\n
\r\n\r\n
\r\n \r\n\r\n setEmail(e.target.value)}\r\n onFocus={() => setFocusedInput(\"email\")}\r\n onBlur={() => setFocusedInput(null)}\r\n className=\"w-full bg-white/5 border-transparent focus:border-white/20 text-white placeholder:text-white/30 h-10 transition-all duration-300 pl-10 pr-3 focus:bg-white/10\"\r\n />\r\n\r\n {/* Input highlight effect */}\r\n {focusedInput === \"email\" && (\r\n \r\n )}\r\n
\r\n \r\n\r\n {/* Password input */}\r\n \r\n
\r\n\r\n
\r\n \r\n\r\n setPassword(e.target.value)}\r\n onFocus={() => setFocusedInput(\"password\")}\r\n onBlur={() => setFocusedInput(null)}\r\n className=\"w-full bg-white/5 border-transparent focus:border-white/20 text-white placeholder:text-white/30 h-10 transition-all duration-300 pl-10 pr-10 focus:bg-white/10\"\r\n />\r\n\r\n {/* Toggle password visibility */}\r\n setShowPassword(!showPassword)}\r\n className=\"absolute right-3 cursor-pointer\"\r\n >\r\n {showPassword ? (\r\n \r\n ) : (\r\n \r\n )}\r\n
\r\n\r\n {/* Input highlight effect */}\r\n {focusedInput === \"password\" && (\r\n \r\n )}\r\n
\r\n \r\n \r\n\r\n {/* Remember me & Forgot password */}\r\n
\r\n
\r\n
\r\n setRememberMe(!rememberMe)}\r\n className=\"appearance-none h-4 w-4 rounded border border-white/20 bg-white/5 checked:bg-white checked:border-white focus:outline-none focus:ring-1 focus:ring-white/30 transition-all duration-200\"\r\n />\r\n {rememberMe && (\r\n \r\n {/* */}\r\n \r\n \r\n \r\n \r\n )}\r\n
\r\n \r\n Remember me\r\n \r\n
\r\n\r\n
\r\n \r\n Forgot password?\r\n \r\n
\r\n
\r\n\r\n {/* Sign in button */}\r\n \r\n {/* Button glow effect - reduced intensity */}\r\n
\r\n\r\n
\r\n {/* Button background animation */}\r\n \r\n\r\n \r\n {isLoading ? (\r\n \r\n
\r\n \r\n ) : (\r\n \r\n Sign In\r\n \r\n \r\n )}\r\n \r\n
\r\n \r\n\r\n {/* Minimal Divider */}\r\n
\r\n
\r\n \r\n or\r\n \r\n
\r\n
\r\n\r\n {/* Google Sign In */}\r\n \r\n
\r\n\r\n
\r\n {/* */}\r\n
\r\n G\r\n
\r\n\r\n \r\n Sign in with Google\r\n \r\n\r\n {/* Button hover effect */}\r\n \r\n
\r\n \r\n\r\n {/* Sign up link */}\r\n \r\n Don't have an account?{\" \"}\r\n \r\n \r\n Sign up\r\n \r\n \r\n \r\n \r\n \r\n
\r\n
\r\n \r\n \r\n
\r\n );\r\n}\r\n", "type": "registry:component" } ] diff --git a/public/r/splash-cursor.json b/public/r/splash-cursor.json index 6614604..42b7f09 100644 --- a/public/r/splash-cursor.json +++ b/public/r/splash-cursor.json @@ -8,12 +8,12 @@ "files": [ { "path": "./src/components/nurui/splash-cursor-demo.tsx", - "content": "import React from \"react\";\nimport SplashCursor from \"@/components/nurui/splash-cursor\";\n\nconst SplashCursorDemo = () => {\n return (\n <>\n

\n Move cursor to see the effect.\n

\n \n \n );\n};\n\nexport default SplashCursorDemo;\n", + "content": "import React from \"react\";\r\nimport SplashCursor from \"@/components/nurui/splash-cursor\";\r\n\r\nconst SplashCursorDemo = () => {\r\n return (\r\n <>\r\n

\r\n Move cursor to see the effect.\r\n

\r\n \r\n \r\n );\r\n};\r\n\r\nexport default SplashCursorDemo;\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/splash-cursor.tsx", - "content": "\"use client\";\nimport React, { useEffect, useRef } from \"react\";\n\ninterface ColorRGB {\n r: number;\n g: number;\n b: number;\n}\n\ninterface SplashCursorProps {\n SIM_RESOLUTION?: number;\n DYE_RESOLUTION?: number;\n CAPTURE_RESOLUTION?: number;\n DENSITY_DISSIPATION?: number;\n VELOCITY_DISSIPATION?: number;\n PRESSURE?: number;\n PRESSURE_ITERATIONS?: number;\n CURL?: number;\n SPLAT_RADIUS?: number;\n SPLAT_FORCE?: number;\n SHADING?: boolean;\n COLOR_UPDATE_SPEED?: number;\n BACK_COLOR?: ColorRGB;\n TRANSPARENT?: boolean;\n}\n\ninterface Pointer {\n id: number;\n texcoordX: number;\n texcoordY: number;\n prevTexcoordX: number;\n prevTexcoordY: number;\n deltaX: number;\n deltaY: number;\n down: boolean;\n moved: boolean;\n color: ColorRGB;\n}\n\nfunction pointerPrototype(): Pointer {\n return {\n id: -1,\n texcoordX: 0,\n texcoordY: 0,\n prevTexcoordX: 0,\n prevTexcoordY: 0,\n deltaX: 0,\n deltaY: 0,\n down: false,\n moved: false,\n color: { r: 0, g: 0, b: 0 },\n };\n}\n\nexport default function SplashCursor({\n SIM_RESOLUTION = 128,\n DYE_RESOLUTION = 1440,\n CAPTURE_RESOLUTION = 512,\n DENSITY_DISSIPATION = 3.5,\n VELOCITY_DISSIPATION = 2,\n PRESSURE = 0.1,\n PRESSURE_ITERATIONS = 20,\n CURL = 3,\n SPLAT_RADIUS = 0.2,\n SPLAT_FORCE = 6000,\n SHADING = true,\n COLOR_UPDATE_SPEED = 10,\n BACK_COLOR = { r: 0.5, g: 0, b: 0 },\n TRANSPARENT = true,\n}: SplashCursorProps) {\n const canvasRef = useRef(null);\n\n useEffect(() => {\n const canvas = canvasRef.current;\n if (!canvas) return; // Guard canvas early\n\n // Pointer and config setup\n const pointers: Pointer[] = [pointerPrototype()];\n\n // All these are guaranteed numbers due to destructuring defaults\n // So we cast them to remove TS warnings:\n const config = {\n SIM_RESOLUTION: SIM_RESOLUTION!,\n DYE_RESOLUTION: DYE_RESOLUTION!,\n CAPTURE_RESOLUTION: CAPTURE_RESOLUTION!,\n DENSITY_DISSIPATION: DENSITY_DISSIPATION!,\n VELOCITY_DISSIPATION: VELOCITY_DISSIPATION!,\n PRESSURE: PRESSURE!,\n PRESSURE_ITERATIONS: PRESSURE_ITERATIONS!,\n CURL: CURL!,\n SPLAT_RADIUS: SPLAT_RADIUS!,\n SPLAT_FORCE: SPLAT_FORCE!,\n SHADING,\n COLOR_UPDATE_SPEED: COLOR_UPDATE_SPEED!,\n PAUSED: false,\n BACK_COLOR,\n TRANSPARENT,\n };\n\n // Get WebGL context (WebGL1 or WebGL2)\n const { gl, ext } = getWebGLContext(canvas);\n if (!gl || !ext) return;\n\n // If no linear filtering, reduce resolution\n if (!ext.supportLinearFiltering) {\n config.DYE_RESOLUTION = 256;\n config.SHADING = false;\n }\n\n function getWebGLContext(canvas: HTMLCanvasElement) {\n const params = {\n alpha: true,\n depth: false,\n stencil: false,\n antialias: false,\n preserveDrawingBuffer: false,\n };\n\n let gl = canvas.getContext(\n \"webgl2\",\n params,\n ) as WebGL2RenderingContext | null;\n\n if (!gl) {\n gl = (canvas.getContext(\"webgl\", params) ||\n canvas.getContext(\n \"experimental-webgl\",\n params,\n )) as WebGL2RenderingContext | null;\n }\n\n if (!gl) {\n throw new Error(\"Unable to initialize WebGL.\");\n }\n\n const isWebGL2 = \"drawBuffers\" in gl;\n\n let supportLinearFiltering = false;\n let halfFloat: OES_texture_half_float | null = null;\n\n if (isWebGL2) {\n // For WebGL2\n (gl as WebGL2RenderingContext).getExtension(\"EXT_color_buffer_float\");\n supportLinearFiltering = !!(gl as WebGL2RenderingContext).getExtension(\n \"OES_texture_float_linear\",\n );\n } else {\n // For WebGL1\n halfFloat = gl.getExtension(\"OES_texture_half_float\");\n supportLinearFiltering = !!gl.getExtension(\n \"OES_texture_half_float_linear\",\n );\n }\n\n gl.clearColor(0, 0, 0, 1);\n\n const halfFloatTexType = isWebGL2\n ? (gl as WebGL2RenderingContext).HALF_FLOAT\n : halfFloat && \"HALF_FLOAT_OES\" in halfFloat\n ? (halfFloat as OES_texture_half_float).HALF_FLOAT_OES\n : 0;\n\n let formatRGBA: { internalFormat: number; format: number } | null;\n let formatRG: { internalFormat: number; format: number } | null;\n let formatR: { internalFormat: number; format: number } | null;\n\n if (isWebGL2) {\n formatRGBA = getSupportedFormat(\n gl,\n (gl as WebGL2RenderingContext).RGBA16F,\n gl.RGBA,\n halfFloatTexType,\n );\n formatRG = getSupportedFormat(\n gl,\n (gl as WebGL2RenderingContext).RG16F,\n (gl as WebGL2RenderingContext).RG,\n halfFloatTexType,\n );\n formatR = getSupportedFormat(\n gl,\n (gl as WebGL2RenderingContext).R16F,\n (gl as WebGL2RenderingContext).RED,\n halfFloatTexType,\n );\n } else {\n formatRGBA = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);\n formatRG = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);\n formatR = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);\n }\n\n return {\n gl,\n ext: {\n formatRGBA,\n formatRG,\n formatR,\n halfFloatTexType,\n supportLinearFiltering,\n },\n };\n }\n\n function getSupportedFormat(\n gl: WebGLRenderingContext | WebGL2RenderingContext,\n internalFormat: number,\n format: number,\n type: number,\n ): { internalFormat: number; format: number } | null {\n if (!supportRenderTextureFormat(gl, internalFormat, format, type)) {\n // For WebGL2 fallback:\n if (\"drawBuffers\" in gl) {\n const gl2 = gl as WebGL2RenderingContext;\n switch (internalFormat) {\n case gl2.R16F:\n return getSupportedFormat(gl2, gl2.RG16F, gl2.RG, type);\n case gl2.RG16F:\n return getSupportedFormat(gl2, gl2.RGBA16F, gl2.RGBA, type);\n default:\n return null;\n }\n }\n return null;\n }\n return { internalFormat, format };\n }\n\n function supportRenderTextureFormat(\n gl: WebGLRenderingContext | WebGL2RenderingContext,\n internalFormat: number,\n format: number,\n type: number,\n ) {\n const texture = gl.createTexture();\n if (!texture) return false;\n\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.texImage2D(\n gl.TEXTURE_2D,\n 0,\n internalFormat,\n 4,\n 4,\n 0,\n format,\n type,\n null,\n );\n\n const fbo = gl.createFramebuffer();\n if (!fbo) return false;\n\n gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);\n gl.framebufferTexture2D(\n gl.FRAMEBUFFER,\n gl.COLOR_ATTACHMENT0,\n gl.TEXTURE_2D,\n texture,\n 0,\n );\n const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);\n return status === gl.FRAMEBUFFER_COMPLETE;\n }\n\n function hashCode(s: string) {\n if (!s.length) return 0;\n let hash = 0;\n for (let i = 0; i < s.length; i++) {\n hash = (hash << 5) - hash + s.charCodeAt(i);\n hash |= 0;\n }\n return hash;\n }\n\n function addKeywords(source: string, keywords: string[] | null) {\n if (!keywords) return source;\n let keywordsString = \"\";\n for (const keyword of keywords) {\n keywordsString += `#define ${keyword}\\n`;\n }\n return keywordsString + source;\n }\n\n function compileShader(\n type: number,\n source: string,\n keywords: string[] | null = null,\n ): WebGLShader | null {\n const shaderSource = addKeywords(source, keywords);\n const shader = gl.createShader(type);\n if (!shader) return null;\n gl.shaderSource(shader, shaderSource);\n gl.compileShader(shader);\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n console.trace(gl.getShaderInfoLog(shader));\n }\n return shader;\n }\n\n function createProgram(\n vertexShader: WebGLShader | null,\n fragmentShader: WebGLShader | null,\n ): WebGLProgram | null {\n if (!vertexShader || !fragmentShader) return null;\n const program = gl.createProgram();\n if (!program) return null;\n gl.attachShader(program, vertexShader);\n gl.attachShader(program, fragmentShader);\n gl.linkProgram(program);\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n console.trace(gl.getProgramInfoLog(program));\n }\n return program;\n }\n\n function getUniforms(program: WebGLProgram) {\n const uniforms: Record = {};\n const uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);\n for (let i = 0; i < uniformCount; i++) {\n const uniformInfo = gl.getActiveUniform(program, i);\n if (uniformInfo) {\n uniforms[uniformInfo.name] = gl.getUniformLocation(\n program,\n uniformInfo.name,\n );\n }\n }\n return uniforms;\n }\n\n class Program {\n program: WebGLProgram | null;\n uniforms: Record;\n\n constructor(\n vertexShader: WebGLShader | null,\n fragmentShader: WebGLShader | null,\n ) {\n this.program = createProgram(vertexShader, fragmentShader);\n this.uniforms = this.program ? getUniforms(this.program) : {};\n }\n\n bind() {\n if (this.program) gl.useProgram(this.program);\n }\n }\n\n class Material {\n vertexShader: WebGLShader | null;\n fragmentShaderSource: string;\n programs: Record;\n activeProgram: WebGLProgram | null;\n uniforms: Record;\n\n constructor(\n vertexShader: WebGLShader | null,\n fragmentShaderSource: string,\n ) {\n this.vertexShader = vertexShader;\n this.fragmentShaderSource = fragmentShaderSource;\n this.programs = {};\n this.activeProgram = null;\n this.uniforms = {};\n }\n\n setKeywords(keywords: string[]) {\n let hash = 0;\n for (const kw of keywords) {\n hash += hashCode(kw);\n }\n let program = this.programs[hash];\n if (program == null) {\n const fragmentShader = compileShader(\n gl.FRAGMENT_SHADER,\n this.fragmentShaderSource,\n keywords,\n );\n program = createProgram(this.vertexShader, fragmentShader);\n this.programs[hash] = program;\n }\n if (program === this.activeProgram) return;\n if (program) {\n this.uniforms = getUniforms(program);\n }\n this.activeProgram = program;\n }\n\n bind() {\n if (this.activeProgram) {\n gl.useProgram(this.activeProgram);\n }\n }\n }\n\n // -------------------- Shaders --------------------\n const baseVertexShader = compileShader(\n gl.VERTEX_SHADER,\n `\n precision highp float;\n attribute vec2 aPosition;\n varying vec2 vUv;\n varying vec2 vL;\n varying vec2 vR;\n varying vec2 vT;\n varying vec2 vB;\n uniform vec2 texelSize;\n\n void main () {\n vUv = aPosition * 0.5 + 0.5;\n vL = vUv - vec2(texelSize.x, 0.0);\n vR = vUv + vec2(texelSize.x, 0.0);\n vT = vUv + vec2(0.0, texelSize.y);\n vB = vUv - vec2(0.0, texelSize.y);\n gl_Position = vec4(aPosition, 0.0, 1.0);\n }\n `,\n );\n\n const copyShader = compileShader(\n gl.FRAGMENT_SHADER,\n `\n precision mediump float;\n precision mediump sampler2D;\n varying highp vec2 vUv;\n uniform sampler2D uTexture;\n\n void main () {\n gl_FragColor = texture2D(uTexture, vUv);\n }\n `,\n );\n\n const clearShader = compileShader(\n gl.FRAGMENT_SHADER,\n `\n precision mediump float;\n precision mediump sampler2D;\n varying highp vec2 vUv;\n uniform sampler2D uTexture;\n uniform float value;\n\n void main () {\n gl_FragColor = value * texture2D(uTexture, vUv);\n }\n `,\n );\n\n const displayShaderSource = `\n precision highp float;\n precision highp sampler2D;\n varying vec2 vUv;\n varying vec2 vL;\n varying vec2 vR;\n varying vec2 vT;\n varying vec2 vB;\n uniform sampler2D uTexture;\n uniform sampler2D uDithering;\n uniform vec2 ditherScale;\n uniform vec2 texelSize;\n\n vec3 linearToGamma (vec3 color) {\n color = max(color, vec3(0));\n return max(1.055 * pow(color, vec3(0.416666667)) - 0.055, vec3(0));\n }\n\n void main () {\n vec3 c = texture2D(uTexture, vUv).rgb;\n #ifdef SHADING\n vec3 lc = texture2D(uTexture, vL).rgb;\n vec3 rc = texture2D(uTexture, vR).rgb;\n vec3 tc = texture2D(uTexture, vT).rgb;\n vec3 bc = texture2D(uTexture, vB).rgb;\n\n float dx = length(rc) - length(lc);\n float dy = length(tc) - length(bc);\n\n vec3 n = normalize(vec3(dx, dy, length(texelSize)));\n vec3 l = vec3(0.0, 0.0, 1.0);\n\n float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0);\n c *= diffuse;\n #endif\n\n float a = max(c.r, max(c.g, c.b));\n gl_FragColor = vec4(c, a);\n }\n `;\n\n const splatShader = compileShader(\n gl.FRAGMENT_SHADER,\n `\n precision highp float;\n precision highp sampler2D;\n varying vec2 vUv;\n uniform sampler2D uTarget;\n uniform float aspectRatio;\n uniform vec3 color;\n uniform vec2 point;\n uniform float radius;\n\n void main () {\n vec2 p = vUv - point.xy;\n p.x *= aspectRatio;\n vec3 splat = exp(-dot(p, p) / radius) * color;\n vec3 base = texture2D(uTarget, vUv).xyz;\n gl_FragColor = vec4(base + splat, 1.0);\n }\n `,\n );\n\n const advectionShader = compileShader(\n gl.FRAGMENT_SHADER,\n `\n precision highp float;\n precision highp sampler2D;\n varying vec2 vUv;\n uniform sampler2D uVelocity;\n uniform sampler2D uSource;\n uniform vec2 texelSize;\n uniform vec2 dyeTexelSize;\n uniform float dt;\n uniform float dissipation;\n\n vec4 bilerp (sampler2D sam, vec2 uv, vec2 tsize) {\n vec2 st = uv / tsize - 0.5;\n vec2 iuv = floor(st);\n vec2 fuv = fract(st);\n\n vec4 a = texture2D(sam, (iuv + vec2(0.5, 0.5)) * tsize);\n vec4 b = texture2D(sam, (iuv + vec2(1.5, 0.5)) * tsize);\n vec4 c = texture2D(sam, (iuv + vec2(0.5, 1.5)) * tsize);\n vec4 d = texture2D(sam, (iuv + vec2(1.5, 1.5)) * tsize);\n\n return mix(mix(a, b, fuv.x), mix(c, d, fuv.x), fuv.y);\n }\n\n void main () {\n #ifdef MANUAL_FILTERING\n vec2 coord = vUv - dt * bilerp(uVelocity, vUv, texelSize).xy * texelSize;\n vec4 result = bilerp(uSource, coord, dyeTexelSize);\n #else\n vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize;\n vec4 result = texture2D(uSource, coord);\n #endif\n float decay = 1.0 + dissipation * dt;\n gl_FragColor = result / decay;\n }\n `,\n ext.supportLinearFiltering ? null : [\"MANUAL_FILTERING\"],\n );\n\n const divergenceShader = compileShader(\n gl.FRAGMENT_SHADER,\n `\n precision mediump float;\n precision mediump sampler2D;\n varying highp vec2 vUv;\n varying highp vec2 vL;\n varying highp vec2 vR;\n varying highp vec2 vT;\n varying highp vec2 vB;\n uniform sampler2D uVelocity;\n\n void main () {\n float L = texture2D(uVelocity, vL).x;\n float R = texture2D(uVelocity, vR).x;\n float T = texture2D(uVelocity, vT).y;\n float B = texture2D(uVelocity, vB).y;\n\n vec2 C = texture2D(uVelocity, vUv).xy;\n if (vL.x < 0.0) { L = -C.x; }\n if (vR.x > 1.0) { R = -C.x; }\n if (vT.y > 1.0) { T = -C.y; }\n if (vB.y < 0.0) { B = -C.y; }\n\n float div = 0.5 * (R - L + T - B);\n gl_FragColor = vec4(div, 0.0, 0.0, 1.0);\n }\n `,\n );\n\n const curlShader = compileShader(\n gl.FRAGMENT_SHADER,\n `\n precision mediump float;\n precision mediump sampler2D;\n varying highp vec2 vUv;\n varying highp vec2 vL;\n varying highp vec2 vR;\n varying highp vec2 vT;\n varying highp vec2 vB;\n uniform sampler2D uVelocity;\n\n void main () {\n float L = texture2D(uVelocity, vL).y;\n float R = texture2D(uVelocity, vR).y;\n float T = texture2D(uVelocity, vT).x;\n float B = texture2D(uVelocity, vB).x;\n float vorticity = R - L - T + B;\n gl_FragColor = vec4(0.5 * vorticity, 0.0, 0.0, 1.0);\n }\n `,\n );\n\n const vorticityShader = compileShader(\n gl.FRAGMENT_SHADER,\n `\n precision highp float;\n precision highp sampler2D;\n varying vec2 vUv;\n varying vec2 vL;\n varying vec2 vR;\n varying vec2 vT;\n varying vec2 vB;\n uniform sampler2D uVelocity;\n uniform sampler2D uCurl;\n uniform float curl;\n uniform float dt;\n\n void main () {\n float L = texture2D(uCurl, vL).x;\n float R = texture2D(uCurl, vR).x;\n float T = texture2D(uCurl, vT).x;\n float B = texture2D(uCurl, vB).x;\n float C = texture2D(uCurl, vUv).x;\n\n vec2 force = 0.5 * vec2(abs(T) - abs(B), abs(R) - abs(L));\n force /= length(force) + 0.0001;\n force *= curl * C;\n force.y *= -1.0;\n\n vec2 velocity = texture2D(uVelocity, vUv).xy;\n velocity += force * dt;\n velocity = min(max(velocity, -1000.0), 1000.0);\n gl_FragColor = vec4(velocity, 0.0, 1.0);\n }\n `,\n );\n\n const pressureShader = compileShader(\n gl.FRAGMENT_SHADER,\n `\n precision mediump float;\n precision mediump sampler2D;\n varying highp vec2 vUv;\n varying highp vec2 vL;\n varying highp vec2 vR;\n varying highp vec2 vT;\n varying highp vec2 vB;\n uniform sampler2D uPressure;\n uniform sampler2D uDivergence;\n\n void main () {\n float L = texture2D(uPressure, vL).x;\n float R = texture2D(uPressure, vR).x;\n float T = texture2D(uPressure, vT).x;\n float B = texture2D(uPressure, vB).x;\n float C = texture2D(uPressure, vUv).x;\n float divergence = texture2D(uDivergence, vUv).x;\n float pressure = (L + R + B + T - divergence) * 0.25;\n gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0);\n }\n `,\n );\n\n const gradientSubtractShader = compileShader(\n gl.FRAGMENT_SHADER,\n `\n precision mediump float;\n precision mediump sampler2D;\n varying highp vec2 vUv;\n varying highp vec2 vL;\n varying highp vec2 vR;\n varying highp vec2 vT;\n varying highp vec2 vB;\n uniform sampler2D uPressure;\n uniform sampler2D uVelocity;\n\n void main () {\n float L = texture2D(uPressure, vL).x;\n float R = texture2D(uPressure, vR).x;\n float T = texture2D(uPressure, vT).x;\n float B = texture2D(uPressure, vB).x;\n vec2 velocity = texture2D(uVelocity, vUv).xy;\n velocity.xy -= vec2(R - L, T - B);\n gl_FragColor = vec4(velocity, 0.0, 1.0);\n }\n `,\n );\n\n // -------------------- Fullscreen Triangles --------------------\n const blit = (() => {\n const buffer = gl.createBuffer()!;\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n gl.bufferData(\n gl.ARRAY_BUFFER,\n new Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]),\n gl.STATIC_DRAW,\n );\n const elemBuffer = gl.createBuffer()!;\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elemBuffer);\n gl.bufferData(\n gl.ELEMENT_ARRAY_BUFFER,\n new Uint16Array([0, 1, 2, 0, 2, 3]),\n gl.STATIC_DRAW,\n );\n gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);\n gl.enableVertexAttribArray(0);\n\n return (target: FBO | null, doClear = false) => {\n if (!gl) return;\n if (!target) {\n gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\n } else {\n gl.viewport(0, 0, target.width, target.height);\n gl.bindFramebuffer(gl.FRAMEBUFFER, target.fbo);\n }\n if (doClear) {\n gl.clearColor(0, 0, 0, 1);\n gl.clear(gl.COLOR_BUFFER_BIT);\n }\n gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);\n };\n })();\n\n // Types for Framebuffers\n interface FBO {\n texture: WebGLTexture;\n fbo: WebGLFramebuffer;\n width: number;\n height: number;\n texelSizeX: number;\n texelSizeY: number;\n attach: (id: number) => number;\n }\n\n interface DoubleFBO {\n width: number;\n height: number;\n texelSizeX: number;\n texelSizeY: number;\n read: FBO;\n write: FBO;\n swap: () => void;\n }\n\n // FBO variables\n let dye: DoubleFBO;\n let velocity: DoubleFBO;\n let divergence: FBO;\n let curl: FBO;\n let pressure: DoubleFBO;\n\n // WebGL Programs\n const copyProgram = new Program(baseVertexShader, copyShader);\n const clearProgram = new Program(baseVertexShader, clearShader);\n const splatProgram = new Program(baseVertexShader, splatShader);\n const advectionProgram = new Program(baseVertexShader, advectionShader);\n const divergenceProgram = new Program(baseVertexShader, divergenceShader);\n const curlProgram = new Program(baseVertexShader, curlShader);\n const vorticityProgram = new Program(baseVertexShader, vorticityShader);\n const pressureProgram = new Program(baseVertexShader, pressureShader);\n const gradienSubtractProgram = new Program(\n baseVertexShader,\n gradientSubtractShader,\n );\n const displayMaterial = new Material(baseVertexShader, displayShaderSource);\n\n // -------------------- FBO creation --------------------\n function createFBO(\n w: number,\n h: number,\n internalFormat: number,\n format: number,\n type: number,\n param: number,\n ): FBO {\n gl.activeTexture(gl.TEXTURE0);\n const texture = gl.createTexture()!;\n gl.bindTexture(gl.TEXTURE_2D, texture);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, param);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, param);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n gl.texImage2D(\n gl.TEXTURE_2D,\n 0,\n internalFormat,\n w,\n h,\n 0,\n format,\n type,\n null,\n );\n const fbo = gl.createFramebuffer()!;\n gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);\n gl.framebufferTexture2D(\n gl.FRAMEBUFFER,\n gl.COLOR_ATTACHMENT0,\n gl.TEXTURE_2D,\n texture,\n 0,\n );\n gl.viewport(0, 0, w, h);\n gl.clear(gl.COLOR_BUFFER_BIT);\n\n const texelSizeX = 1 / w;\n const texelSizeY = 1 / h;\n\n return {\n texture,\n fbo,\n width: w,\n height: h,\n texelSizeX,\n texelSizeY,\n attach(id: number) {\n gl.activeTexture(gl.TEXTURE0 + id);\n gl.bindTexture(gl.TEXTURE_2D, texture);\n return id;\n },\n };\n }\n\n function createDoubleFBO(\n w: number,\n h: number,\n internalFormat: number,\n format: number,\n type: number,\n param: number,\n ): DoubleFBO {\n const fbo1 = createFBO(w, h, internalFormat, format, type, param);\n const fbo2 = createFBO(w, h, internalFormat, format, type, param);\n return {\n width: w,\n height: h,\n texelSizeX: fbo1.texelSizeX,\n texelSizeY: fbo1.texelSizeY,\n read: fbo1,\n write: fbo2,\n swap() {\n const tmp = this.read;\n this.read = this.write;\n this.write = tmp;\n },\n };\n }\n\n function resizeFBO(\n target: FBO,\n w: number,\n h: number,\n internalFormat: number,\n format: number,\n type: number,\n param: number,\n ) {\n const newFBO = createFBO(w, h, internalFormat, format, type, param);\n copyProgram.bind();\n if (copyProgram.uniforms.uTexture)\n gl.uniform1i(copyProgram.uniforms.uTexture, target.attach(0));\n blit(newFBO, false);\n return newFBO;\n }\n\n function resizeDoubleFBO(\n target: DoubleFBO,\n w: number,\n h: number,\n internalFormat: number,\n format: number,\n type: number,\n param: number,\n ) {\n if (target.width === w && target.height === h) return target;\n target.read = resizeFBO(\n target.read,\n w,\n h,\n internalFormat,\n format,\n type,\n param,\n );\n target.write = createFBO(w, h, internalFormat, format, type, param);\n target.width = w;\n target.height = h;\n target.texelSizeX = 1 / w;\n target.texelSizeY = 1 / h;\n return target;\n }\n\n function initFramebuffers() {\n const simRes = getResolution(config.SIM_RESOLUTION!);\n const dyeRes = getResolution(config.DYE_RESOLUTION!);\n\n const texType = ext.halfFloatTexType;\n const rgba = ext.formatRGBA;\n const rg = ext.formatRG;\n const r = ext.formatR;\n const filtering = ext.supportLinearFiltering ? gl.LINEAR : gl.NEAREST;\n gl.disable(gl.BLEND);\n\n if (!rgba) {\n throw new Error(\"Required RGBA format is not supported by WebGL.\");\n }\n if (!dye) {\n dye = createDoubleFBO(\n dyeRes.width,\n dyeRes.height,\n rgba.internalFormat,\n rgba.format,\n texType,\n filtering,\n );\n } else {\n dye = resizeDoubleFBO(\n dye,\n dyeRes.width,\n dyeRes.height,\n rgba.internalFormat,\n rgba.format,\n texType,\n filtering,\n );\n }\n\n if (!rg) {\n throw new Error(\"Required RG format is not supported by WebGL.\");\n }\n if (!velocity) {\n velocity = createDoubleFBO(\n simRes.width,\n simRes.height,\n rg.internalFormat,\n rg.format,\n texType,\n filtering,\n );\n } else {\n velocity = resizeDoubleFBO(\n velocity,\n simRes.width,\n simRes.height,\n rg.internalFormat,\n rg.format,\n texType,\n filtering,\n );\n }\n\n if (!r) {\n throw new Error(\"Required R format is not supported by WebGL.\");\n }\n divergence = createFBO(\n simRes.width,\n simRes.height,\n r.internalFormat,\n r.format,\n texType,\n gl.NEAREST,\n );\n curl = createFBO(\n simRes.width,\n simRes.height,\n r.internalFormat,\n r.format,\n texType,\n gl.NEAREST,\n );\n pressure = createDoubleFBO(\n simRes.width,\n simRes.height,\n r.internalFormat,\n r.format,\n texType,\n gl.NEAREST,\n );\n }\n\n function updateKeywords() {\n const displayKeywords: string[] = [];\n if (config.SHADING) displayKeywords.push(\"SHADING\");\n displayMaterial.setKeywords(displayKeywords);\n }\n\n function getResolution(resolution: number) {\n const w = gl.drawingBufferWidth;\n const h = gl.drawingBufferHeight;\n const aspectRatio = w / h;\n const aspect = aspectRatio < 1 ? 1 / aspectRatio : aspectRatio;\n const min = Math.round(resolution);\n const max = Math.round(resolution * aspect);\n if (w > h) {\n return { width: max, height: min };\n }\n return { width: min, height: max };\n }\n\n function scaleByPixelRatio(input: number) {\n const pixelRatio = window.devicePixelRatio || 1;\n return Math.floor(input * pixelRatio);\n }\n\n // -------------------- Simulation Setup --------------------\n updateKeywords();\n initFramebuffers();\n\n let lastUpdateTime = Date.now();\n let colorUpdateTimer = 0.0;\n\n function updateFrame() {\n const dt = calcDeltaTime();\n if (resizeCanvas()) initFramebuffers();\n updateColors(dt);\n applyInputs();\n step(dt);\n render(null);\n requestAnimationFrame(updateFrame);\n }\n\n function calcDeltaTime() {\n const now = Date.now();\n let dt = (now - lastUpdateTime) / 1000;\n dt = Math.min(dt, 0.016666);\n lastUpdateTime = now;\n return dt;\n }\n\n function resizeCanvas() {\n const width = scaleByPixelRatio(canvas!.clientWidth);\n const height = scaleByPixelRatio(canvas!.clientHeight);\n if (canvas!.width !== width || canvas!.height !== height) {\n canvas!.width = width;\n canvas!.height = height;\n return true;\n }\n return false;\n }\n\n function updateColors(dt: number) {\n colorUpdateTimer += dt * config.COLOR_UPDATE_SPEED;\n if (colorUpdateTimer >= 1) {\n colorUpdateTimer = wrap(colorUpdateTimer, 0, 1);\n pointers.forEach((p) => {\n p.color = generateColor();\n });\n }\n }\n\n function applyInputs() {\n for (const p of pointers) {\n if (p.moved) {\n p.moved = false;\n splatPointer(p);\n }\n }\n }\n\n function step(dt: number) {\n gl.disable(gl.BLEND);\n\n // Curl\n curlProgram.bind();\n if (curlProgram.uniforms.texelSize) {\n gl.uniform2f(\n curlProgram.uniforms.texelSize,\n velocity.texelSizeX,\n velocity.texelSizeY,\n );\n }\n if (curlProgram.uniforms.uVelocity) {\n gl.uniform1i(curlProgram.uniforms.uVelocity, velocity.read.attach(0));\n }\n blit(curl);\n\n // Vorticity\n vorticityProgram.bind();\n if (vorticityProgram.uniforms.texelSize) {\n gl.uniform2f(\n vorticityProgram.uniforms.texelSize,\n velocity.texelSizeX,\n velocity.texelSizeY,\n );\n }\n if (vorticityProgram.uniforms.uVelocity) {\n gl.uniform1i(\n vorticityProgram.uniforms.uVelocity,\n velocity.read.attach(0),\n );\n }\n if (vorticityProgram.uniforms.uCurl) {\n gl.uniform1i(vorticityProgram.uniforms.uCurl, curl.attach(1));\n }\n if (vorticityProgram.uniforms.curl) {\n gl.uniform1f(vorticityProgram.uniforms.curl, config.CURL);\n }\n if (vorticityProgram.uniforms.dt) {\n gl.uniform1f(vorticityProgram.uniforms.dt, dt);\n }\n blit(velocity.write);\n velocity.swap();\n\n // Divergence\n divergenceProgram.bind();\n if (divergenceProgram.uniforms.texelSize) {\n gl.uniform2f(\n divergenceProgram.uniforms.texelSize,\n velocity.texelSizeX,\n velocity.texelSizeY,\n );\n }\n if (divergenceProgram.uniforms.uVelocity) {\n gl.uniform1i(\n divergenceProgram.uniforms.uVelocity,\n velocity.read.attach(0),\n );\n }\n blit(divergence);\n\n // Clear pressure\n clearProgram.bind();\n if (clearProgram.uniforms.uTexture) {\n gl.uniform1i(clearProgram.uniforms.uTexture, pressure.read.attach(0));\n }\n if (clearProgram.uniforms.value) {\n gl.uniform1f(clearProgram.uniforms.value, config.PRESSURE);\n }\n blit(pressure.write);\n pressure.swap();\n\n // Pressure\n pressureProgram.bind();\n if (pressureProgram.uniforms.texelSize) {\n gl.uniform2f(\n pressureProgram.uniforms.texelSize,\n velocity.texelSizeX,\n velocity.texelSizeY,\n );\n }\n if (pressureProgram.uniforms.uDivergence) {\n gl.uniform1i(\n pressureProgram.uniforms.uDivergence,\n divergence.attach(0),\n );\n }\n for (let i = 0; i < config.PRESSURE_ITERATIONS; i++) {\n if (pressureProgram.uniforms.uPressure) {\n gl.uniform1i(\n pressureProgram.uniforms.uPressure,\n pressure.read.attach(1),\n );\n }\n blit(pressure.write);\n pressure.swap();\n }\n\n // Gradient Subtract\n gradienSubtractProgram.bind();\n if (gradienSubtractProgram.uniforms.texelSize) {\n gl.uniform2f(\n gradienSubtractProgram.uniforms.texelSize,\n velocity.texelSizeX,\n velocity.texelSizeY,\n );\n }\n if (gradienSubtractProgram.uniforms.uPressure) {\n gl.uniform1i(\n gradienSubtractProgram.uniforms.uPressure,\n pressure.read.attach(0),\n );\n }\n if (gradienSubtractProgram.uniforms.uVelocity) {\n gl.uniform1i(\n gradienSubtractProgram.uniforms.uVelocity,\n velocity.read.attach(1),\n );\n }\n blit(velocity.write);\n velocity.swap();\n\n // Advection - velocity\n advectionProgram.bind();\n if (advectionProgram.uniforms.texelSize) {\n gl.uniform2f(\n advectionProgram.uniforms.texelSize,\n velocity.texelSizeX,\n velocity.texelSizeY,\n );\n }\n if (\n !ext.supportLinearFiltering &&\n advectionProgram.uniforms.dyeTexelSize\n ) {\n gl.uniform2f(\n advectionProgram.uniforms.dyeTexelSize,\n velocity.texelSizeX,\n velocity.texelSizeY,\n );\n }\n const velocityId = velocity.read.attach(0);\n if (advectionProgram.uniforms.uVelocity) {\n gl.uniform1i(advectionProgram.uniforms.uVelocity, velocityId);\n }\n if (advectionProgram.uniforms.uSource) {\n gl.uniform1i(advectionProgram.uniforms.uSource, velocityId);\n }\n if (advectionProgram.uniforms.dt) {\n gl.uniform1f(advectionProgram.uniforms.dt, dt);\n }\n if (advectionProgram.uniforms.dissipation) {\n gl.uniform1f(\n advectionProgram.uniforms.dissipation,\n config.VELOCITY_DISSIPATION,\n );\n }\n blit(velocity.write);\n velocity.swap();\n\n // Advection - dye\n if (\n !ext.supportLinearFiltering &&\n advectionProgram.uniforms.dyeTexelSize\n ) {\n gl.uniform2f(\n advectionProgram.uniforms.dyeTexelSize,\n dye.texelSizeX,\n dye.texelSizeY,\n );\n }\n if (advectionProgram.uniforms.uVelocity) {\n gl.uniform1i(\n advectionProgram.uniforms.uVelocity,\n velocity.read.attach(0),\n );\n }\n if (advectionProgram.uniforms.uSource) {\n gl.uniform1i(advectionProgram.uniforms.uSource, dye.read.attach(1));\n }\n if (advectionProgram.uniforms.dissipation) {\n gl.uniform1f(\n advectionProgram.uniforms.dissipation,\n config.DENSITY_DISSIPATION,\n );\n }\n blit(dye.write);\n dye.swap();\n }\n\n function render(target: FBO | null) {\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n gl.enable(gl.BLEND);\n drawDisplay(target);\n }\n\n function drawDisplay(target: FBO | null) {\n const width = target ? target.width : gl.drawingBufferWidth;\n const height = target ? target.height : gl.drawingBufferHeight;\n displayMaterial.bind();\n if (config.SHADING && displayMaterial.uniforms.texelSize) {\n gl.uniform2f(displayMaterial.uniforms.texelSize, 1 / width, 1 / height);\n }\n if (displayMaterial.uniforms.uTexture) {\n gl.uniform1i(displayMaterial.uniforms.uTexture, dye.read.attach(0));\n }\n blit(target, false);\n }\n\n // -------------------- Interaction --------------------\n function splatPointer(pointer: Pointer) {\n const dx = pointer.deltaX * config.SPLAT_FORCE;\n const dy = pointer.deltaY * config.SPLAT_FORCE;\n splat(pointer.texcoordX, pointer.texcoordY, dx, dy, pointer.color);\n }\n\n function clickSplat(pointer: Pointer) {\n const color = generateColor();\n color.r *= 10;\n color.g *= 10;\n color.b *= 10;\n const dx = 10 * (Math.random() - 0.5);\n const dy = 30 * (Math.random() - 0.5);\n splat(pointer.texcoordX, pointer.texcoordY, dx, dy, color);\n }\n\n function splat(\n x: number,\n y: number,\n dx: number,\n dy: number,\n color: ColorRGB,\n ) {\n splatProgram.bind();\n if (splatProgram.uniforms.uTarget) {\n gl.uniform1i(splatProgram.uniforms.uTarget, velocity.read.attach(0));\n }\n if (splatProgram.uniforms.aspectRatio) {\n gl.uniform1f(\n splatProgram.uniforms.aspectRatio,\n canvas!.width / canvas!.height,\n );\n }\n if (splatProgram.uniforms.point) {\n gl.uniform2f(splatProgram.uniforms.point, x, y);\n }\n if (splatProgram.uniforms.color) {\n gl.uniform3f(splatProgram.uniforms.color, dx, dy, 0);\n }\n if (splatProgram.uniforms.radius) {\n gl.uniform1f(\n splatProgram.uniforms.radius,\n correctRadius(config.SPLAT_RADIUS / 100)!,\n );\n }\n blit(velocity.write);\n velocity.swap();\n\n if (splatProgram.uniforms.uTarget) {\n gl.uniform1i(splatProgram.uniforms.uTarget, dye.read.attach(0));\n }\n if (splatProgram.uniforms.color) {\n gl.uniform3f(splatProgram.uniforms.color, color.r, color.g, color.b);\n }\n blit(dye.write);\n dye.swap();\n }\n\n function correctRadius(radius: number) {\n // Use non-null assertion (canvas can't be null here)\n const aspectRatio = canvas!.width / canvas!.height;\n if (aspectRatio > 1) radius *= aspectRatio;\n return radius;\n }\n\n function updatePointerDownData(\n pointer: Pointer,\n id: number,\n posX: number,\n posY: number,\n ) {\n pointer.id = id;\n pointer.down = true;\n pointer.moved = false;\n pointer.texcoordX = posX / canvas!.width;\n pointer.texcoordY = 1 - posY / canvas!.height;\n pointer.prevTexcoordX = pointer.texcoordX;\n pointer.prevTexcoordY = pointer.texcoordY;\n pointer.deltaX = 0;\n pointer.deltaY = 0;\n pointer.color = generateColor();\n }\n\n function updatePointerMoveData(\n pointer: Pointer,\n posX: number,\n posY: number,\n color: ColorRGB,\n ) {\n pointer.prevTexcoordX = pointer.texcoordX;\n pointer.prevTexcoordY = pointer.texcoordY;\n pointer.texcoordX = posX / canvas!.width;\n pointer.texcoordY = 1 - posY / canvas!.height;\n pointer.deltaX = correctDeltaX(\n pointer.texcoordX - pointer.prevTexcoordX,\n )!;\n pointer.deltaY = correctDeltaY(\n pointer.texcoordY - pointer.prevTexcoordY,\n )!;\n pointer.moved =\n Math.abs(pointer.deltaX) > 0 || Math.abs(pointer.deltaY) > 0;\n pointer.color = color;\n }\n\n function updatePointerUpData(pointer: Pointer) {\n pointer.down = false;\n }\n\n function correctDeltaX(delta: number) {\n const aspectRatio = canvas!.width / canvas!.height;\n if (aspectRatio < 1) delta *= aspectRatio;\n return delta;\n }\n\n function correctDeltaY(delta: number) {\n const aspectRatio = canvas!.width / canvas!.height;\n if (aspectRatio > 1) delta /= aspectRatio;\n return delta;\n }\n\n function generateColor(): ColorRGB {\n const c = HSVtoRGB(Math.random(), 1.0, 1.0);\n c.r *= 0.15;\n c.g *= 0.15;\n c.b *= 0.15;\n return c;\n }\n\n function HSVtoRGB(h: number, s: number, v: number): ColorRGB {\n let r = 0,\n g = 0,\n b = 0;\n const i = Math.floor(h * 6);\n const f = h * 6 - i;\n const p = v * (1 - s);\n const q = v * (1 - f * s);\n const t = v * (1 - (1 - f) * s);\n\n switch (i % 6) {\n case 0:\n r = v;\n g = t;\n b = p;\n break;\n case 1:\n r = q;\n g = v;\n b = p;\n break;\n case 2:\n r = p;\n g = v;\n b = t;\n break;\n case 3:\n r = p;\n g = q;\n b = v;\n break;\n case 4:\n r = t;\n g = p;\n b = v;\n break;\n case 5:\n r = v;\n g = p;\n b = q;\n break;\n }\n return { r, g, b };\n }\n\n function wrap(value: number, min: number, max: number) {\n const range = max - min;\n if (range === 0) return min;\n return ((value - min) % range) + min;\n }\n\n // -------------------- Event Listeners --------------------\n window.addEventListener(\"mousedown\", (e) => {\n const pointer = pointers[0];\n const posX = scaleByPixelRatio(e.clientX);\n const posY = scaleByPixelRatio(e.clientY);\n updatePointerDownData(pointer, -1, posX, posY);\n clickSplat(pointer);\n });\n\n // Start rendering on first mouse move\n function handleFirstMouseMove(e: MouseEvent) {\n const pointer = pointers[0];\n const posX = scaleByPixelRatio(e.clientX);\n const posY = scaleByPixelRatio(e.clientY);\n const color = generateColor();\n updateFrame();\n updatePointerMoveData(pointer, posX, posY, color);\n document.body.removeEventListener(\"mousemove\", handleFirstMouseMove);\n }\n document.body.addEventListener(\"mousemove\", handleFirstMouseMove);\n\n window.addEventListener(\"mousemove\", (e) => {\n const pointer = pointers[0];\n const posX = scaleByPixelRatio(e.clientX);\n const posY = scaleByPixelRatio(e.clientY);\n const color = pointer.color;\n updatePointerMoveData(pointer, posX, posY, color);\n });\n\n // Start rendering on first touch\n function handleFirstTouchStart(e: TouchEvent) {\n const touches = e.targetTouches;\n const pointer = pointers[0];\n for (let i = 0; i < touches.length; i++) {\n const posX = scaleByPixelRatio(touches[i].clientX);\n const posY = scaleByPixelRatio(touches[i].clientY);\n updateFrame();\n updatePointerDownData(pointer, touches[i].identifier, posX, posY);\n }\n document.body.removeEventListener(\"touchstart\", handleFirstTouchStart);\n }\n document.body.addEventListener(\"touchstart\", handleFirstTouchStart);\n\n window.addEventListener(\n \"touchstart\",\n (e) => {\n const touches = e.targetTouches;\n const pointer = pointers[0];\n for (let i = 0; i < touches.length; i++) {\n const posX = scaleByPixelRatio(touches[i].clientX);\n const posY = scaleByPixelRatio(touches[i].clientY);\n updatePointerDownData(pointer, touches[i].identifier, posX, posY);\n }\n },\n false,\n );\n\n window.addEventListener(\n \"touchmove\",\n (e) => {\n const touches = e.targetTouches;\n const pointer = pointers[0];\n for (let i = 0; i < touches.length; i++) {\n const posX = scaleByPixelRatio(touches[i].clientX);\n const posY = scaleByPixelRatio(touches[i].clientY);\n updatePointerMoveData(pointer, posX, posY, pointer.color);\n }\n },\n false,\n );\n\n window.addEventListener(\"touchend\", (e) => {\n const touches = e.changedTouches;\n const pointer = pointers[0];\n for (let i = 0; i < touches.length; i++) {\n updatePointerUpData(pointer);\n }\n });\n // ------------------------------------------------------------\n }, [\n SIM_RESOLUTION,\n DYE_RESOLUTION,\n CAPTURE_RESOLUTION,\n DENSITY_DISSIPATION,\n VELOCITY_DISSIPATION,\n PRESSURE,\n PRESSURE_ITERATIONS,\n CURL,\n SPLAT_RADIUS,\n SPLAT_FORCE,\n SHADING,\n COLOR_UPDATE_SPEED,\n BACK_COLOR,\n TRANSPARENT,\n ]);\n\n return (\n
\n \n
\n );\n}\n", + "content": "\"use client\";\r\nimport React, { useEffect, useRef } from \"react\";\r\n\r\ninterface ColorRGB {\r\n r: number;\r\n g: number;\r\n b: number;\r\n}\r\n\r\ninterface SplashCursorProps {\r\n SIM_RESOLUTION?: number;\r\n DYE_RESOLUTION?: number;\r\n CAPTURE_RESOLUTION?: number;\r\n DENSITY_DISSIPATION?: number;\r\n VELOCITY_DISSIPATION?: number;\r\n PRESSURE?: number;\r\n PRESSURE_ITERATIONS?: number;\r\n CURL?: number;\r\n SPLAT_RADIUS?: number;\r\n SPLAT_FORCE?: number;\r\n SHADING?: boolean;\r\n COLOR_UPDATE_SPEED?: number;\r\n BACK_COLOR?: ColorRGB;\r\n TRANSPARENT?: boolean;\r\n}\r\n\r\ninterface Pointer {\r\n id: number;\r\n texcoordX: number;\r\n texcoordY: number;\r\n prevTexcoordX: number;\r\n prevTexcoordY: number;\r\n deltaX: number;\r\n deltaY: number;\r\n down: boolean;\r\n moved: boolean;\r\n color: ColorRGB;\r\n}\r\n\r\nfunction pointerPrototype(): Pointer {\r\n return {\r\n id: -1,\r\n texcoordX: 0,\r\n texcoordY: 0,\r\n prevTexcoordX: 0,\r\n prevTexcoordY: 0,\r\n deltaX: 0,\r\n deltaY: 0,\r\n down: false,\r\n moved: false,\r\n color: { r: 0, g: 0, b: 0 },\r\n };\r\n}\r\n\r\nexport default function SplashCursor({\r\n SIM_RESOLUTION = 128,\r\n DYE_RESOLUTION = 1440,\r\n CAPTURE_RESOLUTION = 512,\r\n DENSITY_DISSIPATION = 3.5,\r\n VELOCITY_DISSIPATION = 2,\r\n PRESSURE = 0.1,\r\n PRESSURE_ITERATIONS = 20,\r\n CURL = 3,\r\n SPLAT_RADIUS = 0.2,\r\n SPLAT_FORCE = 6000,\r\n SHADING = true,\r\n COLOR_UPDATE_SPEED = 10,\r\n BACK_COLOR = { r: 0.5, g: 0, b: 0 },\r\n TRANSPARENT = true,\r\n}: SplashCursorProps) {\r\n const canvasRef = useRef(null);\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return; // Guard canvas early\r\n\r\n // Pointer and config setup\r\n const pointers: Pointer[] = [pointerPrototype()];\r\n\r\n // All these are guaranteed numbers due to destructuring defaults\r\n // So we cast them to remove TS warnings:\r\n const config = {\r\n SIM_RESOLUTION: SIM_RESOLUTION!,\r\n DYE_RESOLUTION: DYE_RESOLUTION!,\r\n CAPTURE_RESOLUTION: CAPTURE_RESOLUTION!,\r\n DENSITY_DISSIPATION: DENSITY_DISSIPATION!,\r\n VELOCITY_DISSIPATION: VELOCITY_DISSIPATION!,\r\n PRESSURE: PRESSURE!,\r\n PRESSURE_ITERATIONS: PRESSURE_ITERATIONS!,\r\n CURL: CURL!,\r\n SPLAT_RADIUS: SPLAT_RADIUS!,\r\n SPLAT_FORCE: SPLAT_FORCE!,\r\n SHADING,\r\n COLOR_UPDATE_SPEED: COLOR_UPDATE_SPEED!,\r\n PAUSED: false,\r\n BACK_COLOR,\r\n TRANSPARENT,\r\n };\r\n\r\n // Get WebGL context (WebGL1 or WebGL2)\r\n const { gl, ext } = getWebGLContext(canvas);\r\n if (!gl || !ext) return;\r\n\r\n // If no linear filtering, reduce resolution\r\n if (!ext.supportLinearFiltering) {\r\n config.DYE_RESOLUTION = 256;\r\n config.SHADING = false;\r\n }\r\n\r\n function getWebGLContext(canvas: HTMLCanvasElement) {\r\n const params = {\r\n alpha: true,\r\n depth: false,\r\n stencil: false,\r\n antialias: false,\r\n preserveDrawingBuffer: false,\r\n };\r\n\r\n let gl = canvas.getContext(\r\n \"webgl2\",\r\n params,\r\n ) as WebGL2RenderingContext | null;\r\n\r\n if (!gl) {\r\n gl = (canvas.getContext(\"webgl\", params) ||\r\n canvas.getContext(\r\n \"experimental-webgl\",\r\n params,\r\n )) as WebGL2RenderingContext | null;\r\n }\r\n\r\n if (!gl) {\r\n throw new Error(\"Unable to initialize WebGL.\");\r\n }\r\n\r\n const isWebGL2 = \"drawBuffers\" in gl;\r\n\r\n let supportLinearFiltering = false;\r\n let halfFloat: OES_texture_half_float | null = null;\r\n\r\n if (isWebGL2) {\r\n // For WebGL2\r\n (gl as WebGL2RenderingContext).getExtension(\"EXT_color_buffer_float\");\r\n supportLinearFiltering = !!(gl as WebGL2RenderingContext).getExtension(\r\n \"OES_texture_float_linear\",\r\n );\r\n } else {\r\n // For WebGL1\r\n halfFloat = gl.getExtension(\"OES_texture_half_float\");\r\n supportLinearFiltering = !!gl.getExtension(\r\n \"OES_texture_half_float_linear\",\r\n );\r\n }\r\n\r\n gl.clearColor(0, 0, 0, 1);\r\n\r\n const halfFloatTexType = isWebGL2\r\n ? (gl as WebGL2RenderingContext).HALF_FLOAT\r\n : halfFloat && \"HALF_FLOAT_OES\" in halfFloat\r\n ? (halfFloat as OES_texture_half_float).HALF_FLOAT_OES\r\n : 0;\r\n\r\n let formatRGBA: { internalFormat: number; format: number } | null;\r\n let formatRG: { internalFormat: number; format: number } | null;\r\n let formatR: { internalFormat: number; format: number } | null;\r\n\r\n if (isWebGL2) {\r\n formatRGBA = getSupportedFormat(\r\n gl,\r\n (gl as WebGL2RenderingContext).RGBA16F,\r\n gl.RGBA,\r\n halfFloatTexType,\r\n );\r\n formatRG = getSupportedFormat(\r\n gl,\r\n (gl as WebGL2RenderingContext).RG16F,\r\n (gl as WebGL2RenderingContext).RG,\r\n halfFloatTexType,\r\n );\r\n formatR = getSupportedFormat(\r\n gl,\r\n (gl as WebGL2RenderingContext).R16F,\r\n (gl as WebGL2RenderingContext).RED,\r\n halfFloatTexType,\r\n );\r\n } else {\r\n formatRGBA = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);\r\n formatRG = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);\r\n formatR = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);\r\n }\r\n\r\n return {\r\n gl,\r\n ext: {\r\n formatRGBA,\r\n formatRG,\r\n formatR,\r\n halfFloatTexType,\r\n supportLinearFiltering,\r\n },\r\n };\r\n }\r\n\r\n function getSupportedFormat(\r\n gl: WebGLRenderingContext | WebGL2RenderingContext,\r\n internalFormat: number,\r\n format: number,\r\n type: number,\r\n ): { internalFormat: number; format: number } | null {\r\n if (!supportRenderTextureFormat(gl, internalFormat, format, type)) {\r\n // For WebGL2 fallback:\r\n if (\"drawBuffers\" in gl) {\r\n const gl2 = gl as WebGL2RenderingContext;\r\n switch (internalFormat) {\r\n case gl2.R16F:\r\n return getSupportedFormat(gl2, gl2.RG16F, gl2.RG, type);\r\n case gl2.RG16F:\r\n return getSupportedFormat(gl2, gl2.RGBA16F, gl2.RGBA, type);\r\n default:\r\n return null;\r\n }\r\n }\r\n return null;\r\n }\r\n return { internalFormat, format };\r\n }\r\n\r\n function supportRenderTextureFormat(\r\n gl: WebGLRenderingContext | WebGL2RenderingContext,\r\n internalFormat: number,\r\n format: number,\r\n type: number,\r\n ) {\r\n const texture = gl.createTexture();\r\n if (!texture) return false;\r\n\r\n gl.bindTexture(gl.TEXTURE_2D, texture);\r\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\r\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\r\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\r\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\r\n gl.texImage2D(\r\n gl.TEXTURE_2D,\r\n 0,\r\n internalFormat,\r\n 4,\r\n 4,\r\n 0,\r\n format,\r\n type,\r\n null,\r\n );\r\n\r\n const fbo = gl.createFramebuffer();\r\n if (!fbo) return false;\r\n\r\n gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);\r\n gl.framebufferTexture2D(\r\n gl.FRAMEBUFFER,\r\n gl.COLOR_ATTACHMENT0,\r\n gl.TEXTURE_2D,\r\n texture,\r\n 0,\r\n );\r\n const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);\r\n return status === gl.FRAMEBUFFER_COMPLETE;\r\n }\r\n\r\n function hashCode(s: string) {\r\n if (!s.length) return 0;\r\n let hash = 0;\r\n for (let i = 0; i < s.length; i++) {\r\n hash = (hash << 5) - hash + s.charCodeAt(i);\r\n hash |= 0;\r\n }\r\n return hash;\r\n }\r\n\r\n function addKeywords(source: string, keywords: string[] | null) {\r\n if (!keywords) return source;\r\n let keywordsString = \"\";\r\n for (const keyword of keywords) {\r\n keywordsString += `#define ${keyword}\\n`;\r\n }\r\n return keywordsString + source;\r\n }\r\n\r\n function compileShader(\r\n type: number,\r\n source: string,\r\n keywords: string[] | null = null,\r\n ): WebGLShader | null {\r\n const shaderSource = addKeywords(source, keywords);\r\n const shader = gl.createShader(type);\r\n if (!shader) return null;\r\n gl.shaderSource(shader, shaderSource);\r\n gl.compileShader(shader);\r\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\r\n console.trace(gl.getShaderInfoLog(shader));\r\n }\r\n return shader;\r\n }\r\n\r\n function createProgram(\r\n vertexShader: WebGLShader | null,\r\n fragmentShader: WebGLShader | null,\r\n ): WebGLProgram | null {\r\n if (!vertexShader || !fragmentShader) return null;\r\n const program = gl.createProgram();\r\n if (!program) return null;\r\n gl.attachShader(program, vertexShader);\r\n gl.attachShader(program, fragmentShader);\r\n gl.linkProgram(program);\r\n if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\r\n console.trace(gl.getProgramInfoLog(program));\r\n }\r\n return program;\r\n }\r\n\r\n function getUniforms(program: WebGLProgram) {\r\n const uniforms: Record = {};\r\n const uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);\r\n for (let i = 0; i < uniformCount; i++) {\r\n const uniformInfo = gl.getActiveUniform(program, i);\r\n if (uniformInfo) {\r\n uniforms[uniformInfo.name] = gl.getUniformLocation(\r\n program,\r\n uniformInfo.name,\r\n );\r\n }\r\n }\r\n return uniforms;\r\n }\r\n\r\n class Program {\r\n program: WebGLProgram | null;\r\n uniforms: Record;\r\n\r\n constructor(\r\n vertexShader: WebGLShader | null,\r\n fragmentShader: WebGLShader | null,\r\n ) {\r\n this.program = createProgram(vertexShader, fragmentShader);\r\n this.uniforms = this.program ? getUniforms(this.program) : {};\r\n }\r\n\r\n bind() {\r\n if (this.program) gl.useProgram(this.program);\r\n }\r\n }\r\n\r\n class Material {\r\n vertexShader: WebGLShader | null;\r\n fragmentShaderSource: string;\r\n programs: Record;\r\n activeProgram: WebGLProgram | null;\r\n uniforms: Record;\r\n\r\n constructor(\r\n vertexShader: WebGLShader | null,\r\n fragmentShaderSource: string,\r\n ) {\r\n this.vertexShader = vertexShader;\r\n this.fragmentShaderSource = fragmentShaderSource;\r\n this.programs = {};\r\n this.activeProgram = null;\r\n this.uniforms = {};\r\n }\r\n\r\n setKeywords(keywords: string[]) {\r\n let hash = 0;\r\n for (const kw of keywords) {\r\n hash += hashCode(kw);\r\n }\r\n let program = this.programs[hash];\r\n if (program == null) {\r\n const fragmentShader = compileShader(\r\n gl.FRAGMENT_SHADER,\r\n this.fragmentShaderSource,\r\n keywords,\r\n );\r\n program = createProgram(this.vertexShader, fragmentShader);\r\n this.programs[hash] = program;\r\n }\r\n if (program === this.activeProgram) return;\r\n if (program) {\r\n this.uniforms = getUniforms(program);\r\n }\r\n this.activeProgram = program;\r\n }\r\n\r\n bind() {\r\n if (this.activeProgram) {\r\n gl.useProgram(this.activeProgram);\r\n }\r\n }\r\n }\r\n\r\n // -------------------- Shaders --------------------\r\n const baseVertexShader = compileShader(\r\n gl.VERTEX_SHADER,\r\n `\r\n precision highp float;\r\n attribute vec2 aPosition;\r\n varying vec2 vUv;\r\n varying vec2 vL;\r\n varying vec2 vR;\r\n varying vec2 vT;\r\n varying vec2 vB;\r\n uniform vec2 texelSize;\r\n\r\n void main () {\r\n vUv = aPosition * 0.5 + 0.5;\r\n vL = vUv - vec2(texelSize.x, 0.0);\r\n vR = vUv + vec2(texelSize.x, 0.0);\r\n vT = vUv + vec2(0.0, texelSize.y);\r\n vB = vUv - vec2(0.0, texelSize.y);\r\n gl_Position = vec4(aPosition, 0.0, 1.0);\r\n }\r\n `,\r\n );\r\n\r\n const copyShader = compileShader(\r\n gl.FRAGMENT_SHADER,\r\n `\r\n precision mediump float;\r\n precision mediump sampler2D;\r\n varying highp vec2 vUv;\r\n uniform sampler2D uTexture;\r\n\r\n void main () {\r\n gl_FragColor = texture2D(uTexture, vUv);\r\n }\r\n `,\r\n );\r\n\r\n const clearShader = compileShader(\r\n gl.FRAGMENT_SHADER,\r\n `\r\n precision mediump float;\r\n precision mediump sampler2D;\r\n varying highp vec2 vUv;\r\n uniform sampler2D uTexture;\r\n uniform float value;\r\n\r\n void main () {\r\n gl_FragColor = value * texture2D(uTexture, vUv);\r\n }\r\n `,\r\n );\r\n\r\n const displayShaderSource = `\r\n precision highp float;\r\n precision highp sampler2D;\r\n varying vec2 vUv;\r\n varying vec2 vL;\r\n varying vec2 vR;\r\n varying vec2 vT;\r\n varying vec2 vB;\r\n uniform sampler2D uTexture;\r\n uniform sampler2D uDithering;\r\n uniform vec2 ditherScale;\r\n uniform vec2 texelSize;\r\n\r\n vec3 linearToGamma (vec3 color) {\r\n color = max(color, vec3(0));\r\n return max(1.055 * pow(color, vec3(0.416666667)) - 0.055, vec3(0));\r\n }\r\n\r\n void main () {\r\n vec3 c = texture2D(uTexture, vUv).rgb;\r\n #ifdef SHADING\r\n vec3 lc = texture2D(uTexture, vL).rgb;\r\n vec3 rc = texture2D(uTexture, vR).rgb;\r\n vec3 tc = texture2D(uTexture, vT).rgb;\r\n vec3 bc = texture2D(uTexture, vB).rgb;\r\n\r\n float dx = length(rc) - length(lc);\r\n float dy = length(tc) - length(bc);\r\n\r\n vec3 n = normalize(vec3(dx, dy, length(texelSize)));\r\n vec3 l = vec3(0.0, 0.0, 1.0);\r\n\r\n float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0);\r\n c *= diffuse;\r\n #endif\r\n\r\n float a = max(c.r, max(c.g, c.b));\r\n gl_FragColor = vec4(c, a);\r\n }\r\n `;\r\n\r\n const splatShader = compileShader(\r\n gl.FRAGMENT_SHADER,\r\n `\r\n precision highp float;\r\n precision highp sampler2D;\r\n varying vec2 vUv;\r\n uniform sampler2D uTarget;\r\n uniform float aspectRatio;\r\n uniform vec3 color;\r\n uniform vec2 point;\r\n uniform float radius;\r\n\r\n void main () {\r\n vec2 p = vUv - point.xy;\r\n p.x *= aspectRatio;\r\n vec3 splat = exp(-dot(p, p) / radius) * color;\r\n vec3 base = texture2D(uTarget, vUv).xyz;\r\n gl_FragColor = vec4(base + splat, 1.0);\r\n }\r\n `,\r\n );\r\n\r\n const advectionShader = compileShader(\r\n gl.FRAGMENT_SHADER,\r\n `\r\n precision highp float;\r\n precision highp sampler2D;\r\n varying vec2 vUv;\r\n uniform sampler2D uVelocity;\r\n uniform sampler2D uSource;\r\n uniform vec2 texelSize;\r\n uniform vec2 dyeTexelSize;\r\n uniform float dt;\r\n uniform float dissipation;\r\n\r\n vec4 bilerp (sampler2D sam, vec2 uv, vec2 tsize) {\r\n vec2 st = uv / tsize - 0.5;\r\n vec2 iuv = floor(st);\r\n vec2 fuv = fract(st);\r\n\r\n vec4 a = texture2D(sam, (iuv + vec2(0.5, 0.5)) * tsize);\r\n vec4 b = texture2D(sam, (iuv + vec2(1.5, 0.5)) * tsize);\r\n vec4 c = texture2D(sam, (iuv + vec2(0.5, 1.5)) * tsize);\r\n vec4 d = texture2D(sam, (iuv + vec2(1.5, 1.5)) * tsize);\r\n\r\n return mix(mix(a, b, fuv.x), mix(c, d, fuv.x), fuv.y);\r\n }\r\n\r\n void main () {\r\n #ifdef MANUAL_FILTERING\r\n vec2 coord = vUv - dt * bilerp(uVelocity, vUv, texelSize).xy * texelSize;\r\n vec4 result = bilerp(uSource, coord, dyeTexelSize);\r\n #else\r\n vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize;\r\n vec4 result = texture2D(uSource, coord);\r\n #endif\r\n float decay = 1.0 + dissipation * dt;\r\n gl_FragColor = result / decay;\r\n }\r\n `,\r\n ext.supportLinearFiltering ? null : [\"MANUAL_FILTERING\"],\r\n );\r\n\r\n const divergenceShader = compileShader(\r\n gl.FRAGMENT_SHADER,\r\n `\r\n precision mediump float;\r\n precision mediump sampler2D;\r\n varying highp vec2 vUv;\r\n varying highp vec2 vL;\r\n varying highp vec2 vR;\r\n varying highp vec2 vT;\r\n varying highp vec2 vB;\r\n uniform sampler2D uVelocity;\r\n\r\n void main () {\r\n float L = texture2D(uVelocity, vL).x;\r\n float R = texture2D(uVelocity, vR).x;\r\n float T = texture2D(uVelocity, vT).y;\r\n float B = texture2D(uVelocity, vB).y;\r\n\r\n vec2 C = texture2D(uVelocity, vUv).xy;\r\n if (vL.x < 0.0) { L = -C.x; }\r\n if (vR.x > 1.0) { R = -C.x; }\r\n if (vT.y > 1.0) { T = -C.y; }\r\n if (vB.y < 0.0) { B = -C.y; }\r\n\r\n float div = 0.5 * (R - L + T - B);\r\n gl_FragColor = vec4(div, 0.0, 0.0, 1.0);\r\n }\r\n `,\r\n );\r\n\r\n const curlShader = compileShader(\r\n gl.FRAGMENT_SHADER,\r\n `\r\n precision mediump float;\r\n precision mediump sampler2D;\r\n varying highp vec2 vUv;\r\n varying highp vec2 vL;\r\n varying highp vec2 vR;\r\n varying highp vec2 vT;\r\n varying highp vec2 vB;\r\n uniform sampler2D uVelocity;\r\n\r\n void main () {\r\n float L = texture2D(uVelocity, vL).y;\r\n float R = texture2D(uVelocity, vR).y;\r\n float T = texture2D(uVelocity, vT).x;\r\n float B = texture2D(uVelocity, vB).x;\r\n float vorticity = R - L - T + B;\r\n gl_FragColor = vec4(0.5 * vorticity, 0.0, 0.0, 1.0);\r\n }\r\n `,\r\n );\r\n\r\n const vorticityShader = compileShader(\r\n gl.FRAGMENT_SHADER,\r\n `\r\n precision highp float;\r\n precision highp sampler2D;\r\n varying vec2 vUv;\r\n varying vec2 vL;\r\n varying vec2 vR;\r\n varying vec2 vT;\r\n varying vec2 vB;\r\n uniform sampler2D uVelocity;\r\n uniform sampler2D uCurl;\r\n uniform float curl;\r\n uniform float dt;\r\n\r\n void main () {\r\n float L = texture2D(uCurl, vL).x;\r\n float R = texture2D(uCurl, vR).x;\r\n float T = texture2D(uCurl, vT).x;\r\n float B = texture2D(uCurl, vB).x;\r\n float C = texture2D(uCurl, vUv).x;\r\n\r\n vec2 force = 0.5 * vec2(abs(T) - abs(B), abs(R) - abs(L));\r\n force /= length(force) + 0.0001;\r\n force *= curl * C;\r\n force.y *= -1.0;\r\n\r\n vec2 velocity = texture2D(uVelocity, vUv).xy;\r\n velocity += force * dt;\r\n velocity = min(max(velocity, -1000.0), 1000.0);\r\n gl_FragColor = vec4(velocity, 0.0, 1.0);\r\n }\r\n `,\r\n );\r\n\r\n const pressureShader = compileShader(\r\n gl.FRAGMENT_SHADER,\r\n `\r\n precision mediump float;\r\n precision mediump sampler2D;\r\n varying highp vec2 vUv;\r\n varying highp vec2 vL;\r\n varying highp vec2 vR;\r\n varying highp vec2 vT;\r\n varying highp vec2 vB;\r\n uniform sampler2D uPressure;\r\n uniform sampler2D uDivergence;\r\n\r\n void main () {\r\n float L = texture2D(uPressure, vL).x;\r\n float R = texture2D(uPressure, vR).x;\r\n float T = texture2D(uPressure, vT).x;\r\n float B = texture2D(uPressure, vB).x;\r\n float C = texture2D(uPressure, vUv).x;\r\n float divergence = texture2D(uDivergence, vUv).x;\r\n float pressure = (L + R + B + T - divergence) * 0.25;\r\n gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0);\r\n }\r\n `,\r\n );\r\n\r\n const gradientSubtractShader = compileShader(\r\n gl.FRAGMENT_SHADER,\r\n `\r\n precision mediump float;\r\n precision mediump sampler2D;\r\n varying highp vec2 vUv;\r\n varying highp vec2 vL;\r\n varying highp vec2 vR;\r\n varying highp vec2 vT;\r\n varying highp vec2 vB;\r\n uniform sampler2D uPressure;\r\n uniform sampler2D uVelocity;\r\n\r\n void main () {\r\n float L = texture2D(uPressure, vL).x;\r\n float R = texture2D(uPressure, vR).x;\r\n float T = texture2D(uPressure, vT).x;\r\n float B = texture2D(uPressure, vB).x;\r\n vec2 velocity = texture2D(uVelocity, vUv).xy;\r\n velocity.xy -= vec2(R - L, T - B);\r\n gl_FragColor = vec4(velocity, 0.0, 1.0);\r\n }\r\n `,\r\n );\r\n\r\n // -------------------- Fullscreen Triangles --------------------\r\n const blit = (() => {\r\n const buffer = gl.createBuffer()!;\r\n gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\r\n gl.bufferData(\r\n gl.ARRAY_BUFFER,\r\n new Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]),\r\n gl.STATIC_DRAW,\r\n );\r\n const elemBuffer = gl.createBuffer()!;\r\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elemBuffer);\r\n gl.bufferData(\r\n gl.ELEMENT_ARRAY_BUFFER,\r\n new Uint16Array([0, 1, 2, 0, 2, 3]),\r\n gl.STATIC_DRAW,\r\n );\r\n gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);\r\n gl.enableVertexAttribArray(0);\r\n\r\n return (target: FBO | null, doClear = false) => {\r\n if (!gl) return;\r\n if (!target) {\r\n gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);\r\n gl.bindFramebuffer(gl.FRAMEBUFFER, null);\r\n } else {\r\n gl.viewport(0, 0, target.width, target.height);\r\n gl.bindFramebuffer(gl.FRAMEBUFFER, target.fbo);\r\n }\r\n if (doClear) {\r\n gl.clearColor(0, 0, 0, 1);\r\n gl.clear(gl.COLOR_BUFFER_BIT);\r\n }\r\n gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);\r\n };\r\n })();\r\n\r\n // Types for Framebuffers\r\n interface FBO {\r\n texture: WebGLTexture;\r\n fbo: WebGLFramebuffer;\r\n width: number;\r\n height: number;\r\n texelSizeX: number;\r\n texelSizeY: number;\r\n attach: (id: number) => number;\r\n }\r\n\r\n interface DoubleFBO {\r\n width: number;\r\n height: number;\r\n texelSizeX: number;\r\n texelSizeY: number;\r\n read: FBO;\r\n write: FBO;\r\n swap: () => void;\r\n }\r\n\r\n // FBO variables\r\n let dye: DoubleFBO;\r\n let velocity: DoubleFBO;\r\n let divergence: FBO;\r\n let curl: FBO;\r\n let pressure: DoubleFBO;\r\n\r\n // WebGL Programs\r\n const copyProgram = new Program(baseVertexShader, copyShader);\r\n const clearProgram = new Program(baseVertexShader, clearShader);\r\n const splatProgram = new Program(baseVertexShader, splatShader);\r\n const advectionProgram = new Program(baseVertexShader, advectionShader);\r\n const divergenceProgram = new Program(baseVertexShader, divergenceShader);\r\n const curlProgram = new Program(baseVertexShader, curlShader);\r\n const vorticityProgram = new Program(baseVertexShader, vorticityShader);\r\n const pressureProgram = new Program(baseVertexShader, pressureShader);\r\n const gradienSubtractProgram = new Program(\r\n baseVertexShader,\r\n gradientSubtractShader,\r\n );\r\n const displayMaterial = new Material(baseVertexShader, displayShaderSource);\r\n\r\n // -------------------- FBO creation --------------------\r\n function createFBO(\r\n w: number,\r\n h: number,\r\n internalFormat: number,\r\n format: number,\r\n type: number,\r\n param: number,\r\n ): FBO {\r\n gl.activeTexture(gl.TEXTURE0);\r\n const texture = gl.createTexture()!;\r\n gl.bindTexture(gl.TEXTURE_2D, texture);\r\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, param);\r\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, param);\r\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\r\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\r\n gl.texImage2D(\r\n gl.TEXTURE_2D,\r\n 0,\r\n internalFormat,\r\n w,\r\n h,\r\n 0,\r\n format,\r\n type,\r\n null,\r\n );\r\n const fbo = gl.createFramebuffer()!;\r\n gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);\r\n gl.framebufferTexture2D(\r\n gl.FRAMEBUFFER,\r\n gl.COLOR_ATTACHMENT0,\r\n gl.TEXTURE_2D,\r\n texture,\r\n 0,\r\n );\r\n gl.viewport(0, 0, w, h);\r\n gl.clear(gl.COLOR_BUFFER_BIT);\r\n\r\n const texelSizeX = 1 / w;\r\n const texelSizeY = 1 / h;\r\n\r\n return {\r\n texture,\r\n fbo,\r\n width: w,\r\n height: h,\r\n texelSizeX,\r\n texelSizeY,\r\n attach(id: number) {\r\n gl.activeTexture(gl.TEXTURE0 + id);\r\n gl.bindTexture(gl.TEXTURE_2D, texture);\r\n return id;\r\n },\r\n };\r\n }\r\n\r\n function createDoubleFBO(\r\n w: number,\r\n h: number,\r\n internalFormat: number,\r\n format: number,\r\n type: number,\r\n param: number,\r\n ): DoubleFBO {\r\n const fbo1 = createFBO(w, h, internalFormat, format, type, param);\r\n const fbo2 = createFBO(w, h, internalFormat, format, type, param);\r\n return {\r\n width: w,\r\n height: h,\r\n texelSizeX: fbo1.texelSizeX,\r\n texelSizeY: fbo1.texelSizeY,\r\n read: fbo1,\r\n write: fbo2,\r\n swap() {\r\n const tmp = this.read;\r\n this.read = this.write;\r\n this.write = tmp;\r\n },\r\n };\r\n }\r\n\r\n function resizeFBO(\r\n target: FBO,\r\n w: number,\r\n h: number,\r\n internalFormat: number,\r\n format: number,\r\n type: number,\r\n param: number,\r\n ) {\r\n const newFBO = createFBO(w, h, internalFormat, format, type, param);\r\n copyProgram.bind();\r\n if (copyProgram.uniforms.uTexture)\r\n gl.uniform1i(copyProgram.uniforms.uTexture, target.attach(0));\r\n blit(newFBO, false);\r\n return newFBO;\r\n }\r\n\r\n function resizeDoubleFBO(\r\n target: DoubleFBO,\r\n w: number,\r\n h: number,\r\n internalFormat: number,\r\n format: number,\r\n type: number,\r\n param: number,\r\n ) {\r\n if (target.width === w && target.height === h) return target;\r\n target.read = resizeFBO(\r\n target.read,\r\n w,\r\n h,\r\n internalFormat,\r\n format,\r\n type,\r\n param,\r\n );\r\n target.write = createFBO(w, h, internalFormat, format, type, param);\r\n target.width = w;\r\n target.height = h;\r\n target.texelSizeX = 1 / w;\r\n target.texelSizeY = 1 / h;\r\n return target;\r\n }\r\n\r\n function initFramebuffers() {\r\n const simRes = getResolution(config.SIM_RESOLUTION!);\r\n const dyeRes = getResolution(config.DYE_RESOLUTION!);\r\n\r\n const texType = ext.halfFloatTexType;\r\n const rgba = ext.formatRGBA;\r\n const rg = ext.formatRG;\r\n const r = ext.formatR;\r\n const filtering = ext.supportLinearFiltering ? gl.LINEAR : gl.NEAREST;\r\n gl.disable(gl.BLEND);\r\n\r\n if (!rgba) {\r\n throw new Error(\"Required RGBA format is not supported by WebGL.\");\r\n }\r\n if (!dye) {\r\n dye = createDoubleFBO(\r\n dyeRes.width,\r\n dyeRes.height,\r\n rgba.internalFormat,\r\n rgba.format,\r\n texType,\r\n filtering,\r\n );\r\n } else {\r\n dye = resizeDoubleFBO(\r\n dye,\r\n dyeRes.width,\r\n dyeRes.height,\r\n rgba.internalFormat,\r\n rgba.format,\r\n texType,\r\n filtering,\r\n );\r\n }\r\n\r\n if (!rg) {\r\n throw new Error(\"Required RG format is not supported by WebGL.\");\r\n }\r\n if (!velocity) {\r\n velocity = createDoubleFBO(\r\n simRes.width,\r\n simRes.height,\r\n rg.internalFormat,\r\n rg.format,\r\n texType,\r\n filtering,\r\n );\r\n } else {\r\n velocity = resizeDoubleFBO(\r\n velocity,\r\n simRes.width,\r\n simRes.height,\r\n rg.internalFormat,\r\n rg.format,\r\n texType,\r\n filtering,\r\n );\r\n }\r\n\r\n if (!r) {\r\n throw new Error(\"Required R format is not supported by WebGL.\");\r\n }\r\n divergence = createFBO(\r\n simRes.width,\r\n simRes.height,\r\n r.internalFormat,\r\n r.format,\r\n texType,\r\n gl.NEAREST,\r\n );\r\n curl = createFBO(\r\n simRes.width,\r\n simRes.height,\r\n r.internalFormat,\r\n r.format,\r\n texType,\r\n gl.NEAREST,\r\n );\r\n pressure = createDoubleFBO(\r\n simRes.width,\r\n simRes.height,\r\n r.internalFormat,\r\n r.format,\r\n texType,\r\n gl.NEAREST,\r\n );\r\n }\r\n\r\n function updateKeywords() {\r\n const displayKeywords: string[] = [];\r\n if (config.SHADING) displayKeywords.push(\"SHADING\");\r\n displayMaterial.setKeywords(displayKeywords);\r\n }\r\n\r\n function getResolution(resolution: number) {\r\n const w = gl.drawingBufferWidth;\r\n const h = gl.drawingBufferHeight;\r\n const aspectRatio = w / h;\r\n const aspect = aspectRatio < 1 ? 1 / aspectRatio : aspectRatio;\r\n const min = Math.round(resolution);\r\n const max = Math.round(resolution * aspect);\r\n if (w > h) {\r\n return { width: max, height: min };\r\n }\r\n return { width: min, height: max };\r\n }\r\n\r\n function scaleByPixelRatio(input: number) {\r\n const pixelRatio = window.devicePixelRatio || 1;\r\n return Math.floor(input * pixelRatio);\r\n }\r\n\r\n // -------------------- Simulation Setup --------------------\r\n updateKeywords();\r\n initFramebuffers();\r\n\r\n let lastUpdateTime = Date.now();\r\n let colorUpdateTimer = 0.0;\r\n\r\n function updateFrame() {\r\n const dt = calcDeltaTime();\r\n if (resizeCanvas()) initFramebuffers();\r\n updateColors(dt);\r\n applyInputs();\r\n step(dt);\r\n render(null);\r\n requestAnimationFrame(updateFrame);\r\n }\r\n\r\n function calcDeltaTime() {\r\n const now = Date.now();\r\n let dt = (now - lastUpdateTime) / 1000;\r\n dt = Math.min(dt, 0.016666);\r\n lastUpdateTime = now;\r\n return dt;\r\n }\r\n\r\n function resizeCanvas() {\r\n const width = scaleByPixelRatio(canvas!.clientWidth);\r\n const height = scaleByPixelRatio(canvas!.clientHeight);\r\n if (canvas!.width !== width || canvas!.height !== height) {\r\n canvas!.width = width;\r\n canvas!.height = height;\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n function updateColors(dt: number) {\r\n colorUpdateTimer += dt * config.COLOR_UPDATE_SPEED;\r\n if (colorUpdateTimer >= 1) {\r\n colorUpdateTimer = wrap(colorUpdateTimer, 0, 1);\r\n pointers.forEach((p) => {\r\n p.color = generateColor();\r\n });\r\n }\r\n }\r\n\r\n function applyInputs() {\r\n for (const p of pointers) {\r\n if (p.moved) {\r\n p.moved = false;\r\n splatPointer(p);\r\n }\r\n }\r\n }\r\n\r\n function step(dt: number) {\r\n gl.disable(gl.BLEND);\r\n\r\n // Curl\r\n curlProgram.bind();\r\n if (curlProgram.uniforms.texelSize) {\r\n gl.uniform2f(\r\n curlProgram.uniforms.texelSize,\r\n velocity.texelSizeX,\r\n velocity.texelSizeY,\r\n );\r\n }\r\n if (curlProgram.uniforms.uVelocity) {\r\n gl.uniform1i(curlProgram.uniforms.uVelocity, velocity.read.attach(0));\r\n }\r\n blit(curl);\r\n\r\n // Vorticity\r\n vorticityProgram.bind();\r\n if (vorticityProgram.uniforms.texelSize) {\r\n gl.uniform2f(\r\n vorticityProgram.uniforms.texelSize,\r\n velocity.texelSizeX,\r\n velocity.texelSizeY,\r\n );\r\n }\r\n if (vorticityProgram.uniforms.uVelocity) {\r\n gl.uniform1i(\r\n vorticityProgram.uniforms.uVelocity,\r\n velocity.read.attach(0),\r\n );\r\n }\r\n if (vorticityProgram.uniforms.uCurl) {\r\n gl.uniform1i(vorticityProgram.uniforms.uCurl, curl.attach(1));\r\n }\r\n if (vorticityProgram.uniforms.curl) {\r\n gl.uniform1f(vorticityProgram.uniforms.curl, config.CURL);\r\n }\r\n if (vorticityProgram.uniforms.dt) {\r\n gl.uniform1f(vorticityProgram.uniforms.dt, dt);\r\n }\r\n blit(velocity.write);\r\n velocity.swap();\r\n\r\n // Divergence\r\n divergenceProgram.bind();\r\n if (divergenceProgram.uniforms.texelSize) {\r\n gl.uniform2f(\r\n divergenceProgram.uniforms.texelSize,\r\n velocity.texelSizeX,\r\n velocity.texelSizeY,\r\n );\r\n }\r\n if (divergenceProgram.uniforms.uVelocity) {\r\n gl.uniform1i(\r\n divergenceProgram.uniforms.uVelocity,\r\n velocity.read.attach(0),\r\n );\r\n }\r\n blit(divergence);\r\n\r\n // Clear pressure\r\n clearProgram.bind();\r\n if (clearProgram.uniforms.uTexture) {\r\n gl.uniform1i(clearProgram.uniforms.uTexture, pressure.read.attach(0));\r\n }\r\n if (clearProgram.uniforms.value) {\r\n gl.uniform1f(clearProgram.uniforms.value, config.PRESSURE);\r\n }\r\n blit(pressure.write);\r\n pressure.swap();\r\n\r\n // Pressure\r\n pressureProgram.bind();\r\n if (pressureProgram.uniforms.texelSize) {\r\n gl.uniform2f(\r\n pressureProgram.uniforms.texelSize,\r\n velocity.texelSizeX,\r\n velocity.texelSizeY,\r\n );\r\n }\r\n if (pressureProgram.uniforms.uDivergence) {\r\n gl.uniform1i(\r\n pressureProgram.uniforms.uDivergence,\r\n divergence.attach(0),\r\n );\r\n }\r\n for (let i = 0; i < config.PRESSURE_ITERATIONS; i++) {\r\n if (pressureProgram.uniforms.uPressure) {\r\n gl.uniform1i(\r\n pressureProgram.uniforms.uPressure,\r\n pressure.read.attach(1),\r\n );\r\n }\r\n blit(pressure.write);\r\n pressure.swap();\r\n }\r\n\r\n // Gradient Subtract\r\n gradienSubtractProgram.bind();\r\n if (gradienSubtractProgram.uniforms.texelSize) {\r\n gl.uniform2f(\r\n gradienSubtractProgram.uniforms.texelSize,\r\n velocity.texelSizeX,\r\n velocity.texelSizeY,\r\n );\r\n }\r\n if (gradienSubtractProgram.uniforms.uPressure) {\r\n gl.uniform1i(\r\n gradienSubtractProgram.uniforms.uPressure,\r\n pressure.read.attach(0),\r\n );\r\n }\r\n if (gradienSubtractProgram.uniforms.uVelocity) {\r\n gl.uniform1i(\r\n gradienSubtractProgram.uniforms.uVelocity,\r\n velocity.read.attach(1),\r\n );\r\n }\r\n blit(velocity.write);\r\n velocity.swap();\r\n\r\n // Advection - velocity\r\n advectionProgram.bind();\r\n if (advectionProgram.uniforms.texelSize) {\r\n gl.uniform2f(\r\n advectionProgram.uniforms.texelSize,\r\n velocity.texelSizeX,\r\n velocity.texelSizeY,\r\n );\r\n }\r\n if (\r\n !ext.supportLinearFiltering &&\r\n advectionProgram.uniforms.dyeTexelSize\r\n ) {\r\n gl.uniform2f(\r\n advectionProgram.uniforms.dyeTexelSize,\r\n velocity.texelSizeX,\r\n velocity.texelSizeY,\r\n );\r\n }\r\n const velocityId = velocity.read.attach(0);\r\n if (advectionProgram.uniforms.uVelocity) {\r\n gl.uniform1i(advectionProgram.uniforms.uVelocity, velocityId);\r\n }\r\n if (advectionProgram.uniforms.uSource) {\r\n gl.uniform1i(advectionProgram.uniforms.uSource, velocityId);\r\n }\r\n if (advectionProgram.uniforms.dt) {\r\n gl.uniform1f(advectionProgram.uniforms.dt, dt);\r\n }\r\n if (advectionProgram.uniforms.dissipation) {\r\n gl.uniform1f(\r\n advectionProgram.uniforms.dissipation,\r\n config.VELOCITY_DISSIPATION,\r\n );\r\n }\r\n blit(velocity.write);\r\n velocity.swap();\r\n\r\n // Advection - dye\r\n if (\r\n !ext.supportLinearFiltering &&\r\n advectionProgram.uniforms.dyeTexelSize\r\n ) {\r\n gl.uniform2f(\r\n advectionProgram.uniforms.dyeTexelSize,\r\n dye.texelSizeX,\r\n dye.texelSizeY,\r\n );\r\n }\r\n if (advectionProgram.uniforms.uVelocity) {\r\n gl.uniform1i(\r\n advectionProgram.uniforms.uVelocity,\r\n velocity.read.attach(0),\r\n );\r\n }\r\n if (advectionProgram.uniforms.uSource) {\r\n gl.uniform1i(advectionProgram.uniforms.uSource, dye.read.attach(1));\r\n }\r\n if (advectionProgram.uniforms.dissipation) {\r\n gl.uniform1f(\r\n advectionProgram.uniforms.dissipation,\r\n config.DENSITY_DISSIPATION,\r\n );\r\n }\r\n blit(dye.write);\r\n dye.swap();\r\n }\r\n\r\n function render(target: FBO | null) {\r\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\r\n gl.enable(gl.BLEND);\r\n drawDisplay(target);\r\n }\r\n\r\n function drawDisplay(target: FBO | null) {\r\n const width = target ? target.width : gl.drawingBufferWidth;\r\n const height = target ? target.height : gl.drawingBufferHeight;\r\n displayMaterial.bind();\r\n if (config.SHADING && displayMaterial.uniforms.texelSize) {\r\n gl.uniform2f(displayMaterial.uniforms.texelSize, 1 / width, 1 / height);\r\n }\r\n if (displayMaterial.uniforms.uTexture) {\r\n gl.uniform1i(displayMaterial.uniforms.uTexture, dye.read.attach(0));\r\n }\r\n blit(target, false);\r\n }\r\n\r\n // -------------------- Interaction --------------------\r\n function splatPointer(pointer: Pointer) {\r\n const dx = pointer.deltaX * config.SPLAT_FORCE;\r\n const dy = pointer.deltaY * config.SPLAT_FORCE;\r\n splat(pointer.texcoordX, pointer.texcoordY, dx, dy, pointer.color);\r\n }\r\n\r\n function clickSplat(pointer: Pointer) {\r\n const color = generateColor();\r\n color.r *= 10;\r\n color.g *= 10;\r\n color.b *= 10;\r\n const dx = 10 * (Math.random() - 0.5);\r\n const dy = 30 * (Math.random() - 0.5);\r\n splat(pointer.texcoordX, pointer.texcoordY, dx, dy, color);\r\n }\r\n\r\n function splat(\r\n x: number,\r\n y: number,\r\n dx: number,\r\n dy: number,\r\n color: ColorRGB,\r\n ) {\r\n splatProgram.bind();\r\n if (splatProgram.uniforms.uTarget) {\r\n gl.uniform1i(splatProgram.uniforms.uTarget, velocity.read.attach(0));\r\n }\r\n if (splatProgram.uniforms.aspectRatio) {\r\n gl.uniform1f(\r\n splatProgram.uniforms.aspectRatio,\r\n canvas!.width / canvas!.height,\r\n );\r\n }\r\n if (splatProgram.uniforms.point) {\r\n gl.uniform2f(splatProgram.uniforms.point, x, y);\r\n }\r\n if (splatProgram.uniforms.color) {\r\n gl.uniform3f(splatProgram.uniforms.color, dx, dy, 0);\r\n }\r\n if (splatProgram.uniforms.radius) {\r\n gl.uniform1f(\r\n splatProgram.uniforms.radius,\r\n correctRadius(config.SPLAT_RADIUS / 100)!,\r\n );\r\n }\r\n blit(velocity.write);\r\n velocity.swap();\r\n\r\n if (splatProgram.uniforms.uTarget) {\r\n gl.uniform1i(splatProgram.uniforms.uTarget, dye.read.attach(0));\r\n }\r\n if (splatProgram.uniforms.color) {\r\n gl.uniform3f(splatProgram.uniforms.color, color.r, color.g, color.b);\r\n }\r\n blit(dye.write);\r\n dye.swap();\r\n }\r\n\r\n function correctRadius(radius: number) {\r\n // Use non-null assertion (canvas can't be null here)\r\n const aspectRatio = canvas!.width / canvas!.height;\r\n if (aspectRatio > 1) radius *= aspectRatio;\r\n return radius;\r\n }\r\n\r\n function updatePointerDownData(\r\n pointer: Pointer,\r\n id: number,\r\n posX: number,\r\n posY: number,\r\n ) {\r\n pointer.id = id;\r\n pointer.down = true;\r\n pointer.moved = false;\r\n pointer.texcoordX = posX / canvas!.width;\r\n pointer.texcoordY = 1 - posY / canvas!.height;\r\n pointer.prevTexcoordX = pointer.texcoordX;\r\n pointer.prevTexcoordY = pointer.texcoordY;\r\n pointer.deltaX = 0;\r\n pointer.deltaY = 0;\r\n pointer.color = generateColor();\r\n }\r\n\r\n function updatePointerMoveData(\r\n pointer: Pointer,\r\n posX: number,\r\n posY: number,\r\n color: ColorRGB,\r\n ) {\r\n pointer.prevTexcoordX = pointer.texcoordX;\r\n pointer.prevTexcoordY = pointer.texcoordY;\r\n pointer.texcoordX = posX / canvas!.width;\r\n pointer.texcoordY = 1 - posY / canvas!.height;\r\n pointer.deltaX = correctDeltaX(\r\n pointer.texcoordX - pointer.prevTexcoordX,\r\n )!;\r\n pointer.deltaY = correctDeltaY(\r\n pointer.texcoordY - pointer.prevTexcoordY,\r\n )!;\r\n pointer.moved =\r\n Math.abs(pointer.deltaX) > 0 || Math.abs(pointer.deltaY) > 0;\r\n pointer.color = color;\r\n }\r\n\r\n function updatePointerUpData(pointer: Pointer) {\r\n pointer.down = false;\r\n }\r\n\r\n function correctDeltaX(delta: number) {\r\n const aspectRatio = canvas!.width / canvas!.height;\r\n if (aspectRatio < 1) delta *= aspectRatio;\r\n return delta;\r\n }\r\n\r\n function correctDeltaY(delta: number) {\r\n const aspectRatio = canvas!.width / canvas!.height;\r\n if (aspectRatio > 1) delta /= aspectRatio;\r\n return delta;\r\n }\r\n\r\n function generateColor(): ColorRGB {\r\n const c = HSVtoRGB(Math.random(), 1.0, 1.0);\r\n c.r *= 0.15;\r\n c.g *= 0.15;\r\n c.b *= 0.15;\r\n return c;\r\n }\r\n\r\n function HSVtoRGB(h: number, s: number, v: number): ColorRGB {\r\n let r = 0,\r\n g = 0,\r\n b = 0;\r\n const i = Math.floor(h * 6);\r\n const f = h * 6 - i;\r\n const p = v * (1 - s);\r\n const q = v * (1 - f * s);\r\n const t = v * (1 - (1 - f) * s);\r\n\r\n switch (i % 6) {\r\n case 0:\r\n r = v;\r\n g = t;\r\n b = p;\r\n break;\r\n case 1:\r\n r = q;\r\n g = v;\r\n b = p;\r\n break;\r\n case 2:\r\n r = p;\r\n g = v;\r\n b = t;\r\n break;\r\n case 3:\r\n r = p;\r\n g = q;\r\n b = v;\r\n break;\r\n case 4:\r\n r = t;\r\n g = p;\r\n b = v;\r\n break;\r\n case 5:\r\n r = v;\r\n g = p;\r\n b = q;\r\n break;\r\n }\r\n return { r, g, b };\r\n }\r\n\r\n function wrap(value: number, min: number, max: number) {\r\n const range = max - min;\r\n if (range === 0) return min;\r\n return ((value - min) % range) + min;\r\n }\r\n\r\n // -------------------- Event Listeners --------------------\r\n window.addEventListener(\"mousedown\", (e) => {\r\n const pointer = pointers[0];\r\n const posX = scaleByPixelRatio(e.clientX);\r\n const posY = scaleByPixelRatio(e.clientY);\r\n updatePointerDownData(pointer, -1, posX, posY);\r\n clickSplat(pointer);\r\n });\r\n\r\n // Start rendering on first mouse move\r\n function handleFirstMouseMove(e: MouseEvent) {\r\n const pointer = pointers[0];\r\n const posX = scaleByPixelRatio(e.clientX);\r\n const posY = scaleByPixelRatio(e.clientY);\r\n const color = generateColor();\r\n updateFrame();\r\n updatePointerMoveData(pointer, posX, posY, color);\r\n document.body.removeEventListener(\"mousemove\", handleFirstMouseMove);\r\n }\r\n document.body.addEventListener(\"mousemove\", handleFirstMouseMove);\r\n\r\n window.addEventListener(\"mousemove\", (e) => {\r\n const pointer = pointers[0];\r\n const posX = scaleByPixelRatio(e.clientX);\r\n const posY = scaleByPixelRatio(e.clientY);\r\n const color = pointer.color;\r\n updatePointerMoveData(pointer, posX, posY, color);\r\n });\r\n\r\n // Start rendering on first touch\r\n function handleFirstTouchStart(e: TouchEvent) {\r\n const touches = e.targetTouches;\r\n const pointer = pointers[0];\r\n for (let i = 0; i < touches.length; i++) {\r\n const posX = scaleByPixelRatio(touches[i].clientX);\r\n const posY = scaleByPixelRatio(touches[i].clientY);\r\n updateFrame();\r\n updatePointerDownData(pointer, touches[i].identifier, posX, posY);\r\n }\r\n document.body.removeEventListener(\"touchstart\", handleFirstTouchStart);\r\n }\r\n document.body.addEventListener(\"touchstart\", handleFirstTouchStart);\r\n\r\n window.addEventListener(\r\n \"touchstart\",\r\n (e) => {\r\n const touches = e.targetTouches;\r\n const pointer = pointers[0];\r\n for (let i = 0; i < touches.length; i++) {\r\n const posX = scaleByPixelRatio(touches[i].clientX);\r\n const posY = scaleByPixelRatio(touches[i].clientY);\r\n updatePointerDownData(pointer, touches[i].identifier, posX, posY);\r\n }\r\n },\r\n false,\r\n );\r\n\r\n window.addEventListener(\r\n \"touchmove\",\r\n (e) => {\r\n const touches = e.targetTouches;\r\n const pointer = pointers[0];\r\n for (let i = 0; i < touches.length; i++) {\r\n const posX = scaleByPixelRatio(touches[i].clientX);\r\n const posY = scaleByPixelRatio(touches[i].clientY);\r\n updatePointerMoveData(pointer, posX, posY, pointer.color);\r\n }\r\n },\r\n false,\r\n );\r\n\r\n window.addEventListener(\"touchend\", (e) => {\r\n const touches = e.changedTouches;\r\n const pointer = pointers[0];\r\n for (let i = 0; i < touches.length; i++) {\r\n updatePointerUpData(pointer);\r\n }\r\n });\r\n // ------------------------------------------------------------\r\n }, [\r\n SIM_RESOLUTION,\r\n DYE_RESOLUTION,\r\n CAPTURE_RESOLUTION,\r\n DENSITY_DISSIPATION,\r\n VELOCITY_DISSIPATION,\r\n PRESSURE,\r\n PRESSURE_ITERATIONS,\r\n CURL,\r\n SPLAT_RADIUS,\r\n SPLAT_FORCE,\r\n SHADING,\r\n COLOR_UPDATE_SPEED,\r\n BACK_COLOR,\r\n TRANSPARENT,\r\n ]);\r\n\r\n return (\r\n
\r\n \r\n
\r\n );\r\n}\r\n", "type": "registry:component" } ] diff --git a/public/r/spotlight-card.json b/public/r/spotlight-card.json index eb9e380..6e20f00 100644 --- a/public/r/spotlight-card.json +++ b/public/r/spotlight-card.json @@ -8,12 +8,12 @@ "files": [ { "path": "./src/components/nurui/spotlight-card-demo.tsx", - "content": "import { GlowCard } from \"@/components/nurui/spotlight-card\";\n\nexport function SpotLightCardDemo() {\n return (\n
\n \n \n \n
\n );\n}\n", + "content": "import { GlowCard } from \"@/components/nurui/spotlight-card\";\r\n\r\nexport function SpotLightCardDemo() {\r\n return (\r\n
\r\n \r\n \r\n \r\n
\r\n );\r\n}\r\n", "type": "registry:component" }, { "path": "./src/components/nurui/spotlight-card.tsx", - "content": "'use client';\nimport React, { useEffect, useRef, ReactNode } from \"react\";\n\ninterface GlowCardProps {\n children?: ReactNode;\n className?: string;\n glowColor?: \"blue\" | \"purple\" | \"green\" | \"red\" | \"orange\";\n size?: \"sm\" | \"md\" | \"lg\";\n width?: string | number;\n height?: string | number;\n customSize?: boolean; // When true, ignores size prop and uses width/height or className\n}\n\nconst glowColorMap = {\n blue: { base: 220, spread: 200 },\n purple: { base: 280, spread: 300 },\n green: { base: 120, spread: 200 },\n red: { base: 0, spread: 200 },\n orange: { base: 30, spread: 200 },\n};\n\nconst sizeMap = {\n sm: \"w-48 h-64\",\n md: \"w-64 h-80\",\n lg: \"w-80 h-96\",\n};\n\nconst GlowCard: React.FC = ({\n children,\n className = \"\",\n glowColor = \"blue\",\n size = \"md\",\n width,\n height,\n customSize = false,\n}) => {\n const cardRef = useRef(null);\n const innerRef = useRef(null);\n\n useEffect(() => {\n const syncPointer = (e: PointerEvent) => {\n const { clientX: x, clientY: y } = e;\n\n if (cardRef.current) {\n cardRef.current.style.setProperty(\"--x\", x.toFixed(2));\n cardRef.current.style.setProperty(\n \"--xp\",\n (x / window.innerWidth).toFixed(2),\n );\n cardRef.current.style.setProperty(\"--y\", y.toFixed(2));\n cardRef.current.style.setProperty(\n \"--yp\",\n (y / window.innerHeight).toFixed(2),\n );\n }\n };\n\n document.addEventListener(\"pointermove\", syncPointer);\n return () => document.removeEventListener(\"pointermove\", syncPointer);\n }, []);\n\n const { base, spread } = glowColorMap[glowColor];\n\n // Determine sizing\n const getSizeClasses = () => {\n if (customSize) {\n return \"\"; // Let className or inline styles handle sizing\n }\n return sizeMap[size];\n };\n\n const getInlineStyles = (): React.CSSProperties => {\n const baseStyles: React.CSSProperties & {\n [key: string]: string | number | undefined;\n } = {\n \"--base\": base,\n \"--spread\": spread,\n \"--radius\": \"14\",\n \"--border\": \"3\",\n \"--backdrop\": \"hsl(0 0% 60% / 0.12)\",\n \"--backup-border\": \"var(--backdrop)\",\n \"--size\": \"200\",\n \"--outer\": \"1\",\n \"--border-size\": \"calc(var(--border, 2) * 1px)\",\n \"--spotlight-size\": \"calc(var(--size, 150) * 1px)\",\n \"--hue\": \"calc(var(--base) + (var(--xp, 0) * var(--spread, 0)))\",\n backgroundImage: `radial-gradient(\n var(--spotlight-size) var(--spotlight-size) at\n calc(var(--x, 0) * 1px)\n calc(var(--y, 0) * 1px),\n hsl(var(--hue, 210) calc(var(--saturation, 100) * 1%) calc(var(--lightness, 70) * 1%) / var(--bg-spot-opacity, 0.1)), transparent\n )`,\n backgroundColor: \"var(--backdrop, transparent)\",\n backgroundSize:\n \"calc(100% + (2 * var(--border-size))) calc(100% + (2 * var(--border-size)))\",\n backgroundPosition: \"50% 50%\",\n backgroundAttachment: \"fixed\",\n border: \"var(--border-size) solid var(--backup-border)\",\n position: \"relative\",\n touchAction: \"none\",\n };\n\n // Add width and height if provided\n if (width !== undefined) {\n baseStyles.width = typeof width === \"number\" ? `${width}px` : width;\n }\n if (height !== undefined) {\n baseStyles.height = typeof height === \"number\" ? `${height}px` : height;\n }\n\n return baseStyles;\n };\n\n const beforeAfterStyles = `\n [data-glow]::before,\n [data-glow]::after {\n pointer-events: none;\n content: \"\";\n position: absolute;\n inset: calc(var(--border-size) * -1);\n border: var(--border-size) solid transparent;\n border-radius: calc(var(--radius) * 1px);\n background-attachment: fixed;\n background-size: calc(100% + (2 * var(--border-size))) calc(100% + (2 * var(--border-size)));\n background-repeat: no-repeat;\n background-position: 50% 50%;\n mask: linear-gradient(transparent, transparent), linear-gradient(white, white);\n mask-clip: padding-box, border-box;\n mask-composite: intersect;\n }\n \n [data-glow]::before {\n background-image: radial-gradient(\n calc(var(--spotlight-size) * 0.75) calc(var(--spotlight-size) * 0.75) at\n calc(var(--x, 0) * 1px)\n calc(var(--y, 0) * 1px),\n hsl(var(--hue, 210) calc(var(--saturation, 100) * 1%) calc(var(--lightness, 50) * 1%) / var(--border-spot-opacity, 1)), transparent 100%\n );\n filter: brightness(2);\n }\n \n [data-glow]::after {\n background-image: radial-gradient(\n calc(var(--spotlight-size) * 0.5) calc(var(--spotlight-size) * 0.5) at\n calc(var(--x, 0) * 1px)\n calc(var(--y, 0) * 1px),\n hsl(0 100% 100% / var(--border-light-opacity, 1)), transparent 100%\n );\n }\n \n [data-glow] [data-glow] {\n position: absolute;\n inset: 0;\n will-change: filter;\n opacity: var(--outer, 1);\n border-radius: calc(var(--radius) * 1px);\n border-width: calc(var(--border-size) * 20);\n filter: blur(calc(var(--border-size) * 10));\n background: none;\n pointer-events: none;\n border: none;\n }\n \n [data-glow] > [data-glow]::before {\n inset: -10px;\n border-width: 10px;\n }\n `;\n\n return (\n <>\n