From 23f03028678f3ec5c53153d5e06cc2b6e882e2fc Mon Sep 17 00:00:00 2001 From: Lin Junrong Date: Fri, 31 Jul 2026 14:14:35 +0800 Subject: [PATCH 1/2] fix(registry): import types with type-only specifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Registry components import type-only bindings (Transition, Variants, MotionStyle, HTMLMotionProps, CSSProperties, ReactNode, VariantProps, ...) as value imports. TypeScript's own `--template react-ts` scaffold turns on `verbatimModuleSyntax`, so every one of these is a hard TS1484 compile error the moment someone installs a component into a stock Vite app. The generated payloads under public/r embed the component source verbatim, so the registry was rebuilt too — that is what the CLI actually writes into a user's project. 43 specifiers across 33 files, plus the regenerated registry output. Adds the inline `type` modifier, matching the style already used in the same import statements (spinning-text.tsx already writes `type ComponentPropsWithoutRef` one line above). No runtime or type semantics change. --- .../(marketing)/showcase/[...slug]/page.tsx | 2 +- apps/www/app/layout.tsx | 2 +- apps/www/app/robots.ts | 2 +- apps/www/components/blur-image.tsx | 2 +- apps/www/components/contribute.tsx | 2 +- apps/www/components/copy-button.tsx | 2 +- apps/www/components/docs-sidebar.tsx | 2 +- apps/www/components/main-nav.tsx | 2 +- apps/www/components/mobile-nav.tsx | 2 +- apps/www/components/template-preview.tsx | 2 +- apps/www/components/theme-provider.tsx | 2 +- apps/www/components/ui/sidebar.tsx | 2 +- apps/www/config/docs.ts | 2 +- apps/www/lib/rehype-component.ts | 2 +- apps/www/lib/rehype-npm-command.ts | 2 +- apps/www/lib/utils.ts | 2 +- apps/www/public/llms-full.txt | 44 ++++++++++++------- apps/www/public/r/android.json | 2 +- apps/www/public/r/border-beam.json | 2 +- apps/www/public/r/client-tweet-card.json | 2 +- apps/www/public/r/comic-text.json | 2 +- apps/www/public/r/cool-mode.json | 2 +- apps/www/public/r/dock.json | 2 +- apps/www/public/r/neon-gradient-card.json | 2 +- apps/www/public/r/rainbow-button.json | 2 +- apps/www/public/r/ripple-button.json | 2 +- apps/www/public/r/smooth-cursor.json | 2 +- apps/www/public/r/sparkles-text.json | 2 +- apps/www/public/r/spinning-text.json | 2 +- apps/www/public/r/text-animate.json | 2 +- apps/www/public/r/video-text.json | 2 +- apps/www/public/r/warp-background.json | 2 +- apps/www/registry/index.ts | 6 ++- apps/www/registry/magicui/android.tsx | 2 +- .../magicui/animated-subscribe-button.tsx | 2 +- apps/www/registry/magicui/border-beam.tsx | 2 +- .../registry/magicui/client-tweet-card.tsx | 2 +- apps/www/registry/magicui/comic-text.tsx | 2 +- apps/www/registry/magicui/cool-mode.tsx | 2 +- apps/www/registry/magicui/dock.tsx | 2 +- .../registry/magicui/neon-gradient-card.tsx | 6 +-- apps/www/registry/magicui/rainbow-button.tsx | 2 +- apps/www/registry/magicui/ripple-button.tsx | 2 +- apps/www/registry/magicui/smooth-cursor.tsx | 2 +- apps/www/registry/magicui/sparkles-text.tsx | 7 ++- apps/www/registry/magicui/spinning-text.tsx | 2 +- apps/www/registry/magicui/text-animate.tsx | 2 +- apps/www/registry/magicui/video-text.tsx | 7 ++- apps/www/registry/magicui/warp-background.tsx | 2 +- 49 files changed, 91 insertions(+), 67 deletions(-) diff --git a/apps/www/app/(marketing)/showcase/[...slug]/page.tsx b/apps/www/app/(marketing)/showcase/[...slug]/page.tsx index 68ca1e6b0..7409a117c 100644 --- a/apps/www/app/(marketing)/showcase/[...slug]/page.tsx +++ b/apps/www/app/(marketing)/showcase/[...slug]/page.tsx @@ -1,4 +1,4 @@ -import { Metadata } from "next" +import { type Metadata } from "next" import { notFound } from "next/navigation" import { showcaseSource } from "@/lib/source" diff --git a/apps/www/app/layout.tsx b/apps/www/app/layout.tsx index c8b87fc26..e9f5421ed 100644 --- a/apps/www/app/layout.tsx +++ b/apps/www/app/layout.tsx @@ -1,5 +1,5 @@ import type { Viewport } from "next" -import { Metadata } from "next" +import { type Metadata } from "next" import { Provider as JotaiProvider } from "jotai" import { fontVariables } from "@/lib/fonts" diff --git a/apps/www/app/robots.ts b/apps/www/app/robots.ts index d0ec3f85a..e7fdb4eff 100644 --- a/apps/www/app/robots.ts +++ b/apps/www/app/robots.ts @@ -1,4 +1,4 @@ -import { MetadataRoute } from "next" +import { type MetadataRoute } from "next" export default function robots(): MetadataRoute.Robots { return { diff --git a/apps/www/components/blur-image.tsx b/apps/www/components/blur-image.tsx index 24c062f45..58e00efac 100644 --- a/apps/www/components/blur-image.tsx +++ b/apps/www/components/blur-image.tsx @@ -1,7 +1,7 @@ "use client" import { useEffect, useState } from "react" -import Image, { ImageProps } from "next/image" +import Image, { type ImageProps } from "next/image" export default function BlurImage(props: ImageProps) { const [loading, setLoading] = useState(true) diff --git a/apps/www/components/contribute.tsx b/apps/www/components/contribute.tsx index 715e6b3eb..a62e7366f 100644 --- a/apps/www/components/contribute.tsx +++ b/apps/www/components/contribute.tsx @@ -1,5 +1,5 @@ import Link from "next/link" -import { Page, PageData } from "fumadocs-core/source" +import { type Page, type PageData } from "fumadocs-core/source" import { BugIcon, LightbulbIcon, PencilIcon } from "lucide-react" import { getGithubFileUrl, getGitHubIssueUrl } from "@/lib/github" diff --git a/apps/www/components/copy-button.tsx b/apps/www/components/copy-button.tsx index 02067b39e..4928d5a56 100644 --- a/apps/www/components/copy-button.tsx +++ b/apps/www/components/copy-button.tsx @@ -3,7 +3,7 @@ import * as React from "react" import { CheckIcon, ClipboardIcon } from "lucide-react" -import { Event, trackEvent } from "@/lib/events" +import { trackEvent, type Event } from "@/lib/events" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { diff --git a/apps/www/components/docs-sidebar.tsx b/apps/www/components/docs-sidebar.tsx index 917f36676..d460f0081 100644 --- a/apps/www/components/docs-sidebar.tsx +++ b/apps/www/components/docs-sidebar.tsx @@ -2,7 +2,7 @@ import Link from "next/link" import { usePathname } from "next/navigation" -import { NavItemWithChildren } from "@/types" +import { type NavItemWithChildren } from "@/types" import { docsConfig } from "@/config/docs" import { trackEvent } from "@/lib/events" diff --git a/apps/www/components/main-nav.tsx b/apps/www/components/main-nav.tsx index 930563d20..c8758f86d 100644 --- a/apps/www/components/main-nav.tsx +++ b/apps/www/components/main-nav.tsx @@ -2,7 +2,7 @@ import Link from "next/link" import { usePathname } from "next/navigation" -import { NavItem } from "@/types" +import { type NavItem } from "@/types" import { trackEvent } from "@/lib/events" import { cn } from "@/lib/utils" diff --git a/apps/www/components/mobile-nav.tsx b/apps/www/components/mobile-nav.tsx index cf9fc4aef..deca28992 100644 --- a/apps/www/components/mobile-nav.tsx +++ b/apps/www/components/mobile-nav.tsx @@ -1,7 +1,7 @@ "use client" import * as React from "react" -import Link, { LinkProps } from "next/link" +import Link, { type LinkProps } from "next/link" import { useRouter } from "next/navigation" import { docsConfig } from "@/config/docs" diff --git a/apps/www/components/template-preview.tsx b/apps/www/components/template-preview.tsx index 91712f016..b52bbd397 100644 --- a/apps/www/components/template-preview.tsx +++ b/apps/www/components/template-preview.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from "react" +import { type ReactNode } from "react" import Link from "next/link" import { ExternalLinkIcon } from "@radix-ui/react-icons" diff --git a/apps/www/components/theme-provider.tsx b/apps/www/components/theme-provider.tsx index 880ced400..4c598461e 100644 --- a/apps/www/components/theme-provider.tsx +++ b/apps/www/components/theme-provider.tsx @@ -2,7 +2,7 @@ import { ThemeProvider as NextThemesProvider, - ThemeProviderProps, + type ThemeProviderProps, } from "next-themes" export function ThemeProvider({ children, ...props }: ThemeProviderProps) { diff --git a/apps/www/components/ui/sidebar.tsx b/apps/www/components/ui/sidebar.tsx index 2d6708e5e..02abdffeb 100644 --- a/apps/www/components/ui/sidebar.tsx +++ b/apps/www/components/ui/sidebar.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { Slot } from "@radix-ui/react-slot" -import { cva, VariantProps } from "class-variance-authority" +import { cva, type VariantProps } from "class-variance-authority" import { PanelLeftIcon } from "lucide-react" import { cn } from "@/lib/utils" diff --git a/apps/www/config/docs.ts b/apps/www/config/docs.ts index 19e1ea203..ddb7c84f8 100644 --- a/apps/www/config/docs.ts +++ b/apps/www/config/docs.ts @@ -1,4 +1,4 @@ -import { NavItem, NavItemWithChildren } from "@/types" +import { type NavItem, type NavItemWithChildren } from "@/types" interface DocsConfig { mainNav: NavItem[] diff --git a/apps/www/lib/rehype-component.ts b/apps/www/lib/rehype-component.ts index 728edc72d..219e3b4f3 100644 --- a/apps/www/lib/rehype-component.ts +++ b/apps/www/lib/rehype-component.ts @@ -1,6 +1,6 @@ import fs from "fs" import path from "path" -import { UnistNode, UnistTree } from "types/unist" +import { type UnistNode, type UnistTree } from "types/unist" import { u } from "unist-builder" import { visit } from "unist-util-visit" diff --git a/apps/www/lib/rehype-npm-command.ts b/apps/www/lib/rehype-npm-command.ts index b4f38e7bc..55b462163 100644 --- a/apps/www/lib/rehype-npm-command.ts +++ b/apps/www/lib/rehype-npm-command.ts @@ -1,4 +1,4 @@ -import { UnistNode, UnistTree } from "types/unist" +import { type UnistNode, type UnistTree } from "types/unist" import { visit } from "unist-util-visit" export function rehypeNpmCommand() { diff --git a/apps/www/lib/utils.ts b/apps/www/lib/utils.ts index f82cdebb9..f610181c1 100644 --- a/apps/www/lib/utils.ts +++ b/apps/www/lib/utils.ts @@ -1,6 +1,6 @@ import process from "process" import type { Metadata } from "next" -import clsx, { ClassValue } from "clsx" +import clsx, { type ClassValue } from "clsx" import { twMerge } from "tailwind-merge" import { siteConfig } from "@/config/site" diff --git a/apps/www/public/llms-full.txt b/apps/www/public/llms-full.txt index e8a1ce9b1..cae355aa3 100644 --- a/apps/www/public/llms-full.txt +++ b/apps/www/public/llms-full.txt @@ -3,7 +3,7 @@ Title: Android Description: A mockup of an Android device. --- file: magicui/android.tsx --- -import { SVGProps } from "react" +import { type SVGProps } from "react" export interface AndroidProps extends SVGProps { width?: number @@ -4427,7 +4427,7 @@ Description: An animated beam of light which travels along the border of its con --- file: magicui/border-beam.tsx --- "use client" -import { motion, MotionStyle, Transition } from "motion/react" +import { motion, type MotionStyle, type Transition } from "motion/react" import { cn } from "@/lib/utils" @@ -4745,7 +4745,7 @@ Description: A client-side version of the tweet card that displays a tweet with --- file: magicui/client-tweet-card.tsx --- "use client" -import { TweetProps, useTweet } from "react-tweet" +import { useTweet, type TweetProps } from "react-tweet" import { MagicTweet, @@ -5057,7 +5057,7 @@ Description: Comic text animation --- file: magicui/comic-text.tsx --- "use client" -import { CSSProperties } from "react" +import { type CSSProperties } from "react" import { motion } from "motion/react" import { cn } from "@/lib/utils" @@ -5638,7 +5638,7 @@ Description: Cool mode effect for buttons, links, and other DOMs --- file: magicui/cool-mode.tsx --- "use client" -import React, { ReactNode, useEffect, useRef } from "react" +import React, { useEffect, useRef, type ReactNode } from "react" export interface BaseParticle { element: HTMLElement | SVGSVGElement @@ -6333,7 +6333,7 @@ Description: An implementation of the MacOS dock using react + tailwindcss + mot --- file: magicui/dock.tsx --- "use client" -import React, { PropsWithChildren, useRef } from "react" +import React, { useRef, type PropsWithChildren } from "react" import { cva, type VariantProps } from "class-variance-authority" import { motion, @@ -13188,12 +13188,12 @@ Description: A beautiful neon card effect "use client" import { - CSSProperties, - ReactElement, - ReactNode, useEffect, useRef, useState, + type CSSProperties, + type ReactElement, + type ReactNode, } from "react" import { cn } from "@/lib/utils" @@ -14992,7 +14992,7 @@ Description: An animated button with a rainbow effect. --- file: magicui/rainbow-button.tsx --- import React from "react" import { Slot } from "@radix-ui/react-slot" -import { cva, VariantProps } from "class-variance-authority" +import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" @@ -16062,7 +16062,7 @@ Description: An animated button with ripple useful for user engagement. --- file: magicui/ripple-button.tsx --- "use client" -import React, { MouseEvent, useEffect, useState } from "react" +import React, { useEffect, useState, type MouseEvent } from "react" import { cn } from "@/lib/utils" @@ -17257,7 +17257,7 @@ Description: A customizable, physics-based smooth cursor animation component wit --- file: magicui/smooth-cursor.tsx --- "use client" -import { FC, useEffect, useRef, useState } from "react" +import { useEffect, useRef, useState, type FC } from "react" import { motion, useSpring } from "motion/react" interface Position { @@ -17543,7 +17543,12 @@ Description: A dynamic text that generates continuous sparkles with smooth trans --- file: magicui/sparkles-text.tsx --- "use client" -import { CSSProperties, ReactElement, useEffect, useState } from "react" +import { + useEffect, + useState, + type CSSProperties, + type ReactElement, +} from "react" import { motion } from "motion/react" import { cn } from "@/lib/utils" @@ -17713,7 +17718,7 @@ Description: The Spinning Text component animates text in a circular motion with "use client" import React, { type ComponentPropsWithoutRef } from "react" -import { motion, Transition, Variants } from "motion/react" +import { motion, type Transition, type Variants } from "motion/react" import { cn } from "@/lib/utils" @@ -18694,9 +18699,9 @@ import { memo } from "react" import { AnimatePresence, motion, - Variants, type DOMMotionComponents, type MotionProps, + type Variants, } from "motion/react" import { cn } from "@/lib/utils" @@ -20179,7 +20184,12 @@ Description: A component that displays text with a video playing in the backgrou --- file: magicui/video-text.tsx --- "use client" -import React, { ElementType, ReactNode, useEffect, useState } from "react" +import React, { + useEffect, + useState, + type ElementType, + type ReactNode, +} from "react" import { cn } from "@/lib/utils" @@ -20338,7 +20348,7 @@ Description: A card with a time warping background effect. --- file: magicui/warp-background.tsx --- "use client" -import React, { HTMLAttributes, useCallback, useMemo } from "react" +import React, { useCallback, useMemo, type HTMLAttributes } from "react" import { motion } from "motion/react" import { cn } from "@/lib/utils" diff --git a/apps/www/public/r/android.json b/apps/www/public/r/android.json index 3f4c19b92..f5e63c01d 100644 --- a/apps/www/public/r/android.json +++ b/apps/www/public/r/android.json @@ -7,7 +7,7 @@ "files": [ { "path": "registry/magicui/android.tsx", - "content": "import { SVGProps } from \"react\"\n\nexport interface AndroidProps extends SVGProps {\n width?: number\n height?: number\n src?: string\n videoSrc?: string\n}\n\nexport function Android({\n width = 433,\n height = 882,\n src,\n videoSrc,\n ...props\n}: AndroidProps) {\n return (\n \n \n \n \n \n\n \n \n \n \n \n {src && (\n \n )}\n {videoSrc && (\n \n \n \n )}\n \n \n \n \n \n \n )\n}\n", + "content": "import { type SVGProps } from \"react\"\n\nexport interface AndroidProps extends SVGProps {\n width?: number\n height?: number\n src?: string\n videoSrc?: string\n}\n\nexport function Android({\n width = 433,\n height = 882,\n src,\n videoSrc,\n ...props\n}: AndroidProps) {\n return (\n \n \n \n \n \n\n \n \n \n \n \n {src && (\n \n )}\n {videoSrc && (\n \n \n \n )}\n \n \n \n \n \n \n )\n}\n", "type": "registry:ui" } ] diff --git a/apps/www/public/r/border-beam.json b/apps/www/public/r/border-beam.json index 6814fbe2a..441a58469 100644 --- a/apps/www/public/r/border-beam.json +++ b/apps/www/public/r/border-beam.json @@ -10,7 +10,7 @@ "files": [ { "path": "registry/magicui/border-beam.tsx", - "content": "\"use client\"\n\nimport { motion, MotionStyle, Transition } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface BorderBeamProps {\n /**\n * The size of the border beam.\n */\n size?: number\n /**\n * The duration of the border beam.\n */\n duration?: number\n /**\n * The delay of the border beam.\n */\n delay?: number\n /**\n * The color of the border beam from.\n */\n colorFrom?: string\n /**\n * The color of the border beam to.\n */\n colorTo?: string\n /**\n * The motion transition of the border beam.\n */\n transition?: Transition\n /**\n * The class name of the border beam.\n */\n className?: string\n /**\n * The style of the border beam.\n */\n style?: React.CSSProperties\n /**\n * Whether to reverse the animation direction.\n */\n reverse?: boolean\n /**\n * The initial offset position (0-100).\n */\n initialOffset?: number\n /**\n * The border width of the beam.\n */\n borderWidth?: number\n}\n\nexport const BorderBeam = ({\n className,\n size = 50,\n delay = 0,\n duration = 6,\n colorFrom = \"#ffaa40\",\n colorTo = \"#9c40ff\",\n transition,\n style,\n reverse = false,\n initialOffset = 0,\n borderWidth = 1,\n}: BorderBeamProps) => {\n return (\n \n \n \n )\n}\n", + "content": "\"use client\"\n\nimport { motion, type MotionStyle, type Transition } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface BorderBeamProps {\n /**\n * The size of the border beam.\n */\n size?: number\n /**\n * The duration of the border beam.\n */\n duration?: number\n /**\n * The delay of the border beam.\n */\n delay?: number\n /**\n * The color of the border beam from.\n */\n colorFrom?: string\n /**\n * The color of the border beam to.\n */\n colorTo?: string\n /**\n * The motion transition of the border beam.\n */\n transition?: Transition\n /**\n * The class name of the border beam.\n */\n className?: string\n /**\n * The style of the border beam.\n */\n style?: React.CSSProperties\n /**\n * Whether to reverse the animation direction.\n */\n reverse?: boolean\n /**\n * The initial offset position (0-100).\n */\n initialOffset?: number\n /**\n * The border width of the beam.\n */\n borderWidth?: number\n}\n\nexport const BorderBeam = ({\n className,\n size = 50,\n delay = 0,\n duration = 6,\n colorFrom = \"#ffaa40\",\n colorTo = \"#9c40ff\",\n transition,\n style,\n reverse = false,\n initialOffset = 0,\n borderWidth = 1,\n}: BorderBeamProps) => {\n return (\n \n \n \n )\n}\n", "type": "registry:ui" } ] diff --git a/apps/www/public/r/client-tweet-card.json b/apps/www/public/r/client-tweet-card.json index 98ac10c1f..c2abd5209 100644 --- a/apps/www/public/r/client-tweet-card.json +++ b/apps/www/public/r/client-tweet-card.json @@ -10,7 +10,7 @@ "files": [ { "path": "registry/magicui/client-tweet-card.tsx", - "content": "\"use client\"\n\nimport { TweetProps, useTweet } from \"react-tweet\"\n\nimport {\n MagicTweet,\n TweetNotFound,\n TweetSkeleton,\n} from \"@/registry/magicui/tweet-card\"\n\nexport const ClientTweetCard = ({\n id,\n apiUrl,\n fallback = ,\n components,\n fetchOptions,\n onError,\n ...props\n}: TweetProps & { className?: string }) => {\n const { data, error, isLoading } = useTweet(id, apiUrl, fetchOptions)\n\n if (isLoading) return fallback\n if (error || !data) {\n const NotFound = components?.TweetNotFound ?? TweetNotFound\n return \n }\n\n return \n}\n", + "content": "\"use client\"\n\nimport { useTweet, type TweetProps } from \"react-tweet\"\n\nimport {\n MagicTweet,\n TweetNotFound,\n TweetSkeleton,\n} from \"@/registry/magicui/tweet-card\"\n\nexport const ClientTweetCard = ({\n id,\n apiUrl,\n fallback = ,\n components,\n fetchOptions,\n onError,\n ...props\n}: TweetProps & { className?: string }) => {\n const { data, error, isLoading } = useTweet(id, apiUrl, fetchOptions)\n\n if (isLoading) return fallback\n if (error || !data) {\n const NotFound = components?.TweetNotFound ?? TweetNotFound\n return \n }\n\n return \n}\n", "type": "registry:ui" } ] diff --git a/apps/www/public/r/comic-text.json b/apps/www/public/r/comic-text.json index 42b1728a9..b0467cdff 100644 --- a/apps/www/public/r/comic-text.json +++ b/apps/www/public/r/comic-text.json @@ -10,7 +10,7 @@ "files": [ { "path": "registry/magicui/comic-text.tsx", - "content": "\"use client\"\n\nimport { CSSProperties } from \"react\"\nimport { motion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype ComicTextProps = {\n children: string\n className?: string\n style?: CSSProperties\n fontSize?: number\n}\n\nexport function ComicText({\n children,\n className,\n style,\n fontSize = 5,\n}: ComicTextProps) {\n if (typeof children !== \"string\") {\n throw new Error(\"children must be a string\")\n }\n\n const dotColor = \"#EF4444\"\n const backgroundColor = \"#FACC15\"\n\n return (\n \n {children}\n \n )\n}\n", + "content": "\"use client\"\n\nimport { type CSSProperties } from \"react\"\nimport { motion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype ComicTextProps = {\n children: string\n className?: string\n style?: CSSProperties\n fontSize?: number\n}\n\nexport function ComicText({\n children,\n className,\n style,\n fontSize = 5,\n}: ComicTextProps) {\n if (typeof children !== \"string\") {\n throw new Error(\"children must be a string\")\n }\n\n const dotColor = \"#EF4444\"\n const backgroundColor = \"#FACC15\"\n\n return (\n \n {children}\n \n )\n}\n", "type": "registry:ui" } ] diff --git a/apps/www/public/r/cool-mode.json b/apps/www/public/r/cool-mode.json index 8304a541a..f3093676a 100644 --- a/apps/www/public/r/cool-mode.json +++ b/apps/www/public/r/cool-mode.json @@ -7,7 +7,7 @@ "files": [ { "path": "registry/magicui/cool-mode.tsx", - "content": "\"use client\"\n\nimport React, { ReactNode, useEffect, useRef } from \"react\"\n\nexport interface BaseParticle {\n element: HTMLElement | SVGSVGElement\n left: number\n size: number\n top: number\n}\n\nexport interface BaseParticleOptions {\n particle?: string\n size?: number\n}\n\nexport interface CoolParticle extends BaseParticle {\n direction: number\n speedHorz: number\n speedUp: number\n spinSpeed: number\n spinVal: number\n}\n\nexport interface CoolParticleOptions extends BaseParticleOptions {\n particleCount?: number\n speedHorz?: number\n speedUp?: number\n}\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\"\n\nconst getContainer = () => {\n const id = \"_coolMode_effect\"\n const existingContainer = document.getElementById(id)\n\n if (existingContainer) {\n return existingContainer\n }\n\n const container = document.createElement(\"div\")\n container.setAttribute(\"id\", id)\n container.setAttribute(\n \"style\",\n \"overflow:hidden; position:fixed; height:100%; top:0; left:0; right:0; bottom:0; pointer-events:none; z-index:2147483647\"\n )\n\n document.body.appendChild(container)\n\n return container\n}\n\nlet instanceCounter = 0\n\nconst applyParticleEffect = (\n element: HTMLElement,\n options?: CoolParticleOptions\n): (() => void) => {\n instanceCounter++\n\n const defaultParticle = \"circle\"\n const particleType = options?.particle || defaultParticle\n const sizes = [15, 20, 25, 35, 45]\n const limit = 45\n\n let particles: CoolParticle[] = []\n let autoAddParticle = false\n let mouseX = 0\n let mouseY = 0\n\n const container = getContainer()\n\n const appendCircleParticle = (particle: HTMLDivElement, size: number) => {\n const circleSVG = document.createElementNS(SVG_NS, \"svg\")\n const circle = document.createElementNS(SVG_NS, \"circle\")\n\n circle.setAttributeNS(null, \"cx\", (size / 2).toString())\n circle.setAttributeNS(null, \"cy\", (size / 2).toString())\n circle.setAttributeNS(null, \"r\", (size / 2).toString())\n circle.setAttributeNS(null, \"fill\", `hsl(${Math.random() * 360}, 70%, 50%)`)\n\n circleSVG.appendChild(circle)\n circleSVG.setAttribute(\"width\", size.toString())\n circleSVG.setAttribute(\"height\", size.toString())\n\n particle.appendChild(circleSVG)\n }\n\n const appendImageParticle = (\n particle: HTMLDivElement,\n imageSrc: string,\n size: number\n ) => {\n const image = document.createElement(\"img\")\n image.src = imageSrc\n image.width = size\n image.height = size\n image.alt = \"\"\n image.style.borderRadius = \"50%\"\n\n particle.appendChild(image)\n }\n\n const appendTextParticle = (\n particle: HTMLDivElement,\n particleContent: string,\n size: number\n ) => {\n const fontSizeMultiplier = 3\n const emojiSize = size * fontSizeMultiplier\n const content = document.createElement(\"div\")\n\n content.textContent = particleContent\n content.style.fontSize = `${emojiSize}px`\n content.style.lineHeight = \"1\"\n content.style.textAlign = \"center\"\n content.style.width = `${size}px`\n content.style.height = `${size}px`\n content.style.display = \"flex\"\n content.style.alignItems = \"center\"\n content.style.justifyContent = \"center\"\n content.style.transform = `scale(${fontSizeMultiplier})`\n content.style.transformOrigin = \"center\"\n\n particle.appendChild(content)\n }\n\n function generateParticle() {\n const size =\n options?.size || sizes[Math.floor(Math.random() * sizes.length)]\n const speedHorz = options?.speedHorz || Math.random() * 10\n const speedUp = options?.speedUp || Math.random() * 25\n const spinVal = Math.random() * 360\n const spinSpeed = Math.random() * 35 * (Math.random() <= 0.5 ? -1 : 1)\n const top = mouseY - size / 2\n const left = mouseX - size / 2\n const direction = Math.random() <= 0.5 ? -1 : 1\n\n const particle = document.createElement(\"div\")\n\n if (particleType === \"circle\") {\n appendCircleParticle(particle, size)\n } else if (\n particleType.startsWith(\"http\") ||\n particleType.startsWith(\"/\")\n ) {\n appendImageParticle(particle, particleType, size)\n } else {\n appendTextParticle(particle, particleType, size)\n }\n\n particle.style.position = \"absolute\"\n particle.style.transform = `translate3d(${left}px, ${top}px, 0px) rotate(${spinVal}deg)`\n\n container.appendChild(particle)\n\n particles.push({\n direction,\n element: particle,\n left,\n size,\n speedHorz,\n speedUp,\n spinSpeed,\n spinVal,\n top,\n })\n }\n\n function refreshParticles() {\n particles.forEach((p) => {\n p.left = p.left - p.speedHorz * p.direction\n p.top = p.top - p.speedUp\n p.speedUp = Math.min(p.size, p.speedUp - 1)\n p.spinVal = p.spinVal + p.spinSpeed\n\n if (\n p.top >=\n Math.max(window.innerHeight, document.body.clientHeight) + p.size\n ) {\n particles = particles.filter((o) => o !== p)\n p.element.remove()\n }\n\n p.element.setAttribute(\n \"style\",\n [\n \"position:absolute\",\n \"will-change:transform\",\n `top:${p.top}px`,\n `left:${p.left}px`,\n `transform:rotate(${p.spinVal}deg)`,\n ].join(\";\")\n )\n })\n }\n\n let animationFrame: number | undefined\n\n let lastParticleTimestamp = 0\n const particleGenerationDelay = 30\n\n function loop() {\n const currentTime = performance.now()\n if (\n autoAddParticle &&\n particles.length < limit &&\n currentTime - lastParticleTimestamp > particleGenerationDelay\n ) {\n generateParticle()\n lastParticleTimestamp = currentTime\n }\n\n refreshParticles()\n animationFrame = requestAnimationFrame(loop)\n }\n\n loop()\n\n const isTouchInteraction = \"ontouchstart\" in window\n\n const tap = isTouchInteraction ? \"touchstart\" : \"mousedown\"\n const tapEnd = isTouchInteraction ? \"touchend\" : \"mouseup\"\n const move = isTouchInteraction ? \"touchmove\" : \"mousemove\"\n\n const updateMousePosition = (e: MouseEvent | TouchEvent) => {\n if (\"touches\" in e) {\n mouseX = e.touches?.[0].clientX\n mouseY = e.touches?.[0].clientY\n } else {\n mouseX = e.clientX\n mouseY = e.clientY\n }\n }\n\n const tapHandler = (e: MouseEvent | TouchEvent) => {\n updateMousePosition(e)\n autoAddParticle = true\n }\n\n const disableAutoAddParticle = () => {\n autoAddParticle = false\n }\n\n element.addEventListener(move, updateMousePosition, { passive: true })\n element.addEventListener(tap, tapHandler, { passive: true })\n element.addEventListener(tapEnd, disableAutoAddParticle, { passive: true })\n element.addEventListener(\"mouseleave\", disableAutoAddParticle, {\n passive: true,\n })\n\n return () => {\n element.removeEventListener(move, updateMousePosition)\n element.removeEventListener(tap, tapHandler)\n element.removeEventListener(tapEnd, disableAutoAddParticle)\n element.removeEventListener(\"mouseleave\", disableAutoAddParticle)\n\n const interval = setInterval(() => {\n if (animationFrame && particles.length === 0) {\n cancelAnimationFrame(animationFrame)\n clearInterval(interval)\n\n if (--instanceCounter === 0) {\n container.remove()\n }\n }\n }, 500)\n }\n}\n\ninterface CoolModeProps {\n children: ReactNode\n options?: CoolParticleOptions\n}\n\nexport const CoolMode: React.FC = ({ children, options }) => {\n const ref = useRef(null)\n\n useEffect(() => {\n const element = ref.current\n let cleanup: (() => void) | null = null\n\n if (element) {\n cleanup = applyParticleEffect(element, options)\n }\n\n return () => {\n if (cleanup) {\n cleanup()\n }\n }\n }, [options])\n\n return {children}\n}\n", + "content": "\"use client\"\n\nimport React, { useEffect, useRef, type ReactNode } from \"react\"\n\nexport interface BaseParticle {\n element: HTMLElement | SVGSVGElement\n left: number\n size: number\n top: number\n}\n\nexport interface BaseParticleOptions {\n particle?: string\n size?: number\n}\n\nexport interface CoolParticle extends BaseParticle {\n direction: number\n speedHorz: number\n speedUp: number\n spinSpeed: number\n spinVal: number\n}\n\nexport interface CoolParticleOptions extends BaseParticleOptions {\n particleCount?: number\n speedHorz?: number\n speedUp?: number\n}\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\"\n\nconst getContainer = () => {\n const id = \"_coolMode_effect\"\n const existingContainer = document.getElementById(id)\n\n if (existingContainer) {\n return existingContainer\n }\n\n const container = document.createElement(\"div\")\n container.setAttribute(\"id\", id)\n container.setAttribute(\n \"style\",\n \"overflow:hidden; position:fixed; height:100%; top:0; left:0; right:0; bottom:0; pointer-events:none; z-index:2147483647\"\n )\n\n document.body.appendChild(container)\n\n return container\n}\n\nlet instanceCounter = 0\n\nconst applyParticleEffect = (\n element: HTMLElement,\n options?: CoolParticleOptions\n): (() => void) => {\n instanceCounter++\n\n const defaultParticle = \"circle\"\n const particleType = options?.particle || defaultParticle\n const sizes = [15, 20, 25, 35, 45]\n const limit = 45\n\n let particles: CoolParticle[] = []\n let autoAddParticle = false\n let mouseX = 0\n let mouseY = 0\n\n const container = getContainer()\n\n const appendCircleParticle = (particle: HTMLDivElement, size: number) => {\n const circleSVG = document.createElementNS(SVG_NS, \"svg\")\n const circle = document.createElementNS(SVG_NS, \"circle\")\n\n circle.setAttributeNS(null, \"cx\", (size / 2).toString())\n circle.setAttributeNS(null, \"cy\", (size / 2).toString())\n circle.setAttributeNS(null, \"r\", (size / 2).toString())\n circle.setAttributeNS(null, \"fill\", `hsl(${Math.random() * 360}, 70%, 50%)`)\n\n circleSVG.appendChild(circle)\n circleSVG.setAttribute(\"width\", size.toString())\n circleSVG.setAttribute(\"height\", size.toString())\n\n particle.appendChild(circleSVG)\n }\n\n const appendImageParticle = (\n particle: HTMLDivElement,\n imageSrc: string,\n size: number\n ) => {\n const image = document.createElement(\"img\")\n image.src = imageSrc\n image.width = size\n image.height = size\n image.alt = \"\"\n image.style.borderRadius = \"50%\"\n\n particle.appendChild(image)\n }\n\n const appendTextParticle = (\n particle: HTMLDivElement,\n particleContent: string,\n size: number\n ) => {\n const fontSizeMultiplier = 3\n const emojiSize = size * fontSizeMultiplier\n const content = document.createElement(\"div\")\n\n content.textContent = particleContent\n content.style.fontSize = `${emojiSize}px`\n content.style.lineHeight = \"1\"\n content.style.textAlign = \"center\"\n content.style.width = `${size}px`\n content.style.height = `${size}px`\n content.style.display = \"flex\"\n content.style.alignItems = \"center\"\n content.style.justifyContent = \"center\"\n content.style.transform = `scale(${fontSizeMultiplier})`\n content.style.transformOrigin = \"center\"\n\n particle.appendChild(content)\n }\n\n function generateParticle() {\n const size =\n options?.size || sizes[Math.floor(Math.random() * sizes.length)]\n const speedHorz = options?.speedHorz || Math.random() * 10\n const speedUp = options?.speedUp || Math.random() * 25\n const spinVal = Math.random() * 360\n const spinSpeed = Math.random() * 35 * (Math.random() <= 0.5 ? -1 : 1)\n const top = mouseY - size / 2\n const left = mouseX - size / 2\n const direction = Math.random() <= 0.5 ? -1 : 1\n\n const particle = document.createElement(\"div\")\n\n if (particleType === \"circle\") {\n appendCircleParticle(particle, size)\n } else if (\n particleType.startsWith(\"http\") ||\n particleType.startsWith(\"/\")\n ) {\n appendImageParticle(particle, particleType, size)\n } else {\n appendTextParticle(particle, particleType, size)\n }\n\n particle.style.position = \"absolute\"\n particle.style.transform = `translate3d(${left}px, ${top}px, 0px) rotate(${spinVal}deg)`\n\n container.appendChild(particle)\n\n particles.push({\n direction,\n element: particle,\n left,\n size,\n speedHorz,\n speedUp,\n spinSpeed,\n spinVal,\n top,\n })\n }\n\n function refreshParticles() {\n particles.forEach((p) => {\n p.left = p.left - p.speedHorz * p.direction\n p.top = p.top - p.speedUp\n p.speedUp = Math.min(p.size, p.speedUp - 1)\n p.spinVal = p.spinVal + p.spinSpeed\n\n if (\n p.top >=\n Math.max(window.innerHeight, document.body.clientHeight) + p.size\n ) {\n particles = particles.filter((o) => o !== p)\n p.element.remove()\n }\n\n p.element.setAttribute(\n \"style\",\n [\n \"position:absolute\",\n \"will-change:transform\",\n `top:${p.top}px`,\n `left:${p.left}px`,\n `transform:rotate(${p.spinVal}deg)`,\n ].join(\";\")\n )\n })\n }\n\n let animationFrame: number | undefined\n\n let lastParticleTimestamp = 0\n const particleGenerationDelay = 30\n\n function loop() {\n const currentTime = performance.now()\n if (\n autoAddParticle &&\n particles.length < limit &&\n currentTime - lastParticleTimestamp > particleGenerationDelay\n ) {\n generateParticle()\n lastParticleTimestamp = currentTime\n }\n\n refreshParticles()\n animationFrame = requestAnimationFrame(loop)\n }\n\n loop()\n\n const isTouchInteraction = \"ontouchstart\" in window\n\n const tap = isTouchInteraction ? \"touchstart\" : \"mousedown\"\n const tapEnd = isTouchInteraction ? \"touchend\" : \"mouseup\"\n const move = isTouchInteraction ? \"touchmove\" : \"mousemove\"\n\n const updateMousePosition = (e: MouseEvent | TouchEvent) => {\n if (\"touches\" in e) {\n mouseX = e.touches?.[0].clientX\n mouseY = e.touches?.[0].clientY\n } else {\n mouseX = e.clientX\n mouseY = e.clientY\n }\n }\n\n const tapHandler = (e: MouseEvent | TouchEvent) => {\n updateMousePosition(e)\n autoAddParticle = true\n }\n\n const disableAutoAddParticle = () => {\n autoAddParticle = false\n }\n\n element.addEventListener(move, updateMousePosition, { passive: true })\n element.addEventListener(tap, tapHandler, { passive: true })\n element.addEventListener(tapEnd, disableAutoAddParticle, { passive: true })\n element.addEventListener(\"mouseleave\", disableAutoAddParticle, {\n passive: true,\n })\n\n return () => {\n element.removeEventListener(move, updateMousePosition)\n element.removeEventListener(tap, tapHandler)\n element.removeEventListener(tapEnd, disableAutoAddParticle)\n element.removeEventListener(\"mouseleave\", disableAutoAddParticle)\n\n const interval = setInterval(() => {\n if (animationFrame && particles.length === 0) {\n cancelAnimationFrame(animationFrame)\n clearInterval(interval)\n\n if (--instanceCounter === 0) {\n container.remove()\n }\n }\n }, 500)\n }\n}\n\ninterface CoolModeProps {\n children: ReactNode\n options?: CoolParticleOptions\n}\n\nexport const CoolMode: React.FC = ({ children, options }) => {\n const ref = useRef(null)\n\n useEffect(() => {\n const element = ref.current\n let cleanup: (() => void) | null = null\n\n if (element) {\n cleanup = applyParticleEffect(element, options)\n }\n\n return () => {\n if (cleanup) {\n cleanup()\n }\n }\n }, [options])\n\n return {children}\n}\n", "type": "registry:ui" } ] diff --git a/apps/www/public/r/dock.json b/apps/www/public/r/dock.json index b61481f65..3397b46aa 100644 --- a/apps/www/public/r/dock.json +++ b/apps/www/public/r/dock.json @@ -10,7 +10,7 @@ "files": [ { "path": "registry/magicui/dock.tsx", - "content": "\"use client\"\n\nimport React, { PropsWithChildren, useRef } from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport {\n motion,\n MotionValue,\n useMotionValue,\n useSpring,\n useTransform,\n} from \"motion/react\"\nimport type { MotionProps } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface DockProps extends VariantProps {\n className?: string\n iconSize?: number\n iconMagnification?: number\n disableMagnification?: boolean\n iconDistance?: number\n direction?: \"top\" | \"middle\" | \"bottom\"\n children: React.ReactNode\n}\n\nconst DEFAULT_SIZE = 40\nconst DEFAULT_MAGNIFICATION = 60\nconst DEFAULT_DISTANCE = 140\nconst DEFAULT_DISABLEMAGNIFICATION = false\n\nconst dockVariants = cva(\n \"supports-backdrop-blur:bg-white/10 supports-backdrop-blur:dark:bg-black/10 mx-auto mt-8 flex h-[58px] w-max items-center justify-center gap-2 rounded-2xl border p-2 backdrop-blur-md\"\n)\n\nconst Dock = React.forwardRef(\n (\n {\n className,\n children,\n iconSize = DEFAULT_SIZE,\n iconMagnification = DEFAULT_MAGNIFICATION,\n disableMagnification = DEFAULT_DISABLEMAGNIFICATION,\n iconDistance = DEFAULT_DISTANCE,\n direction = \"middle\",\n ...props\n },\n ref\n ) => {\n const mouseX = useMotionValue(Infinity)\n\n const renderChildren = () => {\n return React.Children.map(children, (child) => {\n if (\n React.isValidElement(child) &&\n child.type === DockIcon\n ) {\n return React.cloneElement(child, {\n ...child.props,\n mouseX: mouseX,\n size: iconSize,\n magnification: iconMagnification,\n disableMagnification: disableMagnification,\n distance: iconDistance,\n })\n }\n return child\n })\n }\n\n return (\n mouseX.set(e.pageX)}\n onMouseLeave={() => mouseX.set(Infinity)}\n {...props}\n className={cn(dockVariants({ className }), {\n \"items-start\": direction === \"top\",\n \"items-center\": direction === \"middle\",\n \"items-end\": direction === \"bottom\",\n })}\n >\n {renderChildren()}\n \n )\n }\n)\n\nDock.displayName = \"Dock\"\n\nexport interface DockIconProps extends Omit<\n MotionProps & React.HTMLAttributes,\n \"children\"\n> {\n size?: number\n magnification?: number\n disableMagnification?: boolean\n distance?: number\n mouseX?: MotionValue\n className?: string\n children?: React.ReactNode\n props?: PropsWithChildren\n}\n\nconst DockIcon = ({\n size = DEFAULT_SIZE,\n magnification = DEFAULT_MAGNIFICATION,\n disableMagnification,\n distance = DEFAULT_DISTANCE,\n mouseX,\n className,\n children,\n ...props\n}: DockIconProps) => {\n const ref = useRef(null)\n const padding = Math.max(6, size * 0.2)\n const defaultMouseX = useMotionValue(Infinity)\n\n const distanceCalc = useTransform(mouseX ?? defaultMouseX, (val: number) => {\n const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 }\n return val - bounds.x - bounds.width / 2\n })\n\n const targetSize = disableMagnification ? size : magnification\n\n const sizeTransform = useTransform(\n distanceCalc,\n [-distance, 0, distance],\n [size, targetSize, size]\n )\n\n const scaleSize = useSpring(sizeTransform, {\n mass: 0.1,\n stiffness: 150,\n damping: 12,\n })\n\n return (\n \n
{children}
\n \n )\n}\n\nDockIcon.displayName = \"DockIcon\"\n\nexport { Dock, DockIcon, dockVariants }\n", + "content": "\"use client\"\n\nimport React, { useRef, type PropsWithChildren } from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport {\n motion,\n MotionValue,\n useMotionValue,\n useSpring,\n useTransform,\n} from \"motion/react\"\nimport type { MotionProps } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface DockProps extends VariantProps {\n className?: string\n iconSize?: number\n iconMagnification?: number\n disableMagnification?: boolean\n iconDistance?: number\n direction?: \"top\" | \"middle\" | \"bottom\"\n children: React.ReactNode\n}\n\nconst DEFAULT_SIZE = 40\nconst DEFAULT_MAGNIFICATION = 60\nconst DEFAULT_DISTANCE = 140\nconst DEFAULT_DISABLEMAGNIFICATION = false\n\nconst dockVariants = cva(\n \"supports-backdrop-blur:bg-white/10 supports-backdrop-blur:dark:bg-black/10 mx-auto mt-8 flex h-[58px] w-max items-center justify-center gap-2 rounded-2xl border p-2 backdrop-blur-md\"\n)\n\nconst Dock = React.forwardRef(\n (\n {\n className,\n children,\n iconSize = DEFAULT_SIZE,\n iconMagnification = DEFAULT_MAGNIFICATION,\n disableMagnification = DEFAULT_DISABLEMAGNIFICATION,\n iconDistance = DEFAULT_DISTANCE,\n direction = \"middle\",\n ...props\n },\n ref\n ) => {\n const mouseX = useMotionValue(Infinity)\n\n const renderChildren = () => {\n return React.Children.map(children, (child) => {\n if (\n React.isValidElement(child) &&\n child.type === DockIcon\n ) {\n return React.cloneElement(child, {\n ...child.props,\n mouseX: mouseX,\n size: iconSize,\n magnification: iconMagnification,\n disableMagnification: disableMagnification,\n distance: iconDistance,\n })\n }\n return child\n })\n }\n\n return (\n mouseX.set(e.pageX)}\n onMouseLeave={() => mouseX.set(Infinity)}\n {...props}\n className={cn(dockVariants({ className }), {\n \"items-start\": direction === \"top\",\n \"items-center\": direction === \"middle\",\n \"items-end\": direction === \"bottom\",\n })}\n >\n {renderChildren()}\n \n )\n }\n)\n\nDock.displayName = \"Dock\"\n\nexport interface DockIconProps extends Omit<\n MotionProps & React.HTMLAttributes,\n \"children\"\n> {\n size?: number\n magnification?: number\n disableMagnification?: boolean\n distance?: number\n mouseX?: MotionValue\n className?: string\n children?: React.ReactNode\n props?: PropsWithChildren\n}\n\nconst DockIcon = ({\n size = DEFAULT_SIZE,\n magnification = DEFAULT_MAGNIFICATION,\n disableMagnification,\n distance = DEFAULT_DISTANCE,\n mouseX,\n className,\n children,\n ...props\n}: DockIconProps) => {\n const ref = useRef(null)\n const padding = Math.max(6, size * 0.2)\n const defaultMouseX = useMotionValue(Infinity)\n\n const distanceCalc = useTransform(mouseX ?? defaultMouseX, (val: number) => {\n const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 }\n return val - bounds.x - bounds.width / 2\n })\n\n const targetSize = disableMagnification ? size : magnification\n\n const sizeTransform = useTransform(\n distanceCalc,\n [-distance, 0, distance],\n [size, targetSize, size]\n )\n\n const scaleSize = useSpring(sizeTransform, {\n mass: 0.1,\n stiffness: 150,\n damping: 12,\n })\n\n return (\n \n
{children}
\n \n )\n}\n\nDockIcon.displayName = \"DockIcon\"\n\nexport { Dock, DockIcon, dockVariants }\n", "type": "registry:ui" } ] diff --git a/apps/www/public/r/neon-gradient-card.json b/apps/www/public/r/neon-gradient-card.json index 9ffdf5beb..16ed09826 100644 --- a/apps/www/public/r/neon-gradient-card.json +++ b/apps/www/public/r/neon-gradient-card.json @@ -7,7 +7,7 @@ "files": [ { "path": "registry/magicui/neon-gradient-card.tsx", - "content": "\"use client\"\n\nimport {\n CSSProperties,\n ReactElement,\n ReactNode,\n useEffect,\n useRef,\n useState,\n} from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface NeonColorsProps {\n firstColor: string\n secondColor: string\n}\n\ninterface NeonGradientCardProps extends React.HTMLAttributes {\n /**\n * @default
\n * @type ReactElement\n * @description\n * The component to be rendered as the card\n * */\n as?: ReactElement\n /**\n * @default \"\"\n * @type string\n * @description\n * The className of the card\n */\n className?: string\n\n /**\n * @default \"\"\n * @type ReactNode\n * @description\n * The children of the card\n * */\n children?: ReactNode\n\n /**\n * @default 5\n * @type number\n * @description\n * The size of the border in pixels\n * */\n borderSize?: number\n\n /**\n * @default 20\n * @type number\n * @description\n * The size of the radius in pixels\n * */\n borderRadius?: number\n\n /**\n * @default \"{ firstColor: '#ff00aa', secondColor: '#00FFF1' }\"\n * @type string\n * @description\n * The colors of the neon gradient\n * */\n neonColors?: NeonColorsProps\n}\n\nexport const NeonGradientCard: React.FC = ({\n className,\n children,\n borderSize = 2,\n borderRadius = 20,\n neonColors = {\n firstColor: \"#ff00aa\",\n secondColor: \"#00FFF1\",\n },\n ...props\n}) => {\n const containerRef = useRef(null)\n const [dimensions, setDimensions] = useState({ width: 0, height: 0 })\n\n useEffect(() => {\n const updateDimensions = () => {\n if (containerRef.current) {\n const { offsetWidth, offsetHeight } = containerRef.current\n setDimensions({ width: offsetWidth, height: offsetHeight })\n }\n }\n\n updateDimensions()\n window.addEventListener(\"resize\", updateDimensions)\n\n return () => {\n window.removeEventListener(\"resize\", updateDimensions)\n }\n }, [])\n\n useEffect(() => {\n if (containerRef.current) {\n const { offsetWidth, offsetHeight } = containerRef.current\n setDimensions({ width: offsetWidth, height: offsetHeight })\n }\n }, [children])\n\n return (\n \n \n {children}\n
\n \n )\n}\n", + "content": "\"use client\"\n\nimport {\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n} from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface NeonColorsProps {\n firstColor: string\n secondColor: string\n}\n\ninterface NeonGradientCardProps extends React.HTMLAttributes {\n /**\n * @default
\n * @type ReactElement\n * @description\n * The component to be rendered as the card\n * */\n as?: ReactElement\n /**\n * @default \"\"\n * @type string\n * @description\n * The className of the card\n */\n className?: string\n\n /**\n * @default \"\"\n * @type ReactNode\n * @description\n * The children of the card\n * */\n children?: ReactNode\n\n /**\n * @default 5\n * @type number\n * @description\n * The size of the border in pixels\n * */\n borderSize?: number\n\n /**\n * @default 20\n * @type number\n * @description\n * The size of the radius in pixels\n * */\n borderRadius?: number\n\n /**\n * @default \"{ firstColor: '#ff00aa', secondColor: '#00FFF1' }\"\n * @type string\n * @description\n * The colors of the neon gradient\n * */\n neonColors?: NeonColorsProps\n}\n\nexport const NeonGradientCard: React.FC = ({\n className,\n children,\n borderSize = 2,\n borderRadius = 20,\n neonColors = {\n firstColor: \"#ff00aa\",\n secondColor: \"#00FFF1\",\n },\n ...props\n}) => {\n const containerRef = useRef(null)\n const [dimensions, setDimensions] = useState({ width: 0, height: 0 })\n\n useEffect(() => {\n const updateDimensions = () => {\n if (containerRef.current) {\n const { offsetWidth, offsetHeight } = containerRef.current\n setDimensions({ width: offsetWidth, height: offsetHeight })\n }\n }\n\n updateDimensions()\n window.addEventListener(\"resize\", updateDimensions)\n\n return () => {\n window.removeEventListener(\"resize\", updateDimensions)\n }\n }, [])\n\n useEffect(() => {\n if (containerRef.current) {\n const { offsetWidth, offsetHeight } = containerRef.current\n setDimensions({ width: offsetWidth, height: offsetHeight })\n }\n }, [children])\n\n return (\n \n \n {children}\n
\n \n )\n}\n", "type": "registry:ui" } ], diff --git a/apps/www/public/r/rainbow-button.json b/apps/www/public/r/rainbow-button.json index b22a6cf13..1650c7316 100644 --- a/apps/www/public/r/rainbow-button.json +++ b/apps/www/public/r/rainbow-button.json @@ -7,7 +7,7 @@ "files": [ { "path": "registry/magicui/rainbow-button.tsx", - "content": "import React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst rainbowButtonVariants = cva(\n cn(\n \"relative cursor-pointer group transition-all animate-rainbow\",\n \"inline-flex items-center justify-center gap-2 shrink-0\",\n \"rounded-sm outline-none focus-visible:ring-[3px] aria-invalid:border-destructive\",\n \"text-sm font-medium whitespace-nowrap\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n \"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0\"\n ),\n {\n variants: {\n variant: {\n default:\n \"border-0 bg-[linear-gradient(#121213,#121213),linear-gradient(#121213_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-primary-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] [border:calc(0.125rem)_solid_transparent] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:bg-[length:200%] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#fff,#fff),linear-gradient(#fff_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]\",\n outline:\n \"border border-input border-b-transparent bg-[linear-gradient(#ffffff,#ffffff),linear-gradient(#ffffff_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-accent-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:bg-[length:200%] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#0a0a0a,#0a0a0a),linear-gradient(#0a0a0a_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]\",\n },\n size: {\n default: \"h-9 px-4 py-2\",\n sm: \"h-8 rounded-xl px-3 text-xs\",\n lg: \"h-11 rounded-xl px-8\",\n icon: \"size-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\ninterface RainbowButtonProps\n extends\n React.ButtonHTMLAttributes,\n VariantProps {\n asChild?: boolean\n}\n\nconst RainbowButton = React.forwardRef(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n return (\n \n )\n }\n)\n\nRainbowButton.displayName = \"RainbowButton\"\n\nexport { RainbowButton, rainbowButtonVariants, type RainbowButtonProps }\n", + "content": "import 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 rainbowButtonVariants = cva(\n cn(\n \"relative cursor-pointer group transition-all animate-rainbow\",\n \"inline-flex items-center justify-center gap-2 shrink-0\",\n \"rounded-sm outline-none focus-visible:ring-[3px] aria-invalid:border-destructive\",\n \"text-sm font-medium whitespace-nowrap\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n \"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0\"\n ),\n {\n variants: {\n variant: {\n default:\n \"border-0 bg-[linear-gradient(#121213,#121213),linear-gradient(#121213_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-primary-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] [border:calc(0.125rem)_solid_transparent] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:bg-[length:200%] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#fff,#fff),linear-gradient(#fff_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]\",\n outline:\n \"border border-input border-b-transparent bg-[linear-gradient(#ffffff,#ffffff),linear-gradient(#ffffff_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-accent-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:bg-[length:200%] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#0a0a0a,#0a0a0a),linear-gradient(#0a0a0a_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]\",\n },\n size: {\n default: \"h-9 px-4 py-2\",\n sm: \"h-8 rounded-xl px-3 text-xs\",\n lg: \"h-11 rounded-xl px-8\",\n icon: \"size-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\ninterface RainbowButtonProps\n extends\n React.ButtonHTMLAttributes,\n VariantProps {\n asChild?: boolean\n}\n\nconst RainbowButton = React.forwardRef(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n return (\n \n )\n }\n)\n\nRainbowButton.displayName = \"RainbowButton\"\n\nexport { RainbowButton, rainbowButtonVariants, type RainbowButtonProps }\n", "type": "registry:ui" } ], diff --git a/apps/www/public/r/ripple-button.json b/apps/www/public/r/ripple-button.json index 2731cb25c..cd88d83d5 100644 --- a/apps/www/public/r/ripple-button.json +++ b/apps/www/public/r/ripple-button.json @@ -7,7 +7,7 @@ "files": [ { "path": "registry/magicui/ripple-button.tsx", - "content": "\"use client\"\n\nimport React, { MouseEvent, useEffect, useState } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface RippleButtonProps extends React.ButtonHTMLAttributes {\n rippleColor?: string\n duration?: string\n}\n\nexport const RippleButton = React.forwardRef<\n HTMLButtonElement,\n RippleButtonProps\n>(\n (\n {\n className,\n children,\n rippleColor = \"#ffffff\",\n duration = \"600ms\",\n onClick,\n ...props\n },\n ref\n ) => {\n const [buttonRipples, setButtonRipples] = useState<\n Array<{ x: number; y: number; size: number; key: number }>\n >([])\n\n const handleClick = (event: MouseEvent) => {\n createRipple(event)\n onClick?.(event)\n }\n\n const createRipple = (event: MouseEvent) => {\n const button = event.currentTarget\n const rect = button.getBoundingClientRect()\n const size = Math.max(rect.width, rect.height)\n const x = event.clientX - rect.left - size / 2\n const y = event.clientY - rect.top - size / 2\n\n const newRipple = { x, y, size, key: Date.now() }\n setButtonRipples((prevRipples) => [...prevRipples, newRipple])\n }\n\n useEffect(() => {\n let timeout: ReturnType | null = null\n\n if (buttonRipples.length > 0) {\n const lastRipple = buttonRipples[buttonRipples.length - 1]\n timeout = setTimeout(() => {\n setButtonRipples((prevRipples) =>\n prevRipples.filter((ripple) => ripple.key !== lastRipple.key)\n )\n }, parseInt(duration))\n }\n\n return () => {\n if (timeout !== null) {\n clearTimeout(timeout)\n }\n }\n }, [buttonRipples, duration])\n\n return (\n \n
{children}
\n \n {buttonRipples.map((ripple) => (\n \n ))}\n \n \n )\n }\n)\n\nRippleButton.displayName = \"RippleButton\"\n", + "content": "\"use client\"\n\nimport React, { useEffect, useState, type MouseEvent } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface RippleButtonProps extends React.ButtonHTMLAttributes {\n rippleColor?: string\n duration?: string\n}\n\nexport const RippleButton = React.forwardRef<\n HTMLButtonElement,\n RippleButtonProps\n>(\n (\n {\n className,\n children,\n rippleColor = \"#ffffff\",\n duration = \"600ms\",\n onClick,\n ...props\n },\n ref\n ) => {\n const [buttonRipples, setButtonRipples] = useState<\n Array<{ x: number; y: number; size: number; key: number }>\n >([])\n\n const handleClick = (event: MouseEvent) => {\n createRipple(event)\n onClick?.(event)\n }\n\n const createRipple = (event: MouseEvent) => {\n const button = event.currentTarget\n const rect = button.getBoundingClientRect()\n const size = Math.max(rect.width, rect.height)\n const x = event.clientX - rect.left - size / 2\n const y = event.clientY - rect.top - size / 2\n\n const newRipple = { x, y, size, key: Date.now() }\n setButtonRipples((prevRipples) => [...prevRipples, newRipple])\n }\n\n useEffect(() => {\n let timeout: ReturnType | null = null\n\n if (buttonRipples.length > 0) {\n const lastRipple = buttonRipples[buttonRipples.length - 1]\n timeout = setTimeout(() => {\n setButtonRipples((prevRipples) =>\n prevRipples.filter((ripple) => ripple.key !== lastRipple.key)\n )\n }, parseInt(duration))\n }\n\n return () => {\n if (timeout !== null) {\n clearTimeout(timeout)\n }\n }\n }, [buttonRipples, duration])\n\n return (\n \n
{children}
\n \n {buttonRipples.map((ripple) => (\n \n ))}\n \n \n )\n }\n)\n\nRippleButton.displayName = \"RippleButton\"\n", "type": "registry:ui" } ], diff --git a/apps/www/public/r/smooth-cursor.json b/apps/www/public/r/smooth-cursor.json index bc7635363..95432d24b 100644 --- a/apps/www/public/r/smooth-cursor.json +++ b/apps/www/public/r/smooth-cursor.json @@ -9,7 +9,7 @@ "files": [ { "path": "registry/magicui/smooth-cursor.tsx", - "content": "\"use client\"\n\nimport { FC, useEffect, useRef, useState } from \"react\"\nimport { motion, useSpring } from \"motion/react\"\n\ninterface Position {\n x: number\n y: number\n}\n\nexport interface SmoothCursorProps {\n cursor?: React.ReactNode\n springConfig?: {\n damping: number\n stiffness: number\n mass: number\n restDelta: number\n }\n}\n\nconst DESKTOP_POINTER_QUERY = \"(any-hover: hover) and (any-pointer: fine)\"\n\nfunction isTrackablePointer(pointerType: string) {\n return pointerType !== \"touch\"\n}\n\nconst DefaultCursorSVG: FC = () => {\n return (\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n )\n}\n\nexport function SmoothCursor({\n cursor = ,\n springConfig = {\n damping: 45,\n stiffness: 400,\n mass: 1,\n restDelta: 0.001,\n },\n}: SmoothCursorProps) {\n const lastMousePos = useRef({ x: 0, y: 0 })\n const velocity = useRef({ x: 0, y: 0 })\n const lastUpdateTime = useRef(Date.now())\n const previousAngle = useRef(0)\n const accumulatedRotation = useRef(0)\n const [isEnabled, setIsEnabled] = useState(false)\n const [isVisible, setIsVisible] = useState(false)\n\n const cursorX = useSpring(0, springConfig)\n const cursorY = useSpring(0, springConfig)\n const rotation = useSpring(0, {\n ...springConfig,\n damping: 60,\n stiffness: 300,\n })\n const scale = useSpring(1, {\n ...springConfig,\n stiffness: 500,\n damping: 35,\n })\n\n useEffect(() => {\n const mediaQuery = window.matchMedia(DESKTOP_POINTER_QUERY)\n\n const updateEnabled = () => {\n const nextIsEnabled = mediaQuery.matches\n setIsEnabled(nextIsEnabled)\n\n if (!nextIsEnabled) {\n setIsVisible(false)\n }\n }\n\n updateEnabled()\n mediaQuery.addEventListener(\"change\", updateEnabled)\n\n return () => {\n mediaQuery.removeEventListener(\"change\", updateEnabled)\n }\n }, [])\n\n useEffect(() => {\n if (!isEnabled) {\n return\n }\n\n let timeout: ReturnType | null = null\n\n const updateVelocity = (currentPos: Position) => {\n const currentTime = Date.now()\n const deltaTime = currentTime - lastUpdateTime.current\n\n if (deltaTime > 0) {\n velocity.current = {\n x: (currentPos.x - lastMousePos.current.x) / deltaTime,\n y: (currentPos.y - lastMousePos.current.y) / deltaTime,\n }\n }\n\n lastUpdateTime.current = currentTime\n lastMousePos.current = currentPos\n }\n\n const smoothPointerMove = (e: PointerEvent) => {\n if (!isTrackablePointer(e.pointerType)) {\n return\n }\n\n setIsVisible(true)\n\n const currentPos = { x: e.clientX, y: e.clientY }\n updateVelocity(currentPos)\n\n const speed = Math.sqrt(\n Math.pow(velocity.current.x, 2) + Math.pow(velocity.current.y, 2)\n )\n\n cursorX.set(currentPos.x)\n cursorY.set(currentPos.y)\n\n if (speed > 0.1) {\n const currentAngle =\n Math.atan2(velocity.current.y, velocity.current.x) * (180 / Math.PI) +\n 90\n\n let angleDiff = currentAngle - previousAngle.current\n if (angleDiff > 180) angleDiff -= 360\n if (angleDiff < -180) angleDiff += 360\n accumulatedRotation.current += angleDiff\n rotation.set(accumulatedRotation.current)\n previousAngle.current = currentAngle\n\n scale.set(0.95)\n\n if (timeout !== null) {\n clearTimeout(timeout)\n }\n\n timeout = setTimeout(() => {\n scale.set(1)\n }, 150)\n }\n }\n\n let rafId = 0\n const throttledPointerMove = (e: PointerEvent) => {\n if (!isTrackablePointer(e.pointerType)) {\n return\n }\n\n if (rafId) return\n\n rafId = requestAnimationFrame(() => {\n smoothPointerMove(e)\n rafId = 0\n })\n }\n\n document.body.style.cursor = \"none\"\n window.addEventListener(\"pointermove\", throttledPointerMove, {\n passive: true,\n })\n\n return () => {\n window.removeEventListener(\"pointermove\", throttledPointerMove)\n document.body.style.cursor = \"auto\"\n if (rafId) cancelAnimationFrame(rafId)\n if (timeout !== null) {\n clearTimeout(timeout)\n }\n }\n }, [cursorX, cursorY, rotation, scale, isEnabled])\n\n if (!isEnabled) {\n return null\n }\n\n return (\n \n {cursor}\n \n )\n}\n", + "content": "\"use client\"\n\nimport { useEffect, useRef, useState, type FC } from \"react\"\nimport { motion, useSpring } from \"motion/react\"\n\ninterface Position {\n x: number\n y: number\n}\n\nexport interface SmoothCursorProps {\n cursor?: React.ReactNode\n springConfig?: {\n damping: number\n stiffness: number\n mass: number\n restDelta: number\n }\n}\n\nconst DESKTOP_POINTER_QUERY = \"(any-hover: hover) and (any-pointer: fine)\"\n\nfunction isTrackablePointer(pointerType: string) {\n return pointerType !== \"touch\"\n}\n\nconst DefaultCursorSVG: FC = () => {\n return (\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n )\n}\n\nexport function SmoothCursor({\n cursor = ,\n springConfig = {\n damping: 45,\n stiffness: 400,\n mass: 1,\n restDelta: 0.001,\n },\n}: SmoothCursorProps) {\n const lastMousePos = useRef({ x: 0, y: 0 })\n const velocity = useRef({ x: 0, y: 0 })\n const lastUpdateTime = useRef(Date.now())\n const previousAngle = useRef(0)\n const accumulatedRotation = useRef(0)\n const [isEnabled, setIsEnabled] = useState(false)\n const [isVisible, setIsVisible] = useState(false)\n\n const cursorX = useSpring(0, springConfig)\n const cursorY = useSpring(0, springConfig)\n const rotation = useSpring(0, {\n ...springConfig,\n damping: 60,\n stiffness: 300,\n })\n const scale = useSpring(1, {\n ...springConfig,\n stiffness: 500,\n damping: 35,\n })\n\n useEffect(() => {\n const mediaQuery = window.matchMedia(DESKTOP_POINTER_QUERY)\n\n const updateEnabled = () => {\n const nextIsEnabled = mediaQuery.matches\n setIsEnabled(nextIsEnabled)\n\n if (!nextIsEnabled) {\n setIsVisible(false)\n }\n }\n\n updateEnabled()\n mediaQuery.addEventListener(\"change\", updateEnabled)\n\n return () => {\n mediaQuery.removeEventListener(\"change\", updateEnabled)\n }\n }, [])\n\n useEffect(() => {\n if (!isEnabled) {\n return\n }\n\n let timeout: ReturnType | null = null\n\n const updateVelocity = (currentPos: Position) => {\n const currentTime = Date.now()\n const deltaTime = currentTime - lastUpdateTime.current\n\n if (deltaTime > 0) {\n velocity.current = {\n x: (currentPos.x - lastMousePos.current.x) / deltaTime,\n y: (currentPos.y - lastMousePos.current.y) / deltaTime,\n }\n }\n\n lastUpdateTime.current = currentTime\n lastMousePos.current = currentPos\n }\n\n const smoothPointerMove = (e: PointerEvent) => {\n if (!isTrackablePointer(e.pointerType)) {\n return\n }\n\n setIsVisible(true)\n\n const currentPos = { x: e.clientX, y: e.clientY }\n updateVelocity(currentPos)\n\n const speed = Math.sqrt(\n Math.pow(velocity.current.x, 2) + Math.pow(velocity.current.y, 2)\n )\n\n cursorX.set(currentPos.x)\n cursorY.set(currentPos.y)\n\n if (speed > 0.1) {\n const currentAngle =\n Math.atan2(velocity.current.y, velocity.current.x) * (180 / Math.PI) +\n 90\n\n let angleDiff = currentAngle - previousAngle.current\n if (angleDiff > 180) angleDiff -= 360\n if (angleDiff < -180) angleDiff += 360\n accumulatedRotation.current += angleDiff\n rotation.set(accumulatedRotation.current)\n previousAngle.current = currentAngle\n\n scale.set(0.95)\n\n if (timeout !== null) {\n clearTimeout(timeout)\n }\n\n timeout = setTimeout(() => {\n scale.set(1)\n }, 150)\n }\n }\n\n let rafId = 0\n const throttledPointerMove = (e: PointerEvent) => {\n if (!isTrackablePointer(e.pointerType)) {\n return\n }\n\n if (rafId) return\n\n rafId = requestAnimationFrame(() => {\n smoothPointerMove(e)\n rafId = 0\n })\n }\n\n document.body.style.cursor = \"none\"\n window.addEventListener(\"pointermove\", throttledPointerMove, {\n passive: true,\n })\n\n return () => {\n window.removeEventListener(\"pointermove\", throttledPointerMove)\n document.body.style.cursor = \"auto\"\n if (rafId) cancelAnimationFrame(rafId)\n if (timeout !== null) {\n clearTimeout(timeout)\n }\n }\n }, [cursorX, cursorY, rotation, scale, isEnabled])\n\n if (!isEnabled) {\n return null\n }\n\n return (\n \n {cursor}\n \n )\n}\n", "type": "registry:ui" } ] diff --git a/apps/www/public/r/sparkles-text.json b/apps/www/public/r/sparkles-text.json index 22a08ec5b..67d1fcd31 100644 --- a/apps/www/public/r/sparkles-text.json +++ b/apps/www/public/r/sparkles-text.json @@ -10,7 +10,7 @@ "files": [ { "path": "registry/magicui/sparkles-text.tsx", - "content": "\"use client\"\n\nimport { CSSProperties, ReactElement, useEffect, useState } from \"react\"\nimport { motion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface Sparkle {\n id: string\n x: string\n y: string\n color: string\n delay: number\n scale: number\n lifespan: number\n}\n\nconst Sparkle: React.FC = ({ id, x, y, color, delay, scale }) => {\n return (\n \n \n \n )\n}\n\ninterface SparklesTextProps {\n /**\n * @default
\n * @type ReactElement\n * @description\n * The component to be rendered as the text\n * */\n as?: ReactElement\n\n /**\n * @default \"\"\n * @type string\n * @description\n * The className of the text\n */\n className?: string\n\n /**\n * @required\n * @type ReactNode\n * @description\n * The content to be displayed\n * */\n children: React.ReactNode\n\n /**\n * @default 10\n * @type number\n * @description\n * The count of sparkles\n * */\n sparklesCount?: number\n\n /**\n * @default \"{first: '#9E7AFF', second: '#FE8BBB'}\"\n * @type string\n * @description\n * The colors of the sparkles\n * */\n colors?: {\n first: string\n second: string\n }\n}\n\nexport const SparklesText: React.FC = ({\n children,\n colors = { first: \"#9E7AFF\", second: \"#FE8BBB\" },\n className,\n sparklesCount = 10,\n ...props\n}) => {\n const [sparkles, setSparkles] = useState([])\n\n useEffect(() => {\n const generateStar = (): Sparkle => {\n const starX = `${Math.random() * 100}%`\n const starY = `${Math.random() * 100}%`\n const color = Math.random() > 0.5 ? colors.first : colors.second\n const delay = Math.random() * 2\n const scale = Math.random() * 1 + 0.3\n const lifespan = Math.random() * 10 + 5\n const id = `${starX}-${starY}-${Date.now()}`\n return { id, x: starX, y: starY, color, delay, scale, lifespan }\n }\n\n const initializeStars = () => {\n const newSparkles = Array.from({ length: sparklesCount }, generateStar)\n setSparkles(newSparkles)\n }\n\n const updateStars = () => {\n setSparkles((currentSparkles) =>\n currentSparkles.map((star) => {\n if (star.lifespan <= 0) {\n return generateStar()\n } else {\n return { ...star, lifespan: star.lifespan - 0.1 }\n }\n })\n )\n }\n\n initializeStars()\n const interval = setInterval(updateStars, 100)\n\n return () => clearInterval(interval)\n }, [colors.first, colors.second, sparklesCount])\n\n return (\n \n \n {sparkles.map((sparkle) => (\n \n ))}\n {children}\n \n
\n )\n}\n", + "content": "\"use client\"\n\nimport {\n useEffect,\n useState,\n type CSSProperties,\n type ReactElement,\n} from \"react\"\nimport { motion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface Sparkle {\n id: string\n x: string\n y: string\n color: string\n delay: number\n scale: number\n lifespan: number\n}\n\nconst Sparkle: React.FC = ({ id, x, y, color, delay, scale }) => {\n return (\n \n \n \n )\n}\n\ninterface SparklesTextProps {\n /**\n * @default
\n * @type ReactElement\n * @description\n * The component to be rendered as the text\n * */\n as?: ReactElement\n\n /**\n * @default \"\"\n * @type string\n * @description\n * The className of the text\n */\n className?: string\n\n /**\n * @required\n * @type ReactNode\n * @description\n * The content to be displayed\n * */\n children: React.ReactNode\n\n /**\n * @default 10\n * @type number\n * @description\n * The count of sparkles\n * */\n sparklesCount?: number\n\n /**\n * @default \"{first: '#9E7AFF', second: '#FE8BBB'}\"\n * @type string\n * @description\n * The colors of the sparkles\n * */\n colors?: {\n first: string\n second: string\n }\n}\n\nexport const SparklesText: React.FC = ({\n children,\n colors = { first: \"#9E7AFF\", second: \"#FE8BBB\" },\n className,\n sparklesCount = 10,\n ...props\n}) => {\n const [sparkles, setSparkles] = useState([])\n\n useEffect(() => {\n const generateStar = (): Sparkle => {\n const starX = `${Math.random() * 100}%`\n const starY = `${Math.random() * 100}%`\n const color = Math.random() > 0.5 ? colors.first : colors.second\n const delay = Math.random() * 2\n const scale = Math.random() * 1 + 0.3\n const lifespan = Math.random() * 10 + 5\n const id = `${starX}-${starY}-${Date.now()}`\n return { id, x: starX, y: starY, color, delay, scale, lifespan }\n }\n\n const initializeStars = () => {\n const newSparkles = Array.from({ length: sparklesCount }, generateStar)\n setSparkles(newSparkles)\n }\n\n const updateStars = () => {\n setSparkles((currentSparkles) =>\n currentSparkles.map((star) => {\n if (star.lifespan <= 0) {\n return generateStar()\n } else {\n return { ...star, lifespan: star.lifespan - 0.1 }\n }\n })\n )\n }\n\n initializeStars()\n const interval = setInterval(updateStars, 100)\n\n return () => clearInterval(interval)\n }, [colors.first, colors.second, sparklesCount])\n\n return (\n \n \n {sparkles.map((sparkle) => (\n \n ))}\n {children}\n \n
\n )\n}\n", "type": "registry:ui" } ] diff --git a/apps/www/public/r/spinning-text.json b/apps/www/public/r/spinning-text.json index ab0317cca..e711e3286 100644 --- a/apps/www/public/r/spinning-text.json +++ b/apps/www/public/r/spinning-text.json @@ -10,7 +10,7 @@ "files": [ { "path": "registry/magicui/spinning-text.tsx", - "content": "\"use client\"\n\nimport React, { type ComponentPropsWithoutRef } from \"react\"\nimport { motion, Transition, Variants } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface SpinningTextProps extends ComponentPropsWithoutRef<\"div\"> {\n children: string | string[]\n duration?: number\n reverse?: boolean\n radius?: number\n transition?: Transition\n variants?: {\n container?: Variants\n item?: Variants\n }\n}\n\nconst BASE_TRANSITION: Transition = {\n repeat: Infinity,\n ease: \"linear\",\n}\n\nconst BASE_ITEM_VARIANTS: Variants = {\n hidden: {\n opacity: 1,\n },\n visible: {\n opacity: 1,\n },\n}\n\nexport function SpinningText({\n children,\n duration = 10,\n reverse = false,\n radius = 5,\n transition,\n variants,\n className,\n style,\n}: SpinningTextProps) {\n if (typeof children !== \"string\" && !Array.isArray(children)) {\n throw new Error(\"children must be a string or an array of strings\")\n }\n\n if (Array.isArray(children)) {\n // Validate all elements are strings\n if (!children.every((child) => typeof child === \"string\")) {\n throw new Error(\"all elements in children array must be strings\")\n }\n children = children.join(\"\")\n }\n\n const letters = children.split(\"\")\n letters.push(\" \")\n\n const finalTransition: Transition = {\n ...BASE_TRANSITION,\n ...transition,\n duration: (transition as { duration?: number })?.duration ?? duration,\n }\n\n const containerVariants: Variants = {\n visible: { rotate: reverse ? -360 : 360 },\n ...variants?.container,\n }\n\n const itemVariants: Variants = {\n ...BASE_ITEM_VARIANTS,\n ...variants?.item,\n }\n\n return (\n \n {letters.map((letter, index) => (\n \n {letter}\n \n ))}\n {children}\n \n )\n}\n", + "content": "\"use client\"\n\nimport React, { type ComponentPropsWithoutRef } from \"react\"\nimport { motion, type Transition, type Variants } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface SpinningTextProps extends ComponentPropsWithoutRef<\"div\"> {\n children: string | string[]\n duration?: number\n reverse?: boolean\n radius?: number\n transition?: Transition\n variants?: {\n container?: Variants\n item?: Variants\n }\n}\n\nconst BASE_TRANSITION: Transition = {\n repeat: Infinity,\n ease: \"linear\",\n}\n\nconst BASE_ITEM_VARIANTS: Variants = {\n hidden: {\n opacity: 1,\n },\n visible: {\n opacity: 1,\n },\n}\n\nexport function SpinningText({\n children,\n duration = 10,\n reverse = false,\n radius = 5,\n transition,\n variants,\n className,\n style,\n}: SpinningTextProps) {\n if (typeof children !== \"string\" && !Array.isArray(children)) {\n throw new Error(\"children must be a string or an array of strings\")\n }\n\n if (Array.isArray(children)) {\n // Validate all elements are strings\n if (!children.every((child) => typeof child === \"string\")) {\n throw new Error(\"all elements in children array must be strings\")\n }\n children = children.join(\"\")\n }\n\n const letters = children.split(\"\")\n letters.push(\" \")\n\n const finalTransition: Transition = {\n ...BASE_TRANSITION,\n ...transition,\n duration: (transition as { duration?: number })?.duration ?? duration,\n }\n\n const containerVariants: Variants = {\n visible: { rotate: reverse ? -360 : 360 },\n ...variants?.container,\n }\n\n const itemVariants: Variants = {\n ...BASE_ITEM_VARIANTS,\n ...variants?.item,\n }\n\n return (\n \n {letters.map((letter, index) => (\n \n {letter}\n \n ))}\n {children}\n \n )\n}\n", "type": "registry:ui" } ] diff --git a/apps/www/public/r/text-animate.json b/apps/www/public/r/text-animate.json index 5ad60f8cd..f5a87dba7 100644 --- a/apps/www/public/r/text-animate.json +++ b/apps/www/public/r/text-animate.json @@ -10,7 +10,7 @@ "files": [ { "path": "registry/magicui/text-animate.tsx", - "content": "\"use client\"\n\nimport { memo } from \"react\"\nimport {\n AnimatePresence,\n motion,\n Variants,\n type DOMMotionComponents,\n type MotionProps,\n} from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype AnimationType = \"text\" | \"word\" | \"character\" | \"line\"\ntype AnimationVariant =\n | \"fadeIn\"\n | \"blurIn\"\n | \"blurInUp\"\n | \"blurInDown\"\n | \"slideUp\"\n | \"slideDown\"\n | \"slideLeft\"\n | \"slideRight\"\n | \"scaleUp\"\n | \"scaleDown\"\n\nconst motionElements = {\n article: motion.article,\n div: motion.div,\n h1: motion.h1,\n h2: motion.h2,\n h3: motion.h3,\n h4: motion.h4,\n h5: motion.h5,\n h6: motion.h6,\n li: motion.li,\n p: motion.p,\n section: motion.section,\n span: motion.span,\n} as const\n\ntype MotionElementType = Extract<\n keyof DOMMotionComponents,\n keyof typeof motionElements\n>\n\ninterface TextAnimateProps extends Omit {\n /**\n * The text content to animate\n */\n children: string\n /**\n * The class name to be applied to the component\n */\n className?: string\n /**\n * The class name to be applied to each segment\n */\n segmentClassName?: string\n /**\n * The delay before the animation starts\n */\n delay?: number\n /**\n * The duration of the animation\n */\n duration?: number\n /**\n * Custom motion variants for the animation\n */\n variants?: Variants\n /**\n * The element type to render\n */\n as?: MotionElementType\n /**\n * How to split the text (\"text\", \"word\", \"character\")\n */\n by?: AnimationType\n /**\n * Whether to start animation when component enters viewport\n */\n startOnView?: boolean\n /**\n * Whether to animate only once\n */\n once?: boolean\n /**\n * The animation preset to use\n */\n animation?: AnimationVariant\n /**\n * Whether to enable accessibility features (default: true)\n */\n accessible?: boolean\n}\n\nconst staggerTimings: Record = {\n text: 0.06,\n word: 0.05,\n character: 0.03,\n line: 0.06,\n}\n\nconst defaultContainerVariants = {\n hidden: { opacity: 1 },\n show: {\n opacity: 1,\n transition: {\n delayChildren: 0,\n staggerChildren: 0.05,\n },\n },\n exit: {\n opacity: 0,\n transition: {\n staggerChildren: 0.05,\n staggerDirection: -1,\n },\n },\n}\n\nconst defaultItemVariants: Variants = {\n hidden: { opacity: 0 },\n show: {\n opacity: 1,\n },\n exit: {\n opacity: 0,\n },\n}\n\nconst defaultItemAnimationVariants: Record<\n AnimationVariant,\n { container: Variants; item: Variants }\n> = {\n fadeIn: {\n container: defaultContainerVariants,\n item: {\n hidden: { opacity: 0, y: 20 },\n show: {\n opacity: 1,\n y: 0,\n transition: {\n duration: 0.3,\n },\n },\n exit: {\n opacity: 0,\n y: 20,\n transition: { duration: 0.3 },\n },\n },\n },\n blurIn: {\n container: defaultContainerVariants,\n item: {\n hidden: { opacity: 0, filter: \"blur(10px)\" },\n show: {\n opacity: 1,\n filter: \"blur(0px)\",\n transition: {\n duration: 0.3,\n },\n },\n exit: {\n opacity: 0,\n filter: \"blur(10px)\",\n transition: { duration: 0.3 },\n },\n },\n },\n blurInUp: {\n container: defaultContainerVariants,\n item: {\n hidden: { opacity: 0, filter: \"blur(10px)\", y: 20 },\n show: {\n opacity: 1,\n filter: \"blur(0px)\",\n y: 0,\n transition: {\n y: { duration: 0.3 },\n opacity: { duration: 0.4 },\n filter: { duration: 0.3 },\n },\n },\n exit: {\n opacity: 0,\n filter: \"blur(10px)\",\n y: 20,\n transition: {\n y: { duration: 0.3 },\n opacity: { duration: 0.4 },\n filter: { duration: 0.3 },\n },\n },\n },\n },\n blurInDown: {\n container: defaultContainerVariants,\n item: {\n hidden: { opacity: 0, filter: \"blur(10px)\", y: -20 },\n show: {\n opacity: 1,\n filter: \"blur(0px)\",\n y: 0,\n transition: {\n y: { duration: 0.3 },\n opacity: { duration: 0.4 },\n filter: { duration: 0.3 },\n },\n },\n },\n },\n slideUp: {\n container: defaultContainerVariants,\n item: {\n hidden: { y: 20, opacity: 0 },\n show: {\n y: 0,\n opacity: 1,\n transition: {\n duration: 0.3,\n },\n },\n exit: {\n y: -20,\n opacity: 0,\n transition: {\n duration: 0.3,\n },\n },\n },\n },\n slideDown: {\n container: defaultContainerVariants,\n item: {\n hidden: { y: -20, opacity: 0 },\n show: {\n y: 0,\n opacity: 1,\n transition: { duration: 0.3 },\n },\n exit: {\n y: 20,\n opacity: 0,\n transition: { duration: 0.3 },\n },\n },\n },\n slideLeft: {\n container: defaultContainerVariants,\n item: {\n hidden: { x: 20, opacity: 0 },\n show: {\n x: 0,\n opacity: 1,\n transition: { duration: 0.3 },\n },\n exit: {\n x: -20,\n opacity: 0,\n transition: { duration: 0.3 },\n },\n },\n },\n slideRight: {\n container: defaultContainerVariants,\n item: {\n hidden: { x: -20, opacity: 0 },\n show: {\n x: 0,\n opacity: 1,\n transition: { duration: 0.3 },\n },\n exit: {\n x: 20,\n opacity: 0,\n transition: { duration: 0.3 },\n },\n },\n },\n scaleUp: {\n container: defaultContainerVariants,\n item: {\n hidden: { scale: 0.5, opacity: 0 },\n show: {\n scale: 1,\n opacity: 1,\n transition: {\n duration: 0.3,\n scale: {\n type: \"spring\",\n damping: 15,\n stiffness: 300,\n },\n },\n },\n exit: {\n scale: 0.5,\n opacity: 0,\n transition: { duration: 0.3 },\n },\n },\n },\n scaleDown: {\n container: defaultContainerVariants,\n item: {\n hidden: { scale: 1.5, opacity: 0 },\n show: {\n scale: 1,\n opacity: 1,\n transition: {\n duration: 0.3,\n scale: {\n type: \"spring\",\n damping: 15,\n stiffness: 300,\n },\n },\n },\n exit: {\n scale: 1.5,\n opacity: 0,\n transition: { duration: 0.3 },\n },\n },\n },\n}\n\nconst TextAnimateBase = ({\n children,\n delay = 0,\n duration = 0.3,\n variants,\n className,\n segmentClassName,\n as: Component = \"p\",\n startOnView = true,\n once = false,\n by = \"word\",\n animation = \"fadeIn\",\n accessible = true,\n ...props\n}: TextAnimateProps) => {\n const MotionComponent = motionElements[Component]\n\n let segments: string[] = []\n switch (by) {\n case \"word\":\n segments = children.split(/(\\s+)/)\n break\n case \"character\":\n segments = children.split(\"\")\n break\n case \"line\":\n segments = children.split(\"\\n\")\n break\n case \"text\":\n default:\n segments = [children]\n break\n }\n\n const finalVariants = variants\n ? {\n container: {\n hidden: { opacity: 0 },\n show: {\n opacity: 1,\n transition: {\n opacity: { duration: 0.01, delay },\n delayChildren: delay,\n staggerChildren: duration / segments.length,\n },\n },\n exit: {\n opacity: 0,\n transition: {\n staggerChildren: duration / segments.length,\n staggerDirection: -1,\n },\n },\n },\n item: variants,\n }\n : animation\n ? {\n container: {\n ...defaultItemAnimationVariants[animation].container,\n show: {\n ...defaultItemAnimationVariants[animation].container.show,\n transition: {\n delayChildren: delay,\n staggerChildren: duration / segments.length,\n },\n },\n exit: {\n ...defaultItemAnimationVariants[animation].container.exit,\n transition: {\n staggerChildren: duration / segments.length,\n staggerDirection: -1,\n },\n },\n },\n item: defaultItemAnimationVariants[animation].item,\n }\n : { container: defaultContainerVariants, item: defaultItemVariants }\n\n return (\n \n \n {accessible && {children}}\n {segments.map((segment, i) => (\n \n {segment}\n \n ))}\n \n \n )\n}\n\n// Export the memoized version\nexport const TextAnimate = memo(TextAnimateBase)\n", + "content": "\"use client\"\n\nimport { memo } from \"react\"\nimport {\n AnimatePresence,\n motion,\n type DOMMotionComponents,\n type MotionProps,\n type Variants,\n} from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ntype AnimationType = \"text\" | \"word\" | \"character\" | \"line\"\ntype AnimationVariant =\n | \"fadeIn\"\n | \"blurIn\"\n | \"blurInUp\"\n | \"blurInDown\"\n | \"slideUp\"\n | \"slideDown\"\n | \"slideLeft\"\n | \"slideRight\"\n | \"scaleUp\"\n | \"scaleDown\"\n\nconst motionElements = {\n article: motion.article,\n div: motion.div,\n h1: motion.h1,\n h2: motion.h2,\n h3: motion.h3,\n h4: motion.h4,\n h5: motion.h5,\n h6: motion.h6,\n li: motion.li,\n p: motion.p,\n section: motion.section,\n span: motion.span,\n} as const\n\ntype MotionElementType = Extract<\n keyof DOMMotionComponents,\n keyof typeof motionElements\n>\n\ninterface TextAnimateProps extends Omit {\n /**\n * The text content to animate\n */\n children: string\n /**\n * The class name to be applied to the component\n */\n className?: string\n /**\n * The class name to be applied to each segment\n */\n segmentClassName?: string\n /**\n * The delay before the animation starts\n */\n delay?: number\n /**\n * The duration of the animation\n */\n duration?: number\n /**\n * Custom motion variants for the animation\n */\n variants?: Variants\n /**\n * The element type to render\n */\n as?: MotionElementType\n /**\n * How to split the text (\"text\", \"word\", \"character\")\n */\n by?: AnimationType\n /**\n * Whether to start animation when component enters viewport\n */\n startOnView?: boolean\n /**\n * Whether to animate only once\n */\n once?: boolean\n /**\n * The animation preset to use\n */\n animation?: AnimationVariant\n /**\n * Whether to enable accessibility features (default: true)\n */\n accessible?: boolean\n}\n\nconst staggerTimings: Record = {\n text: 0.06,\n word: 0.05,\n character: 0.03,\n line: 0.06,\n}\n\nconst defaultContainerVariants = {\n hidden: { opacity: 1 },\n show: {\n opacity: 1,\n transition: {\n delayChildren: 0,\n staggerChildren: 0.05,\n },\n },\n exit: {\n opacity: 0,\n transition: {\n staggerChildren: 0.05,\n staggerDirection: -1,\n },\n },\n}\n\nconst defaultItemVariants: Variants = {\n hidden: { opacity: 0 },\n show: {\n opacity: 1,\n },\n exit: {\n opacity: 0,\n },\n}\n\nconst defaultItemAnimationVariants: Record<\n AnimationVariant,\n { container: Variants; item: Variants }\n> = {\n fadeIn: {\n container: defaultContainerVariants,\n item: {\n hidden: { opacity: 0, y: 20 },\n show: {\n opacity: 1,\n y: 0,\n transition: {\n duration: 0.3,\n },\n },\n exit: {\n opacity: 0,\n y: 20,\n transition: { duration: 0.3 },\n },\n },\n },\n blurIn: {\n container: defaultContainerVariants,\n item: {\n hidden: { opacity: 0, filter: \"blur(10px)\" },\n show: {\n opacity: 1,\n filter: \"blur(0px)\",\n transition: {\n duration: 0.3,\n },\n },\n exit: {\n opacity: 0,\n filter: \"blur(10px)\",\n transition: { duration: 0.3 },\n },\n },\n },\n blurInUp: {\n container: defaultContainerVariants,\n item: {\n hidden: { opacity: 0, filter: \"blur(10px)\", y: 20 },\n show: {\n opacity: 1,\n filter: \"blur(0px)\",\n y: 0,\n transition: {\n y: { duration: 0.3 },\n opacity: { duration: 0.4 },\n filter: { duration: 0.3 },\n },\n },\n exit: {\n opacity: 0,\n filter: \"blur(10px)\",\n y: 20,\n transition: {\n y: { duration: 0.3 },\n opacity: { duration: 0.4 },\n filter: { duration: 0.3 },\n },\n },\n },\n },\n blurInDown: {\n container: defaultContainerVariants,\n item: {\n hidden: { opacity: 0, filter: \"blur(10px)\", y: -20 },\n show: {\n opacity: 1,\n filter: \"blur(0px)\",\n y: 0,\n transition: {\n y: { duration: 0.3 },\n opacity: { duration: 0.4 },\n filter: { duration: 0.3 },\n },\n },\n },\n },\n slideUp: {\n container: defaultContainerVariants,\n item: {\n hidden: { y: 20, opacity: 0 },\n show: {\n y: 0,\n opacity: 1,\n transition: {\n duration: 0.3,\n },\n },\n exit: {\n y: -20,\n opacity: 0,\n transition: {\n duration: 0.3,\n },\n },\n },\n },\n slideDown: {\n container: defaultContainerVariants,\n item: {\n hidden: { y: -20, opacity: 0 },\n show: {\n y: 0,\n opacity: 1,\n transition: { duration: 0.3 },\n },\n exit: {\n y: 20,\n opacity: 0,\n transition: { duration: 0.3 },\n },\n },\n },\n slideLeft: {\n container: defaultContainerVariants,\n item: {\n hidden: { x: 20, opacity: 0 },\n show: {\n x: 0,\n opacity: 1,\n transition: { duration: 0.3 },\n },\n exit: {\n x: -20,\n opacity: 0,\n transition: { duration: 0.3 },\n },\n },\n },\n slideRight: {\n container: defaultContainerVariants,\n item: {\n hidden: { x: -20, opacity: 0 },\n show: {\n x: 0,\n opacity: 1,\n transition: { duration: 0.3 },\n },\n exit: {\n x: 20,\n opacity: 0,\n transition: { duration: 0.3 },\n },\n },\n },\n scaleUp: {\n container: defaultContainerVariants,\n item: {\n hidden: { scale: 0.5, opacity: 0 },\n show: {\n scale: 1,\n opacity: 1,\n transition: {\n duration: 0.3,\n scale: {\n type: \"spring\",\n damping: 15,\n stiffness: 300,\n },\n },\n },\n exit: {\n scale: 0.5,\n opacity: 0,\n transition: { duration: 0.3 },\n },\n },\n },\n scaleDown: {\n container: defaultContainerVariants,\n item: {\n hidden: { scale: 1.5, opacity: 0 },\n show: {\n scale: 1,\n opacity: 1,\n transition: {\n duration: 0.3,\n scale: {\n type: \"spring\",\n damping: 15,\n stiffness: 300,\n },\n },\n },\n exit: {\n scale: 1.5,\n opacity: 0,\n transition: { duration: 0.3 },\n },\n },\n },\n}\n\nconst TextAnimateBase = ({\n children,\n delay = 0,\n duration = 0.3,\n variants,\n className,\n segmentClassName,\n as: Component = \"p\",\n startOnView = true,\n once = false,\n by = \"word\",\n animation = \"fadeIn\",\n accessible = true,\n ...props\n}: TextAnimateProps) => {\n const MotionComponent = motionElements[Component]\n\n let segments: string[] = []\n switch (by) {\n case \"word\":\n segments = children.split(/(\\s+)/)\n break\n case \"character\":\n segments = children.split(\"\")\n break\n case \"line\":\n segments = children.split(\"\\n\")\n break\n case \"text\":\n default:\n segments = [children]\n break\n }\n\n const finalVariants = variants\n ? {\n container: {\n hidden: { opacity: 0 },\n show: {\n opacity: 1,\n transition: {\n opacity: { duration: 0.01, delay },\n delayChildren: delay,\n staggerChildren: duration / segments.length,\n },\n },\n exit: {\n opacity: 0,\n transition: {\n staggerChildren: duration / segments.length,\n staggerDirection: -1,\n },\n },\n },\n item: variants,\n }\n : animation\n ? {\n container: {\n ...defaultItemAnimationVariants[animation].container,\n show: {\n ...defaultItemAnimationVariants[animation].container.show,\n transition: {\n delayChildren: delay,\n staggerChildren: duration / segments.length,\n },\n },\n exit: {\n ...defaultItemAnimationVariants[animation].container.exit,\n transition: {\n staggerChildren: duration / segments.length,\n staggerDirection: -1,\n },\n },\n },\n item: defaultItemAnimationVariants[animation].item,\n }\n : { container: defaultContainerVariants, item: defaultItemVariants }\n\n return (\n \n \n {accessible && {children}}\n {segments.map((segment, i) => (\n \n {segment}\n \n ))}\n \n \n )\n}\n\n// Export the memoized version\nexport const TextAnimate = memo(TextAnimateBase)\n", "type": "registry:ui" } ] diff --git a/apps/www/public/r/video-text.json b/apps/www/public/r/video-text.json index 8779a53d4..858aabc64 100644 --- a/apps/www/public/r/video-text.json +++ b/apps/www/public/r/video-text.json @@ -7,7 +7,7 @@ "files": [ { "path": "registry/magicui/video-text.tsx", - "content": "\"use client\"\n\nimport React, { ElementType, ReactNode, useEffect, useState } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface VideoTextProps {\n /**\n * The video source URL\n */\n src: string\n /**\n * Additional className for the container\n */\n className?: string\n /**\n * Whether to autoplay the video\n */\n autoPlay?: boolean\n /**\n * Whether to mute the video\n */\n muted?: boolean\n /**\n * Whether to loop the video\n */\n loop?: boolean\n /**\n * Whether to preload the video\n */\n preload?: \"auto\" | \"metadata\" | \"none\"\n /**\n * The content to display (will have the video \"inside\" it)\n */\n children: ReactNode\n /**\n * Font size for the text mask (in viewport width units)\n * @default 10\n */\n fontSize?: string | number\n /**\n * Font weight for the text mask\n * @default \"bold\"\n */\n fontWeight?: string | number\n /**\n * Text anchor for the text mask\n * @default \"middle\"\n */\n textAnchor?: string\n /**\n * Dominant baseline for the text mask\n * @default \"middle\"\n */\n dominantBaseline?: string\n /**\n * Font family for the text mask\n * @default \"sans-serif\"\n */\n fontFamily?: string\n /**\n * The element type to render for the text\n * @default \"div\"\n */\n as?: ElementType\n}\n\nexport function VideoText({\n src,\n children,\n className = \"\",\n autoPlay = true,\n muted = true,\n loop = true,\n preload = \"auto\",\n fontSize = 20,\n fontWeight = \"bold\",\n textAnchor = \"middle\",\n dominantBaseline = \"middle\",\n fontFamily = \"sans-serif\",\n as: Component = \"div\",\n}: VideoTextProps) {\n const [svgMask, setSvgMask] = useState(\"\")\n const content = React.Children.toArray(children).join(\"\")\n\n useEffect(() => {\n const updateSvgMask = () => {\n const responsiveFontSize =\n typeof fontSize === \"number\" ? `${fontSize}vw` : fontSize\n const newSvgMask = `${content}`\n setSvgMask(newSvgMask)\n }\n\n updateSvgMask()\n window.addEventListener(\"resize\", updateSvgMask)\n return () => window.removeEventListener(\"resize\", updateSvgMask)\n }, [content, fontSize, fontWeight, textAnchor, dominantBaseline, fontFamily])\n\n const dataUrlMask = `url(\"data:image/svg+xml,${encodeURIComponent(svgMask)}\")`\n\n return (\n \n {/* Create a container that masks the video to only show within text */}\n \n \n \n Your browser does not support the video tag.\n \n \n\n {/* Add a backup text element for SEO/accessibility */}\n {content}\n \n )\n}\n", + "content": "\"use client\"\n\nimport React, {\n useEffect,\n useState,\n type ElementType,\n type ReactNode,\n} from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport interface VideoTextProps {\n /**\n * The video source URL\n */\n src: string\n /**\n * Additional className for the container\n */\n className?: string\n /**\n * Whether to autoplay the video\n */\n autoPlay?: boolean\n /**\n * Whether to mute the video\n */\n muted?: boolean\n /**\n * Whether to loop the video\n */\n loop?: boolean\n /**\n * Whether to preload the video\n */\n preload?: \"auto\" | \"metadata\" | \"none\"\n /**\n * The content to display (will have the video \"inside\" it)\n */\n children: ReactNode\n /**\n * Font size for the text mask (in viewport width units)\n * @default 10\n */\n fontSize?: string | number\n /**\n * Font weight for the text mask\n * @default \"bold\"\n */\n fontWeight?: string | number\n /**\n * Text anchor for the text mask\n * @default \"middle\"\n */\n textAnchor?: string\n /**\n * Dominant baseline for the text mask\n * @default \"middle\"\n */\n dominantBaseline?: string\n /**\n * Font family for the text mask\n * @default \"sans-serif\"\n */\n fontFamily?: string\n /**\n * The element type to render for the text\n * @default \"div\"\n */\n as?: ElementType\n}\n\nexport function VideoText({\n src,\n children,\n className = \"\",\n autoPlay = true,\n muted = true,\n loop = true,\n preload = \"auto\",\n fontSize = 20,\n fontWeight = \"bold\",\n textAnchor = \"middle\",\n dominantBaseline = \"middle\",\n fontFamily = \"sans-serif\",\n as: Component = \"div\",\n}: VideoTextProps) {\n const [svgMask, setSvgMask] = useState(\"\")\n const content = React.Children.toArray(children).join(\"\")\n\n useEffect(() => {\n const updateSvgMask = () => {\n const responsiveFontSize =\n typeof fontSize === \"number\" ? `${fontSize}vw` : fontSize\n const newSvgMask = `${content}`\n setSvgMask(newSvgMask)\n }\n\n updateSvgMask()\n window.addEventListener(\"resize\", updateSvgMask)\n return () => window.removeEventListener(\"resize\", updateSvgMask)\n }, [content, fontSize, fontWeight, textAnchor, dominantBaseline, fontFamily])\n\n const dataUrlMask = `url(\"data:image/svg+xml,${encodeURIComponent(svgMask)}\")`\n\n return (\n \n {/* Create a container that masks the video to only show within text */}\n \n \n \n Your browser does not support the video tag.\n \n \n\n {/* Add a backup text element for SEO/accessibility */}\n {content}\n \n )\n}\n", "type": "registry:ui" } ] diff --git a/apps/www/public/r/warp-background.json b/apps/www/public/r/warp-background.json index 221c2213d..a46362ed6 100644 --- a/apps/www/public/r/warp-background.json +++ b/apps/www/public/r/warp-background.json @@ -10,7 +10,7 @@ "files": [ { "path": "registry/magicui/warp-background.tsx", - "content": "\"use client\"\n\nimport React, { HTMLAttributes, useCallback, useMemo } from \"react\"\nimport { motion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface WarpBackgroundProps extends HTMLAttributes {\n children: React.ReactNode\n perspective?: number\n beamsPerSide?: number\n beamSize?: number\n beamDelayMax?: number\n beamDelayMin?: number\n beamDuration?: number\n gridColor?: string\n}\n\nconst Beam = ({\n width,\n x,\n delay,\n duration,\n}: {\n width: string | number\n x: string | number\n delay: number\n duration: number\n}) => {\n const hue = Math.floor(Math.random() * 360)\n const ar = Math.floor(Math.random() * 10) + 1\n\n return (\n \n )\n}\n\nexport const WarpBackground: React.FC = ({\n children,\n perspective = 100,\n className,\n beamsPerSide = 3,\n beamSize = 5,\n beamDelayMax = 3,\n beamDelayMin = 0,\n beamDuration = 3,\n gridColor = \"var(--border)\",\n ...props\n}) => {\n const generateBeams = useCallback(() => {\n const beams = []\n const cellsPerSide = Math.floor(100 / beamSize)\n const step = cellsPerSide / beamsPerSide\n\n for (let i = 0; i < beamsPerSide; i++) {\n const x = Math.floor(i * step)\n const delay = Math.random() * (beamDelayMax - beamDelayMin) + beamDelayMin\n beams.push({ x, delay })\n }\n return beams\n }, [beamsPerSide, beamSize, beamDelayMax, beamDelayMin])\n\n const topBeams = useMemo(() => generateBeams(), [generateBeams])\n const rightBeams = useMemo(() => generateBeams(), [generateBeams])\n const bottomBeams = useMemo(() => generateBeams(), [generateBeams])\n const leftBeams = useMemo(() => generateBeams(), [generateBeams])\n\n return (\n
\n \n {/* top side */}\n
\n {topBeams.map((beam, index) => (\n \n ))}\n
\n {/* bottom side */}\n
\n {bottomBeams.map((beam, index) => (\n \n ))}\n
\n {/* left side */}\n
\n {leftBeams.map((beam, index) => (\n \n ))}\n
\n {/* right side */}\n
\n {rightBeams.map((beam, index) => (\n \n ))}\n
\n
\n
{children}
\n \n )\n}\n", + "content": "\"use client\"\n\nimport React, { useCallback, useMemo, type HTMLAttributes } from \"react\"\nimport { motion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface WarpBackgroundProps extends HTMLAttributes {\n children: React.ReactNode\n perspective?: number\n beamsPerSide?: number\n beamSize?: number\n beamDelayMax?: number\n beamDelayMin?: number\n beamDuration?: number\n gridColor?: string\n}\n\nconst Beam = ({\n width,\n x,\n delay,\n duration,\n}: {\n width: string | number\n x: string | number\n delay: number\n duration: number\n}) => {\n const hue = Math.floor(Math.random() * 360)\n const ar = Math.floor(Math.random() * 10) + 1\n\n return (\n \n )\n}\n\nexport const WarpBackground: React.FC = ({\n children,\n perspective = 100,\n className,\n beamsPerSide = 3,\n beamSize = 5,\n beamDelayMax = 3,\n beamDelayMin = 0,\n beamDuration = 3,\n gridColor = \"var(--border)\",\n ...props\n}) => {\n const generateBeams = useCallback(() => {\n const beams = []\n const cellsPerSide = Math.floor(100 / beamSize)\n const step = cellsPerSide / beamsPerSide\n\n for (let i = 0; i < beamsPerSide; i++) {\n const x = Math.floor(i * step)\n const delay = Math.random() * (beamDelayMax - beamDelayMin) + beamDelayMin\n beams.push({ x, delay })\n }\n return beams\n }, [beamsPerSide, beamSize, beamDelayMax, beamDelayMin])\n\n const topBeams = useMemo(() => generateBeams(), [generateBeams])\n const rightBeams = useMemo(() => generateBeams(), [generateBeams])\n const bottomBeams = useMemo(() => generateBeams(), [generateBeams])\n const leftBeams = useMemo(() => generateBeams(), [generateBeams])\n\n return (\n
\n \n {/* top side */}\n
\n {topBeams.map((beam, index) => (\n \n ))}\n
\n {/* bottom side */}\n
\n {bottomBeams.map((beam, index) => (\n \n ))}\n
\n {/* left side */}\n
\n {leftBeams.map((beam, index) => (\n \n ))}\n
\n {/* right side */}\n
\n {rightBeams.map((beam, index) => (\n \n ))}\n
\n
\n
{children}
\n \n )\n}\n", "type": "registry:ui" } ] diff --git a/apps/www/registry/index.ts b/apps/www/registry/index.ts index 3c2923bc3..830be3b40 100644 --- a/apps/www/registry/index.ts +++ b/apps/www/registry/index.ts @@ -1,4 +1,8 @@ -import { registryIndexSchema, RegistryItem, type Registry } from "shadcn/schema" +import { + registryIndexSchema, + type Registry, + type RegistryItem, +} from "shadcn/schema" import { examples } from "@/registry/registry-examples" import { lib } from "@/registry/registry-lib" diff --git a/apps/www/registry/magicui/android.tsx b/apps/www/registry/magicui/android.tsx index 7bec58f61..754b06550 100644 --- a/apps/www/registry/magicui/android.tsx +++ b/apps/www/registry/magicui/android.tsx @@ -1,4 +1,4 @@ -import { SVGProps } from "react" +import { type SVGProps } from "react" export interface AndroidProps extends SVGProps { width?: number diff --git a/apps/www/registry/magicui/animated-subscribe-button.tsx b/apps/www/registry/magicui/animated-subscribe-button.tsx index 2b30417ad..d78e95e69 100644 --- a/apps/www/registry/magicui/animated-subscribe-button.tsx +++ b/apps/www/registry/magicui/animated-subscribe-button.tsx @@ -1,7 +1,7 @@ "use client" import React, { useEffect, useState } from "react" -import { AnimatePresence, HTMLMotionProps, motion } from "motion/react" +import { AnimatePresence, motion, type HTMLMotionProps } from "motion/react" import { cn } from "@/lib/utils" diff --git a/apps/www/registry/magicui/border-beam.tsx b/apps/www/registry/magicui/border-beam.tsx index 1f594d1ec..f35bb34cc 100644 --- a/apps/www/registry/magicui/border-beam.tsx +++ b/apps/www/registry/magicui/border-beam.tsx @@ -1,6 +1,6 @@ "use client" -import { motion, MotionStyle, Transition } from "motion/react" +import { motion, type MotionStyle, type Transition } from "motion/react" import { cn } from "@/lib/utils" diff --git a/apps/www/registry/magicui/client-tweet-card.tsx b/apps/www/registry/magicui/client-tweet-card.tsx index d5c03f9b1..24dc9a88c 100644 --- a/apps/www/registry/magicui/client-tweet-card.tsx +++ b/apps/www/registry/magicui/client-tweet-card.tsx @@ -1,6 +1,6 @@ "use client" -import { TweetProps, useTweet } from "react-tweet" +import { useTweet, type TweetProps } from "react-tweet" import { MagicTweet, diff --git a/apps/www/registry/magicui/comic-text.tsx b/apps/www/registry/magicui/comic-text.tsx index 059681fec..12c9b4a0c 100644 --- a/apps/www/registry/magicui/comic-text.tsx +++ b/apps/www/registry/magicui/comic-text.tsx @@ -1,6 +1,6 @@ "use client" -import { CSSProperties } from "react" +import { type CSSProperties } from "react" import { motion } from "motion/react" import { cn } from "@/lib/utils" diff --git a/apps/www/registry/magicui/cool-mode.tsx b/apps/www/registry/magicui/cool-mode.tsx index 6115a0272..5089f028a 100644 --- a/apps/www/registry/magicui/cool-mode.tsx +++ b/apps/www/registry/magicui/cool-mode.tsx @@ -1,6 +1,6 @@ "use client" -import React, { ReactNode, useEffect, useRef } from "react" +import React, { useEffect, useRef, type ReactNode } from "react" export interface BaseParticle { element: HTMLElement | SVGSVGElement diff --git a/apps/www/registry/magicui/dock.tsx b/apps/www/registry/magicui/dock.tsx index d0b5b58ce..c62a97b10 100644 --- a/apps/www/registry/magicui/dock.tsx +++ b/apps/www/registry/magicui/dock.tsx @@ -1,6 +1,6 @@ "use client" -import React, { PropsWithChildren, useRef } from "react" +import React, { useRef, type PropsWithChildren } from "react" import { cva, type VariantProps } from "class-variance-authority" import { motion, diff --git a/apps/www/registry/magicui/neon-gradient-card.tsx b/apps/www/registry/magicui/neon-gradient-card.tsx index c9d46c56c..931de84f7 100644 --- a/apps/www/registry/magicui/neon-gradient-card.tsx +++ b/apps/www/registry/magicui/neon-gradient-card.tsx @@ -1,12 +1,12 @@ "use client" import { - CSSProperties, - ReactElement, - ReactNode, useEffect, useRef, useState, + type CSSProperties, + type ReactElement, + type ReactNode, } from "react" import { cn } from "@/lib/utils" diff --git a/apps/www/registry/magicui/rainbow-button.tsx b/apps/www/registry/magicui/rainbow-button.tsx index 075dcbf68..472730077 100644 --- a/apps/www/registry/magicui/rainbow-button.tsx +++ b/apps/www/registry/magicui/rainbow-button.tsx @@ -1,6 +1,6 @@ import React from "react" import { Slot } from "@radix-ui/react-slot" -import { cva, VariantProps } from "class-variance-authority" +import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" diff --git a/apps/www/registry/magicui/ripple-button.tsx b/apps/www/registry/magicui/ripple-button.tsx index f583f9814..0cc571cb0 100644 --- a/apps/www/registry/magicui/ripple-button.tsx +++ b/apps/www/registry/magicui/ripple-button.tsx @@ -1,6 +1,6 @@ "use client" -import React, { MouseEvent, useEffect, useState } from "react" +import React, { useEffect, useState, type MouseEvent } from "react" import { cn } from "@/lib/utils" diff --git a/apps/www/registry/magicui/smooth-cursor.tsx b/apps/www/registry/magicui/smooth-cursor.tsx index 39c9f5f44..bcb2bdc79 100644 --- a/apps/www/registry/magicui/smooth-cursor.tsx +++ b/apps/www/registry/magicui/smooth-cursor.tsx @@ -1,6 +1,6 @@ "use client" -import { FC, useEffect, useRef, useState } from "react" +import { useEffect, useRef, useState, type FC } from "react" import { motion, useSpring } from "motion/react" interface Position { diff --git a/apps/www/registry/magicui/sparkles-text.tsx b/apps/www/registry/magicui/sparkles-text.tsx index a4073359b..2befd5174 100644 --- a/apps/www/registry/magicui/sparkles-text.tsx +++ b/apps/www/registry/magicui/sparkles-text.tsx @@ -1,6 +1,11 @@ "use client" -import { CSSProperties, ReactElement, useEffect, useState } from "react" +import { + useEffect, + useState, + type CSSProperties, + type ReactElement, +} from "react" import { motion } from "motion/react" import { cn } from "@/lib/utils" diff --git a/apps/www/registry/magicui/spinning-text.tsx b/apps/www/registry/magicui/spinning-text.tsx index 0a55d27f7..1162d1b11 100644 --- a/apps/www/registry/magicui/spinning-text.tsx +++ b/apps/www/registry/magicui/spinning-text.tsx @@ -1,7 +1,7 @@ "use client" import React, { type ComponentPropsWithoutRef } from "react" -import { motion, Transition, Variants } from "motion/react" +import { motion, type Transition, type Variants } from "motion/react" import { cn } from "@/lib/utils" diff --git a/apps/www/registry/magicui/text-animate.tsx b/apps/www/registry/magicui/text-animate.tsx index ce2b453f2..6b470f1ed 100644 --- a/apps/www/registry/magicui/text-animate.tsx +++ b/apps/www/registry/magicui/text-animate.tsx @@ -4,9 +4,9 @@ import { memo } from "react" import { AnimatePresence, motion, - Variants, type DOMMotionComponents, type MotionProps, + type Variants, } from "motion/react" import { cn } from "@/lib/utils" diff --git a/apps/www/registry/magicui/video-text.tsx b/apps/www/registry/magicui/video-text.tsx index 9f51c0e54..274c11975 100644 --- a/apps/www/registry/magicui/video-text.tsx +++ b/apps/www/registry/magicui/video-text.tsx @@ -1,6 +1,11 @@ "use client" -import React, { ElementType, ReactNode, useEffect, useState } from "react" +import React, { + useEffect, + useState, + type ElementType, + type ReactNode, +} from "react" import { cn } from "@/lib/utils" diff --git a/apps/www/registry/magicui/warp-background.tsx b/apps/www/registry/magicui/warp-background.tsx index 9e05319de..0b78c589a 100644 --- a/apps/www/registry/magicui/warp-background.tsx +++ b/apps/www/registry/magicui/warp-background.tsx @@ -1,6 +1,6 @@ "use client" -import React, { HTMLAttributes, useCallback, useMemo } from "react" +import React, { useCallback, useMemo, type HTMLAttributes } from "react" import { motion } from "motion/react" import { cn } from "@/lib/utils" From 2138c4ec8409073def74597093b35cb278df9a47 Mon Sep 17 00:00:00 2001 From: Jinho Yeom Date: Fri, 31 Jul 2026 20:30:37 +0900 Subject: [PATCH 2/2] chore(www): enable verbatimModuleSyntax to prevent TS1484 regressions The previous commit removed every type-only binding imported as a value, so the flag turns on with zero additional changes (tsc --noEmit passes). New registry code that reintroduces the pattern now fails in-editor and in CI instead of surfacing as a build error in consumers' Vite apps. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01BPdQpL26E46TqVKrBFKrgH --- apps/www/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/www/tsconfig.json b/apps/www/tsconfig.json index 3251b69c2..80fb9b2e1 100644 --- a/apps/www/tsconfig.json +++ b/apps/www/tsconfig.json @@ -13,6 +13,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, + "verbatimModuleSyntax": true, "jsx": "preserve", "incremental": true, "plugins": [