@lerpa/mcp-server 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/registry/registry.json +152 -16
package/package.json
CHANGED
package/registry/registry.json
CHANGED
|
@@ -583,6 +583,7 @@
|
|
|
583
583
|
"name": "agency-hero",
|
|
584
584
|
"type": "registry:block",
|
|
585
585
|
"dependencies": [
|
|
586
|
+
"class-variance-authority",
|
|
586
587
|
"framer-motion",
|
|
587
588
|
"lucide-react"
|
|
588
589
|
],
|
|
@@ -590,7 +591,22 @@
|
|
|
590
591
|
{
|
|
591
592
|
"path": "components/blocks/agency-hero.tsx",
|
|
592
593
|
"type": "registry:block",
|
|
593
|
-
"content": "import React from 'react';\nimport { motion} from \"framer-motion\";\nimport { Button } from '
|
|
594
|
+
"content": "import React from 'react';\nimport { motion} from \"framer-motion\";\nimport { Button } from './button';\nimport { Container } from './container';\nimport { Sparkles, ArrowRight, Star } from 'lucide-react';\n\nexport interface AgencyHeroProps {\n badgeText?: string;\n headline?: string;\n subheadline?: string;\n description?: string;\n clientsRatingText?: string;\n primaryActionLabel?: string;\n secondaryActionLabel?: string;\n}\n\nexport function AgencyHero({\n badgeText = 'Voted #1 Design Studio',\n headline = 'We Craft Digital Masterpieces.',\n subheadline = 'Next-gen interfaces, custom branding, and ultra-high speed web solutions designed for high-caliber businesses.',\n clientsRatingText = 'Trusted by 100+ global brands',\n primaryActionLabel = 'Start a Project',\n secondaryActionLabel = 'View Portfolio',\n}: AgencyHeroProps) {\n return (\n <section className=\"relative overflow-hidden bg-background py-20 lg:py-32\">\n {/* Visual background grids */}\n <div className=\"absolute inset-0 bg-[linear-gradient(to_right,rgba(15,23,42,0.02)_1px,transparent_1px),linear-gradient(to_bottom,rgba(15,23,42,0.02)_1px,transparent_1px)] bg-[size:4rem_4rem] dark:bg-[linear-gradient(to_right,rgba(255,255,255,0.01)_1px,transparent_1px),linear-gradient(to_bottom,rgba(255,255,255,0.01)_1px,transparent_1px)]\" />\n\n <Container className=\"relative\">\n <div className=\"grid grid-cols-1 gap-12 lg:grid-cols-12 lg:items-center\">\n {/* Text Area */}\n <div className=\"lg:col-span-7 flex flex-col items-start text-left\">\n <motion.div\n initial={{ opacity: 0, y: -10 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.5 }}\n className=\"inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold bg-primary/10 text-primary border border-primary/20 mb-6\"\n >\n <Sparkles className=\"h-3 w-3\" />\n {badgeText}\n </motion.div>\n\n <motion.h1\n initial={{ opacity: 0, y: 20 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.6, delay: 0.1 }}\n className=\"text-4xl sm:text-6xl font-black tracking-tight text-foreground leading-[1.05] mb-6\"\n >\n {headline}\n </motion.h1>\n\n <motion.p\n initial={{ opacity: 0, y: 20 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.6, delay: 0.2 }}\n className=\"text-lg text-muted-foreground leading-relaxed max-w-xl mb-10\"\n >\n {subheadline}\n </motion.p>\n\n <motion.div\n initial={{ opacity: 0, y: 20 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.6, delay: 0.3 }}\n className=\"flex flex-wrap items-center gap-4 w-full sm:w-auto mb-12\"\n >\n <Button size=\"lg\" className=\"w-full sm:w-auto font-bold gap-2 group\">\n {primaryActionLabel}\n <ArrowRight className=\"h-4 w-4 transition-transform group-hover:translate-x-1\" />\n </Button>\n <Button variant=\"outline\" size=\"lg\" className=\"w-full sm:w-auto font-bold\">\n {secondaryActionLabel}\n </Button>\n </motion.div>\n\n {/* Ratings block */}\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n transition={{ duration: 0.6, delay: 0.4 }}\n className=\"flex items-center gap-3 border-t border-border pt-6 w-full\"\n >\n <div className=\"flex -space-x-2.5\">\n {[1, 2, 3, 4].map((i) => (\n <div\n key={i}\n className=\"h-8 w-8 rounded-full border-2 border-background bg-muted flex items-center justify-center text-[10px] font-bold shrink-0 overflow-hidden\"\n >\n <img src={`https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=80&fit=crop&crop=faces&q=80`} alt=\"Client\" loading=\"lazy\" decoding=\"async\" />\n </div>\n ))}\n </div>\n <div>\n <div className=\"flex items-center text-amber-500\">\n {Array.from({ length: 5 }).map((_, i) => (\n <Star key={i} className=\"h-3.5 w-3.5 fill-current\" />\n ))}\n </div>\n <span className=\"text-xs font-semibold text-muted-foreground mt-0.5 block\">\n {clientsRatingText}\n </span>\n </div>\n </motion.div>\n </div>\n\n {/* Animated Interactive Grid Showcase */}\n <div className=\"lg:col-span-5 relative flex items-center justify-center\">\n <motion.div\n initial={{ opacity: 0, scale: 0.95 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ duration: 0.8, type: 'spring', stiffness: 100, damping: 20 }}\n className=\"relative w-full aspect-square max-w-md rounded-2xl border border-border bg-card shadow-2xl p-6 overflow-hidden flex flex-col justify-between\"\n >\n <div className=\"absolute inset-0 bg-gradient-to-br from-primary/10 via-transparent to-transparent pointer-events-none\" />\n\n {/* Mock floating visual node elements */}\n <div className=\"flex items-start justify-between\">\n <div className=\"p-3 bg-primary/10 border border-primary/20 text-primary rounded-xl shadow-md\">\n <Sparkles className=\"h-6 w-6\" />\n </div>\n <div className=\"p-2.5 bg-muted rounded-xl text-xs font-mono font-semibold border border-border\">\n STUDIO_OUTPUT\n </div>\n </div>\n\n {/* Design center node */}\n <div className=\"my-8 space-y-4\">\n <span className=\"text-[10px] font-bold tracking-widest uppercase text-primary/80\">\n Active Sprint\n </span>\n <h2 className=\"text-3xl font-extrabold text-foreground\">\n Branding, Design & Performance.\n </h2>\n <p className=\"text-xs text-muted-foreground leading-relaxed\">\n We consolidate UI workflows into high-speed reactive design tokens, yielding flawless render passes across edge systems.\n </p>\n </div>\n\n {/* Progress visual */}\n <div className=\"space-y-2.5\">\n <div className=\"flex items-center justify-between text-xs font-semibold\">\n <span className=\"text-muted-foreground\">Fidelity Rating</span>\n <span className=\"text-primary\">99.8%</span>\n </div>\n <div className=\"h-2 w-full bg-secondary rounded-full overflow-hidden\">\n <motion.div\n initial={{ width: 0 }}\n animate={{ width: '99.8%' }}\n transition={{ duration: 1.5, delay: 0.5, ease: 'easeOut' }}\n className=\"h-full bg-primary\"\n />\n </div>\n </div>\n </motion.div>\n </div>\n </div>\n </Container>\n </section>\n );\n}\n"
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
"path": "components/blocks/button.tsx",
|
|
598
|
+
"type": "registry:block",
|
|
599
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from \"@/lib/utils\";\nimport { Spinner } from './spinner';\n\nconst buttonVariants = cva(\n 'relative inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-all duration-150 ease-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:scale-[0.98] motion-reduce:active:scale-100 motion-reduce:transition-none disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:shrink-0',\n {\n variants: {\n variant: {\n default: 'bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 shadow-sm hover:shadow',\n destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80 shadow-sm hover:shadow',\n outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground hover:border-accent active:bg-accent/80',\n secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70',\n ghost: 'hover:bg-accent hover:text-accent-foreground active:bg-accent/80',\n link: 'text-primary underline-offset-4 hover:underline active:text-primary/80',\n },\n size: {\n default: 'h-10 px-4 py-2',\n sm: 'h-9 rounded-md px-3',\n lg: 'h-11 rounded-md px-8',\n icon: 'h-10 w-10',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n }\n);\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n loading?: boolean;\n loadingLabel?: string;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n variant,\n size,\n asChild: _asChild = false,\n loading = false,\n loadingLabel,\n disabled,\n children,\n ...props\n },\n ref\n ) => {\n const isDisabled = disabled || loading;\n return (\n <button\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n disabled={isDisabled}\n aria-busy={loading || undefined}\n data-loading={loading || undefined}\n {...props}\n >\n {loading && (\n <Spinner\n size={size === 'lg' ? 'default' : 'sm'}\n label={loadingLabel ?? 'Loading'}\n aria-hidden={!loadingLabel || undefined}\n />\n )}\n {loading && loadingLabel ? <span>{loadingLabel}</span> : children}\n </button>\n );\n }\n);\nButton.displayName = 'Button';\n\nexport { Button, buttonVariants };\n"
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
"path": "components/blocks/container.tsx",
|
|
603
|
+
"type": "registry:block",
|
|
604
|
+
"content": "import React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> {\n as?: React.ElementType;\n clean?: boolean;\n}\n\nconst Container = React.forwardRef<HTMLDivElement, ContainerProps>(\n ({ className, as: Component = 'div', clean = false, ...props }, ref) => {\n return (\n <Component\n ref={ref}\n className={cn(\n !clean && 'mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8',\n className\n )}\n {...props}\n />\n );\n }\n);\nContainer.displayName = 'Container';\n\nexport { Container };\n"
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
"path": "components/blocks/spinner.tsx",
|
|
608
|
+
"type": "registry:block",
|
|
609
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from \"@/lib/utils\";\n\nconst spinnerVariants = cva(\n 'inline-block animate-spin rounded-full border-current border-solid border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]',\n {\n variants: {\n size: {\n xs: 'h-3 w-3 border',\n sm: 'h-4 w-4 border-2',\n default: 'h-5 w-5 border-2',\n lg: 'h-8 w-8 border-[3px]',\n xl: 'h-12 w-12 border-4',\n },\n },\n defaultVariants: {\n size: 'default',\n },\n }\n);\n\nexport interface SpinnerProps\n extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'role'>,\n VariantProps<typeof spinnerVariants> {\n label?: string;\n}\n\nconst Spinner = React.forwardRef<HTMLSpanElement, SpinnerProps>(\n ({ className, size, label = 'Loading', ...props }, ref) => {\n return (\n <span\n ref={ref}\n role=\"status\"\n aria-live=\"polite\"\n className={cn(spinnerVariants({ size }), className)}\n {...props}\n >\n <span className=\"sr-only\">{label}</span>\n </span>\n );\n }\n);\nSpinner.displayName = 'Spinner';\n\nexport { Spinner, spinnerVariants };\n"
|
|
594
610
|
}
|
|
595
611
|
]
|
|
596
612
|
},
|
|
@@ -5354,6 +5370,11 @@
|
|
|
5354
5370
|
"type": "registry:ui",
|
|
5355
5371
|
"content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { Copy, Check } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\ntype Tab = { label: string; code: string };\n\nconst TABS: Tab[] = [\n {\n label: \"index.ts\",\n code: `export { Button } from \"./button\";\nexport { Card } from \"./card\";\nexport { Input } from \"./input\";`,\n },\n {\n label: \"button.tsx\",\n code: `export function Button(props: ButtonProps) {\n return <button className=\"btn\" {...props} />;\n}`,\n },\n {\n label: \"styles.css\",\n code: `.btn {\n border-radius: 0.5rem;\n padding: 0.5rem 1rem;\n}`,\n },\n];\n\nexport interface CodeBlockTabsProps {\n className?: string;\n}\n\nexport function CodeBlockTabs({ className }: CodeBlockTabsProps) {\n const [active, setActive] = useState(0);\n const [copied, setCopied] = useState(false);\n const current = TABS[active] ?? TABS[0];\n\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(current.code);\n } catch {\n /* clipboard unavailable */\n }\n setCopied(true);\n window.setTimeout(() => setCopied(false), 1500);\n };\n\n return (\n <div className={cn(\"w-full max-w-md overflow-hidden rounded-2xl border border-border bg-muted font-mono shadow-sm\", className)}>\n <div className=\"flex items-center justify-between border-b border-border bg-foreground/[0.02] pl-2 pr-2\">\n <div role=\"tablist\" aria-label=\"Source files\" className=\"flex\">\n {TABS.map((t, i) => (\n <button\n key={t.label}\n role=\"tab\"\n aria-selected={active === i}\n onClick={() => setActive(i)}\n className={cn(\n \"border-b-2 px-3 py-2.5 text-xs transition-colors\",\n active === i\n ? \"border-primary text-foreground\"\n : \"border-transparent text-muted-foreground hover:text-foreground\"\n )}\n >\n {t.label}\n </button>\n ))}\n </div>\n <button\n type=\"button\"\n onClick={handleCopy}\n aria-label={copied ? \"Copied to clipboard\" : \"Copy code to clipboard\"}\n className=\"grid h-8 w-8 place-items-center rounded-lg text-muted-foreground transition hover:text-foreground\"\n >\n {copied ? <Check className=\"h-4 w-4 text-emerald-600 dark:text-emerald-400\" /> : <Copy className=\"h-4 w-4\" />}\n </button>\n </div>\n <pre className=\"overflow-x-auto p-5 text-xs leading-relaxed text-foreground/85\">\n <code>{current.code}</code>\n </pre>\n </div>\n );\n}\n"
|
|
5356
5372
|
}
|
|
5373
|
+
],
|
|
5374
|
+
"registryDependencies": [
|
|
5375
|
+
"button",
|
|
5376
|
+
"card",
|
|
5377
|
+
"input"
|
|
5357
5378
|
]
|
|
5358
5379
|
},
|
|
5359
5380
|
{
|
|
@@ -6973,6 +6994,7 @@
|
|
|
6973
6994
|
"name": "dashboard-overview",
|
|
6974
6995
|
"type": "registry:block",
|
|
6975
6996
|
"dependencies": [
|
|
6997
|
+
"class-variance-authority",
|
|
6976
6998
|
"lucide-react",
|
|
6977
6999
|
"recharts"
|
|
6978
7000
|
],
|
|
@@ -6980,7 +7002,32 @@
|
|
|
6980
7002
|
{
|
|
6981
7003
|
"path": "components/blocks/dashboard-overview.tsx",
|
|
6982
7004
|
"type": "registry:block",
|
|
6983
|
-
"content": "import React from 'react';\nimport { Card, CardContent, CardHeader, CardTitle, CardDescription } from '
|
|
7005
|
+
"content": "import React from 'react';\nimport { Card, CardContent, CardHeader, CardTitle, CardDescription } from './card';\nimport { StatCard } from './stat-card';\nimport { Container } from './container';\nimport { Button } from './button';\nimport { Users, DollarSign, Activity, ShoppingBag, ArrowUpRight } from 'lucide-react';\nimport {\n ResponsiveContainer,\n AreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n} from 'recharts';\n\nexport interface ChartDataPoint {\n name: string;\n visitors: number;\n revenue: number;\n}\n\nexport interface RecentTransaction {\n id: string;\n user: string;\n email: string;\n amount: string;\n status: 'Completed' | 'Pending' | 'Failed';\n date: string;\n}\n\nexport interface DashboardOverviewProps {\n stats?: {\n revenue: string | number;\n revenueChange: number;\n users: string | number;\n usersChange: number;\n conversion: string | number;\n conversionChange: number;\n orders: string | number;\n ordersChange: number;\n };\n chartData?: ChartDataPoint[];\n transactions?: RecentTransaction[];\n}\n\nconst defaultChartData: ChartDataPoint[] = [\n { name: 'Jan', visitors: 2400, revenue: 1400 },\n { name: 'Feb', visitors: 1398, revenue: 2210 },\n { name: 'Mar', visitors: 9800, revenue: 2290 },\n { name: 'Apr', visitors: 3908, revenue: 2000 },\n { name: 'May', visitors: 4800, revenue: 2181 },\n { name: 'Jun', visitors: 3800, revenue: 2500 },\n { name: 'Jul', visitors: 4300, revenue: 2100 },\n];\n\nconst defaultTransactions: RecentTransaction[] = [\n { id: '1', user: 'Olivia Martin', email: 'olivia.martin@email.com', amount: '+$1,999.00', status: 'Completed', date: 'Just now' },\n { id: '2', user: 'Jackson Lee', email: 'jackson.lee@email.com', amount: '+$39.00', status: 'Completed', date: '5 mins ago' },\n { id: '3', user: 'Isabella Nguyen', email: 'isabella.nguyen@email.com', amount: '+$299.00', status: 'Pending', date: '1 hour ago' },\n { id: '4', user: 'William Kim', email: 'will@email.com', amount: '-$15.00', status: 'Failed', date: 'Yesterday' },\n { id: '5', user: 'Sofia Davis', email: 'sofia.davis@email.com', amount: '+$350.00', status: 'Completed', date: '2 days ago' },\n];\n\nexport function DashboardOverview({\n stats = {\n revenue: '$45,231.89',\n revenueChange: 20.1,\n users: '+2,350',\n usersChange: 180.1,\n conversion: '+12.2%',\n conversionChange: 19.2,\n orders: '+12,234',\n ordersChange: -1.2,\n },\n chartData = defaultChartData,\n transactions = defaultTransactions,\n}: DashboardOverviewProps) {\n return (\n <section className=\"py-10 bg-background/50\">\n <Container className=\"space-y-8\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h2 className=\"text-3xl font-bold tracking-tight text-foreground\">Dashboard</h2>\n <p className=\"text-muted-foreground text-sm mt-1\">\n Real-time analytics and system state metrics.\n </p>\n </div>\n </div>\n\n {/* Stats Grid */}\n <h3 className=\"sr-only\">Key metrics</h3>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n title=\"Total Revenue\"\n value={stats.revenue}\n change={stats.revenueChange}\n timeframe=\"from last month\"\n icon={<DollarSign className=\"h-4 w-4\" />}\n />\n <StatCard\n title=\"Active Users\"\n value={stats.users}\n change={stats.usersChange}\n timeframe=\"from last month\"\n icon={<Users className=\"h-4 w-4\" />}\n />\n <StatCard\n title=\"Conversion Rate\"\n value={stats.conversion}\n change={stats.conversionChange}\n timeframe=\"from last month\"\n icon={<Activity className=\"h-4 w-4\" />}\n />\n <StatCard\n title=\"Sales Volume\"\n value={stats.orders}\n change={stats.ordersChange}\n timeframe=\"from last month\"\n icon={<ShoppingBag className=\"h-4 w-4\" />}\n />\n </div>\n\n {/* Graphs and Lists */}\n <div className=\"grid grid-cols-1 gap-8 lg:grid-cols-7\">\n {/* Main Area Graph */}\n <Card className=\"lg:col-span-4 border border-border bg-card\">\n <CardHeader>\n <CardTitle>Analytics Overview</CardTitle>\n <CardDescription>\n Track visitor count and monthly revenue streams dynamically.\n </CardDescription>\n </CardHeader>\n <CardContent className=\"h-[300px] pl-2 pr-4 pb-4\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={chartData} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>\n <defs>\n <linearGradient id=\"colorRevenue\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"5%\" stopColor=\"var(--primary, #2563eb)\" stopOpacity={0.2} />\n <stop offset=\"95%\" stopColor=\"var(--primary, #2563eb)\" stopOpacity={0} />\n </linearGradient>\n </defs>\n <CartesianGrid strokeDasharray=\"3 3\" vertical={false} stroke=\"var(--border, #e2e8f0)\" />\n <XAxis dataKey=\"name\" fontSize={11} stroke=\"#94a3b8\" />\n <YAxis fontSize={11} stroke=\"#94a3b8\" />\n <Tooltip\n contentStyle={{\n backgroundColor: 'var(--card, #ffffff)',\n borderColor: 'var(--border, #e2e8f0)',\n color: 'var(--foreground, #0f172a)',\n borderRadius: '8px',\n }}\n />\n <Area\n type=\"monotone\"\n dataKey=\"revenue\"\n stroke=\"var(--primary, #2563eb)\"\n strokeWidth={2}\n fillOpacity={1}\n fill=\"url(#colorRevenue)\"\n />\n </AreaChart>\n </ResponsiveContainer>\n </CardContent>\n </Card>\n\n {/* Recent Operations */}\n <Card className=\"lg:col-span-3 border border-border bg-card\">\n <CardHeader className=\"flex flex-row items-center justify-between pb-2\">\n <div>\n <CardTitle>Recent Sales</CardTitle>\n <CardDescription>You made 265 sales this month.</CardDescription>\n </div>\n <Button size=\"icon\" variant=\"ghost\" aria-label=\"View all sales\" className=\"h-8 w-8\">\n <ArrowUpRight className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </CardHeader>\n <CardContent className=\"p-6 pt-0\">\n <div className=\"space-y-6\">\n {transactions.map((t) => (\n <div key={t.id} className=\"flex items-center justify-between gap-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"h-9 w-9 rounded-full bg-secondary flex items-center justify-center font-bold text-xs uppercase text-secondary-foreground shrink-0\">\n {t.user.split(' ').map((n) => n[0]).join('')}\n </div>\n <div className=\"min-w-0\">\n <p className=\"text-sm font-semibold text-foreground truncate\">{t.user}</p>\n <p className=\"text-xs text-muted-foreground truncate\">{t.email}</p>\n </div>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm font-bold text-foreground\">{t.amount}</p>\n <span className=\"text-[10px] text-muted-foreground\">{t.date}</span>\n </div>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n </div>\n </Container>\n </section>\n );\n}\n"
|
|
7006
|
+
},
|
|
7007
|
+
{
|
|
7008
|
+
"path": "components/blocks/card.tsx",
|
|
7009
|
+
"type": "registry:block",
|
|
7010
|
+
"content": "import React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nconst Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n const isInteractive =\n typeof props.onClick === 'function' || props.tabIndex !== undefined || props.role === 'button';\n return (\n <div\n ref={ref}\n className={cn(\n 'rounded-lg border border-border bg-card text-card-foreground shadow-sm transition-all duration-150 ease-out',\n isInteractive &&\n 'cursor-pointer hover:shadow-md hover:border-border/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:scale-[0.99] motion-reduce:active:scale-100 motion-reduce:transition-none',\n className\n )}\n {...props}\n />\n );\n }\n);\nCard.displayName = 'Card';\n\nconst CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex flex-col space-y-1.5 p-6', className)}\n {...props}\n />\n )\n);\nCardHeader.displayName = 'CardHeader';\n\nconst CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(\n ({ className, children, ...props }, ref) =>\n children == null || children === '' ? null : (\n <h3\n ref={ref}\n className={cn('text-2xl font-semibold leading-none tracking-tight', className)}\n {...props}\n >\n {children}\n </h3>\n )\n);\nCardTitle.displayName = 'CardTitle';\n\nconst CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(\n ({ className, ...props }, ref) => (\n <p\n ref={ref}\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n )\n);\nCardDescription.displayName = 'CardDescription';\n\nconst CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />\n )\n);\nCardContent.displayName = 'CardContent';\n\nconst CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center p-6 pt-0 border-t border-border mt-6', className)}\n {...props}\n />\n )\n);\nCardFooter.displayName = 'CardFooter';\n\nexport { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };\n"
|
|
7011
|
+
},
|
|
7012
|
+
{
|
|
7013
|
+
"path": "components/blocks/stat-card.tsx",
|
|
7014
|
+
"type": "registry:block",
|
|
7015
|
+
"content": "import React from 'react';\nimport { Card, CardContent } from './card';\nimport { cn } from \"@/lib/utils\";\nimport { ArrowUpRight, ArrowDownRight } from 'lucide-react';\n\nexport interface StatCardProps extends React.HTMLAttributes<HTMLDivElement> {\n title: string;\n value: string | number;\n change?: number;\n changeType?: 'positive' | 'negative' | 'neutral';\n timeframe?: string;\n icon?: React.ReactNode;\n trendLabel?: string;\n}\n\nconst StatCard = React.forwardRef<HTMLDivElement, StatCardProps>(\n ({ className, title, value, change, changeType, timeframe, icon, trendLabel, ...props }, ref) => {\n const defaultChangeType = change !== undefined\n ? change > 0 ? 'positive' : change < 0 ? 'negative' : 'neutral'\n : changeType;\n\n const isPositive = defaultChangeType === 'positive';\n const isNegative = defaultChangeType === 'negative';\n\n return (\n <Card ref={ref} className={cn('overflow-hidden relative', className)} {...props}>\n <CardContent className=\"p-6\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm font-medium text-muted-foreground truncate\">{title}</p>\n {icon && (\n <div className=\"p-2 bg-muted rounded-md text-muted-foreground\">\n {icon}\n </div>\n )}\n </div>\n <div className=\"mt-2 flex items-baseline justify-between\">\n <div>\n {value != null && value !== '' && (\n <h4 className=\"text-3xl font-bold tracking-tight text-foreground\">{value}</h4>\n )}\n </div>\n </div>\n {(change !== undefined || timeframe || trendLabel) && (\n <div className=\"mt-4 flex items-center gap-2 text-xs\">\n {change !== undefined && (\n <span\n className={cn(\n 'flex items-center gap-0.5 font-semibold px-1.5 py-0.5 rounded-full',\n isPositive && 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400',\n isNegative && 'bg-rose-500/10 text-rose-600 dark:text-rose-400',\n !isPositive && !isNegative && 'bg-muted text-muted-foreground'\n )}\n >\n {isPositive && <ArrowUpRight className=\"h-3 w-3 shrink-0\" />}\n {isNegative && <ArrowDownRight className=\"h-3 w-3 shrink-0\" />}\n {Math.abs(change)}%\n </span>\n )}\n {trendLabel && <span className=\"text-muted-foreground font-medium\">{trendLabel}</span>}\n {timeframe && <span className=\"text-muted-foreground/80\">{timeframe}</span>}\n </div>\n )}\n </CardContent>\n </Card>\n );\n }\n);\nStatCard.displayName = 'StatCard';\n\nexport { StatCard };\n"
|
|
7016
|
+
},
|
|
7017
|
+
{
|
|
7018
|
+
"path": "components/blocks/container.tsx",
|
|
7019
|
+
"type": "registry:block",
|
|
7020
|
+
"content": "import React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> {\n as?: React.ElementType;\n clean?: boolean;\n}\n\nconst Container = React.forwardRef<HTMLDivElement, ContainerProps>(\n ({ className, as: Component = 'div', clean = false, ...props }, ref) => {\n return (\n <Component\n ref={ref}\n className={cn(\n !clean && 'mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8',\n className\n )}\n {...props}\n />\n );\n }\n);\nContainer.displayName = 'Container';\n\nexport { Container };\n"
|
|
7021
|
+
},
|
|
7022
|
+
{
|
|
7023
|
+
"path": "components/blocks/button.tsx",
|
|
7024
|
+
"type": "registry:block",
|
|
7025
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from \"@/lib/utils\";\nimport { Spinner } from './spinner';\n\nconst buttonVariants = cva(\n 'relative inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-all duration-150 ease-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:scale-[0.98] motion-reduce:active:scale-100 motion-reduce:transition-none disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:shrink-0',\n {\n variants: {\n variant: {\n default: 'bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 shadow-sm hover:shadow',\n destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80 shadow-sm hover:shadow',\n outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground hover:border-accent active:bg-accent/80',\n secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70',\n ghost: 'hover:bg-accent hover:text-accent-foreground active:bg-accent/80',\n link: 'text-primary underline-offset-4 hover:underline active:text-primary/80',\n },\n size: {\n default: 'h-10 px-4 py-2',\n sm: 'h-9 rounded-md px-3',\n lg: 'h-11 rounded-md px-8',\n icon: 'h-10 w-10',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n }\n);\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n loading?: boolean;\n loadingLabel?: string;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n variant,\n size,\n asChild: _asChild = false,\n loading = false,\n loadingLabel,\n disabled,\n children,\n ...props\n },\n ref\n ) => {\n const isDisabled = disabled || loading;\n return (\n <button\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n disabled={isDisabled}\n aria-busy={loading || undefined}\n data-loading={loading || undefined}\n {...props}\n >\n {loading && (\n <Spinner\n size={size === 'lg' ? 'default' : 'sm'}\n label={loadingLabel ?? 'Loading'}\n aria-hidden={!loadingLabel || undefined}\n />\n )}\n {loading && loadingLabel ? <span>{loadingLabel}</span> : children}\n </button>\n );\n }\n);\nButton.displayName = 'Button';\n\nexport { Button, buttonVariants };\n"
|
|
7026
|
+
},
|
|
7027
|
+
{
|
|
7028
|
+
"path": "components/blocks/spinner.tsx",
|
|
7029
|
+
"type": "registry:block",
|
|
7030
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from \"@/lib/utils\";\n\nconst spinnerVariants = cva(\n 'inline-block animate-spin rounded-full border-current border-solid border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]',\n {\n variants: {\n size: {\n xs: 'h-3 w-3 border',\n sm: 'h-4 w-4 border-2',\n default: 'h-5 w-5 border-2',\n lg: 'h-8 w-8 border-[3px]',\n xl: 'h-12 w-12 border-4',\n },\n },\n defaultVariants: {\n size: 'default',\n },\n }\n);\n\nexport interface SpinnerProps\n extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'role'>,\n VariantProps<typeof spinnerVariants> {\n label?: string;\n}\n\nconst Spinner = React.forwardRef<HTMLSpanElement, SpinnerProps>(\n ({ className, size, label = 'Loading', ...props }, ref) => {\n return (\n <span\n ref={ref}\n role=\"status\"\n aria-live=\"polite\"\n className={cn(spinnerVariants({ size }), className)}\n {...props}\n >\n <span className=\"sr-only\">{label}</span>\n </span>\n );\n }\n);\nSpinner.displayName = 'Spinner';\n\nexport { Spinner, spinnerVariants };\n"
|
|
6984
7031
|
}
|
|
6985
7032
|
]
|
|
6986
7033
|
},
|
|
@@ -9158,7 +9205,7 @@
|
|
|
9158
9205
|
],
|
|
9159
9206
|
"files": [
|
|
9160
9207
|
{
|
|
9161
|
-
"path": "components/blocks/ecommerce-trust-strip
|
|
9208
|
+
"path": "components/blocks/ecommerce-trust-strip.tsx",
|
|
9162
9209
|
"type": "registry:block",
|
|
9163
9210
|
"content": "\"use client\";\n\nimport React from \"react\";\nimport { ShieldCheck, Truck, RotateCcw, Headphones, Lock, Leaf } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\ntype Badge = {\n id: string;\n icon: React.ComponentType<{ className?: string }>;\n title: string;\n sub: string;\n};\n\nconst BADGES: Badge[] = [\n { id: \"secure\", icon: Lock, title: \"Secure checkout\", sub: \"256-bit SSL encryption\" },\n { id: \"ship\", icon: Truck, title: \"Free shipping\", sub: \"On orders over $50\" },\n { id: \"returns\", icon: RotateCcw, title: \"60-day returns\", sub: \"No questions asked\" },\n { id: \"warranty\", icon: ShieldCheck, title: \"2-year warranty\", sub: \"On all products\" },\n { id: \"support\", icon: Headphones, title: \"24/7 support\", sub: \"Real humans, anytime\" },\n { id: \"eco\", icon: Leaf, title: \"Carbon neutral\", sub: \"Offset every order\" },\n];\n\nexport interface EcommerceTrustStripBlockProps {\n className?: string;\n}\n\nexport function EcommerceTrustStripBlock({ className }: EcommerceTrustStripBlockProps) {\n return (\n <div className={cn(\"w-full max-w-2xl bg-card/45 backdrop-blur-xl border border-border/50 p-4 rounded-2xl shadow-xl font-sans text-foreground\", className)}>\n <ul className=\"grid grid-cols-2 sm:grid-cols-3 gap-2.5\">\n {BADGES.map((b) => {\n const Icon = b.icon;\n return (\n <li key={b.id} className=\"flex items-center gap-2.5 rounded-xl border border-foreground/[0.05] bg-foreground/[0.02] p-3.5\">\n <div className=\"h-10 w-10 rounded-lg bg-primary/10 text-primary flex items-center justify-center shrink-0\">\n <Icon className=\"w-5 h-5\" />\n </div>\n <div className=\"min-w-0\">\n <div className=\"text-sm font-semibold truncate\">{b.title}</div>\n <div className=\"text-xs text-muted-foreground/55 truncate\">{b.sub}</div>\n </div>\n </li>\n );\n })}\n </ul>\n </div>\n );\n}\n"
|
|
9164
9211
|
}
|
|
@@ -9277,7 +9324,7 @@
|
|
|
9277
9324
|
],
|
|
9278
9325
|
"files": [
|
|
9279
9326
|
{
|
|
9280
|
-
"path": "components/ui/elastic-image-
|
|
9327
|
+
"path": "components/ui/elastic-image-shrink-carousel.tsx",
|
|
9281
9328
|
"type": "registry:ui",
|
|
9282
9329
|
"content": "'use client';\n\nimport React, { useRef, useState } from 'react';\nimport { motion, useReducedMotion } from \"framer-motion\";\nimport { Sparkles } from 'lucide-react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface ElasticImageShrinkerCarouselProps {\n className?: string;\n}\n\nexport const ElasticImageShrinkerCarousel: React.FC<ElasticImageShrinkerCarouselProps> = ({ className }) => {\n const [isPressing, setIsPressing] = useState(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const prefersReducedMotion = useReducedMotion();\n\n return (\n <div className={cn('relative w-full max-w-[320px] h-[340px] bg-secondary/5 rounded-3xl border border-border flex flex-col justify-between p-5 overflow-hidden select-none', className)}>\n <span className=\"text-[9px] text-muted-foreground font-black tracking-widest uppercase\">Elastic Image Shrinker</span>\n\n <div \n ref={containerRef}\n onPointerDown={() => setIsPressing(true)}\n onPointerUp={() => setIsPressing(false)}\n onPointerLeave={() => setIsPressing(false)}\n className=\"relative w-full h-[180px] border border-border rounded-2xl overflow-hidden bg-card cursor-grab active:cursor-grabbing shadow-lg\"\n >\n <motion.div\n animate={{\n scale: isPressing ? 0.93 : 1,\n borderRadius: isPressing ? \"24px\" : \"16px\",\n }}\n transition={prefersReducedMotion ? { duration: 0 } : { type: \"spring\", stiffness: 180, damping: 15 }}\n className=\"w-full h-full bg-gradient-to-tr from-purple-900/80 to-indigo-900/80 p-4 flex flex-col justify-between text-white\"\n >\n <div className=\"flex justify-between items-start\">\n <span className=\"text-[8px] font-mono tracking-widest bg-white/20 px-2 py-0.5 rounded-full uppercase\">ELASTIC SCALE</span>\n <Sparkles className=\"w-4 h-4\" />\n </div>\n <div>\n <h4 className=\"text-xs font-black tracking-wide\">Press and hold element</h4>\n <p className=\"text-[9px] leading-relaxed opacity-85 mt-1\">Pressing anywhere pulls the visual frame boundary inward elastically, snapping back on release.</p>\n </div>\n </motion.div>\n </div>\n\n <span className=\"text-[8px] text-muted-foreground font-extrabold uppercase tracking-wide text-center\">Press pointer over canvas to shrink</span>\n </div>\n );\n};\n"
|
|
9283
9330
|
}
|
|
@@ -9367,7 +9414,7 @@
|
|
|
9367
9414
|
],
|
|
9368
9415
|
"files": [
|
|
9369
9416
|
{
|
|
9370
|
-
"path": "components/ui/elastic-search-pill-
|
|
9417
|
+
"path": "components/ui/elastic-search-pill-suggestions.tsx",
|
|
9371
9418
|
"type": "registry:ui",
|
|
9372
9419
|
"content": "\"use client\";\n\nimport React, { useState } from \"react\";\nimport { motion, AnimatePresence} from \"framer-motion\";\nimport { Search, X, Sparkles } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\ninterface ElasticSearchPillWithSuggestionsProps {\n className?: string;\n}\n\nexport function ElasticSearchPillWithSuggestions({ className }: ElasticSearchPillWithSuggestionsProps) {\n const [isFocused, setIsFocused] = useState(false);\n const [query, setQuery] = useState(\"\");\n\n const mockSuggestions = [\n { title: \"Dashboard Overview\", tag: \"Analytics\", link: \"#\" },\n { title: \"API Secure Gateway\", tag: \"Security\", link: \"#\" },\n { title: \"Theme Customizer Studio\", tag: \"Aesthetics\", link: \"#\" },\n ];\n\n return (\n <div\n className={cn(\n \"relative flex flex-col justify-between border border-white/5 bg-zinc-950/40 rounded-2xl p-6 shadow-2xl font-sans select-none\",\n className\n )}\n style={{ width: 340, height: 260 }}\n >\n <div className=\"absolute top-4 left-4 flex flex-col gap-1 font-mono select-none\">\n <span className=\"text-[9px] uppercase tracking-widest font-bold text-primary\">SEARCH_PILL</span>\n <span className=\"text-[8px] text-white/40 uppercase font-semibold\">Click input to trigger expanding list</span>\n </div>\n\n {/* Pill Search Core */}\n <div className=\"relative mt-12 w-full z-20\">\n <motion.div\n animate={{\n borderColor: isFocused ? \"var(--primary)\" : \"rgba(255, 255, 255, 0.1)\",\n boxShadow: isFocused ? \"0 0 15px rgba(var(--primary), 0.25)\" : \"none\",\n }}\n className=\"flex items-center gap-2 px-3 py-2 bg-black border rounded-full w-full relative\"\n >\n <Search className=\"w-4 h-4 text-white/40 shrink-0\" />\n <input\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n onFocus={() => setIsFocused(true)}\n onBlur={() => setTimeout(() => setIsFocused(false), 200)}\n placeholder=\"Search systems & components...\"\n className=\"flex-1 bg-transparent border-none outline-none font-sans text-xs text-white placeholder-white/35\"\n />\n {query.length > 0 && (\n <button onClick={() => setQuery(\"\")} className=\"text-white/40 hover:text-white cursor-pointer\">\n <X className=\"w-3.5 h-3.5\" />\n </button>\n )}\n </motion.div>\n\n {/* Suggestion Dropdown List */}\n <AnimatePresence>\n {isFocused && (\n <motion.div\n initial={{ opacity: 0, y: 10, scale: 0.95 }}\n animate={{ opacity: 1, y: 6, scale: 1 }}\n exit={{ opacity: 0, y: 10, scale: 0.95 }}\n className=\"absolute left-0 right-0 bg-zinc-950/95 border border-white/10 rounded-2xl p-3 flex flex-col gap-2 shadow-2xl backdrop-blur-md z-30\"\n >\n <span className=\"text-[8px] font-mono text-white/30 uppercase tracking-widest px-1\">SUGGESTED CHANNELS</span>\n\n <div className=\"flex flex-col gap-1.5\">\n {mockSuggestions.map((item, idx) => (\n <motion.div\n key={idx}\n initial={{ opacity: 0, x: -5 }}\n animate={{ opacity: 1, x: 0 }}\n transition={{ delay: idx * 0.05 }}\n className=\"flex items-center justify-between p-2 rounded-lg hover:bg-white/5 cursor-pointer text-white/70 hover:text-white transition-colors duration-200\"\n >\n <div className=\"flex items-center gap-1.5\">\n <Sparkles className=\"w-3 h-3 text-primary shrink-0\" />\n <span className=\"text-[10px] font-bold tracking-tight\">{item.title}</span>\n </div>\n <span className=\"text-[7px] font-mono font-bold bg-white/5 px-2 py-0.5 rounded text-white/50\">{item.tag}</span>\n </motion.div>\n ))}\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n\n <div className=\"text-[8px] font-mono text-white/30 uppercase tracking-widest mt-auto\">\n SYSTEM_INDEX // SECURED\n </div>\n </div>\n );\n}\n"
|
|
9373
9420
|
}
|
|
@@ -9888,7 +9935,22 @@
|
|
|
9888
9935
|
{
|
|
9889
9936
|
"path": "components/blocks/feature-bento-grid.tsx",
|
|
9890
9937
|
"type": "registry:block",
|
|
9891
|
-
"content": "import React from 'react';\nimport { motion} from \"framer-motion\";\nimport { Card, CardContent } from '
|
|
9938
|
+
"content": "import React from 'react';\nimport { motion} from \"framer-motion\";\nimport { Card, CardContent } from './card';\nimport { SectionHeader } from './section-header';\nimport { Container } from './container';\nimport { Terminal, Shield, Zap, Globe, Cpu } from 'lucide-react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface BentoItem {\n id: string;\n title: string;\n description: string;\n icon: React.ReactNode;\n className?: string;\n badge?: string;\n}\n\nexport interface FeatureBentoGridProps {\n tag?: string;\n title?: string;\n description?: string;\n items?: BentoItem[];\n}\n\nconst defaultItems: BentoItem[] = [\n {\n id: '1',\n title: 'Lightning Fast Speeds',\n description: 'Optimize your performance with custom compilers and Edge delivery networks designed for sub-millisecond reactions.',\n icon: <Zap className=\"h-6 w-6 text-amber-500\" />,\n className: 'md:col-span-2 md:row-span-1 bg-amber-500/5 hover:border-amber-500/30',\n badge: 'Performance',\n },\n {\n id: '2',\n title: 'Developer Console',\n description: 'A powerful interactive CLI toolchain that helps you deploy and inspect instantly.',\n icon: <Terminal className=\"h-6 w-6 text-blue-500\" />,\n className: 'md:col-span-1 md:row-span-1 bg-blue-500/5 hover:border-blue-500/30',\n },\n {\n id: '3',\n title: 'Zero-Trust Security',\n description: 'We safeguard your databases with built-in active encryption, biometric passkeys, and multi-region backups.',\n icon: <Shield className=\"h-6 w-6 text-emerald-500\" />,\n className: 'md:col-span-1 md:row-span-2 bg-emerald-500/5 hover:border-emerald-500/30 flex-col justify-between',\n badge: 'Security',\n },\n {\n id: '4',\n title: 'Global Scale Node Network',\n description: 'Serve your application globally with over 150 low-latency edge caching regions mapped automatically.',\n icon: <Globe className=\"h-6 w-6 text-purple-500\" />,\n className: 'md:col-span-2 md:row-span-1 bg-purple-500/5 hover:border-purple-500/30',\n },\n {\n id: '5',\n title: 'Neural Core AI Systems',\n description: 'Powered by highly optimized inference models yielding context-aware automated coding operations.',\n icon: <Cpu className=\"h-6 w-6 text-indigo-500\" />,\n className: 'md:col-span-2 md:row-span-1 bg-indigo-500/5 hover:border-indigo-500/30',\n badge: 'AI Powered',\n },\n];\n\nexport function FeatureBentoGrid({\n tag = 'Platform Capabilities',\n title = 'Engineered for the Modern Web',\n description = 'Everything you need to ship highly interactive, responsive applications at global scale without the operations tax.',\n items = defaultItems,\n}: FeatureBentoGridProps) {\n return (\n <section className=\"py-20 bg-background\">\n <Container>\n <SectionHeader tag={tag} title={title} description={description} align=\"center\" />\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-6 auto-rows-[minmax(200px,auto)]\">\n {items.map((item, index) => (\n <motion.div\n key={item.id}\n initial={{ opacity: 0, y: 30 }}\n whileInView={{ opacity: 1, y: 0 }}\n viewport={{ once: true, margin: '-100px' }}\n transition={{ duration: 0.5, delay: index * 0.1 }}\n className=\"flex h-full\"\n >\n <Card\n className={cn(\n 'w-full hover:shadow-md transition-all duration-300 border border-border flex flex-col justify-between overflow-hidden',\n item.className\n )}\n >\n <CardContent className=\"p-8 flex flex-col h-full justify-between gap-6\">\n <div className=\"flex items-center justify-between\">\n <div className=\"p-3 bg-background rounded-lg shadow-sm w-fit border border-border\">\n {item.icon}\n </div>\n {item.badge && (\n <span className=\"text-[10px] uppercase font-bold tracking-wider px-2 py-0.5 rounded-full border border-border bg-background\">\n {item.badge}\n </span>\n )}\n </div>\n <div>\n <h3 className=\"text-xl font-bold text-foreground mb-2\">{item.title}</h3>\n <p className=\"text-sm text-muted-foreground leading-relaxed\">{item.description}</p>\n </div>\n </CardContent>\n </Card>\n </motion.div>\n ))}\n </div>\n </Container>\n </section>\n );\n}\n"
|
|
9939
|
+
},
|
|
9940
|
+
{
|
|
9941
|
+
"path": "components/blocks/card.tsx",
|
|
9942
|
+
"type": "registry:block",
|
|
9943
|
+
"content": "import React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nconst Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n const isInteractive =\n typeof props.onClick === 'function' || props.tabIndex !== undefined || props.role === 'button';\n return (\n <div\n ref={ref}\n className={cn(\n 'rounded-lg border border-border bg-card text-card-foreground shadow-sm transition-all duration-150 ease-out',\n isInteractive &&\n 'cursor-pointer hover:shadow-md hover:border-border/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:scale-[0.99] motion-reduce:active:scale-100 motion-reduce:transition-none',\n className\n )}\n {...props}\n />\n );\n }\n);\nCard.displayName = 'Card';\n\nconst CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex flex-col space-y-1.5 p-6', className)}\n {...props}\n />\n )\n);\nCardHeader.displayName = 'CardHeader';\n\nconst CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(\n ({ className, children, ...props }, ref) =>\n children == null || children === '' ? null : (\n <h3\n ref={ref}\n className={cn('text-2xl font-semibold leading-none tracking-tight', className)}\n {...props}\n >\n {children}\n </h3>\n )\n);\nCardTitle.displayName = 'CardTitle';\n\nconst CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(\n ({ className, ...props }, ref) => (\n <p\n ref={ref}\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n )\n);\nCardDescription.displayName = 'CardDescription';\n\nconst CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />\n )\n);\nCardContent.displayName = 'CardContent';\n\nconst CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center p-6 pt-0 border-t border-border mt-6', className)}\n {...props}\n />\n )\n);\nCardFooter.displayName = 'CardFooter';\n\nexport { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };\n"
|
|
9944
|
+
},
|
|
9945
|
+
{
|
|
9946
|
+
"path": "components/blocks/section-header.tsx",
|
|
9947
|
+
"type": "registry:block",
|
|
9948
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface SectionHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n tag?: string;\n title: string;\n description?: string;\n align?: 'left' | 'center' | 'right';\n actions?: React.ReactNode;\n}\n\nconst SectionHeader = React.forwardRef<HTMLDivElement, SectionHeaderProps>(\n ({ className, tag, title, description, align = 'left', actions, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn(\n 'flex flex-col md:flex-row md:items-end justify-between gap-6 mb-8 md:mb-12',\n align === 'center' && 'text-center md:flex-col md:items-center',\n align === 'right' && 'text-right md:flex-row-reverse',\n className\n )}\n {...props}\n >\n <div className={cn('flex-1 max-w-3xl', align === 'center' && 'mx-auto')}>\n {tag && (\n <span className=\"inline-block text-sm font-semibold tracking-wider uppercase text-primary mb-2\">\n {tag}\n </span>\n )}\n {title && (\n <h2 className=\"text-3xl font-bold tracking-tight sm:text-4xl text-foreground\">\n {title}\n </h2>\n )}\n {description && (\n <p className=\"mt-4 text-lg text-muted-foreground leading-relaxed\">\n {description}\n </p>\n )}\n </div>\n {actions && (\n <div\n className={cn(\n 'flex flex-wrap gap-3 shrink-0',\n align === 'center' && 'justify-center'\n )}\n >\n {actions}\n </div>\n )}\n </div>\n );\n }\n);\nSectionHeader.displayName = 'SectionHeader';\n\nexport { SectionHeader };\n"
|
|
9949
|
+
},
|
|
9950
|
+
{
|
|
9951
|
+
"path": "components/blocks/container.tsx",
|
|
9952
|
+
"type": "registry:block",
|
|
9953
|
+
"content": "import React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> {\n as?: React.ElementType;\n clean?: boolean;\n}\n\nconst Container = React.forwardRef<HTMLDivElement, ContainerProps>(\n ({ className, as: Component = 'div', clean = false, ...props }, ref) => {\n return (\n <Component\n ref={ref}\n className={cn(\n !clean && 'mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8',\n className\n )}\n {...props}\n />\n );\n }\n);\nContainer.displayName = 'Container';\n\nexport { Container };\n"
|
|
9892
9954
|
}
|
|
9893
9955
|
]
|
|
9894
9956
|
},
|
|
@@ -9931,7 +9993,7 @@
|
|
|
9931
9993
|
],
|
|
9932
9994
|
"files": [
|
|
9933
9995
|
{
|
|
9934
|
-
"path": "components/blocks/feature-
|
|
9996
|
+
"path": "components/blocks/feature-grid-3-column.tsx",
|
|
9935
9997
|
"type": "registry:block",
|
|
9936
9998
|
"content": "\"use client\";\n\nimport React from \"react\";\nimport { motion, useReducedMotion } from \"framer-motion\";\nimport {\n ArrowUpRight,\n Boxes,\n Gauge,\n Keyboard,\n Layers,\n Paintbrush,\n ShieldCheck,\n} from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface FeatureGrid3ColumnProps {\n className?: string;\n}\n\nconst FEATURES = [\n {\n title: \"Composable primitives\",\n description:\n \"Small, focused parts you arrange into anything — from a single button to a full marketing page.\",\n icon: Boxes,\n href: \"#composable\",\n },\n {\n title: \"Tokenized theming\",\n description:\n \"Color, spacing, type, and motion live as CSS variables you can swap per brand or per surface.\",\n icon: Paintbrush,\n href: \"#tokens\",\n },\n {\n title: \"Accessibility by default\",\n description:\n \"Keyboard, screen reader, and reduced-motion behavior verified by axe-core in CI on every commit.\",\n icon: ShieldCheck,\n href: \"#a11y\",\n },\n {\n title: \"Performance budget\",\n description:\n \"Tree-shakeable exports, lazy motion, and zero runtime CSS keep your INP and LCP healthy.\",\n icon: Gauge,\n href: \"#performance\",\n },\n {\n title: \"Stackable blocks\",\n description:\n \"Drop in heroes, pricing, dashboards, and footers that already speak the same design language.\",\n icon: Layers,\n href: \"#blocks\",\n },\n {\n title: \"Keyboard ergonomics\",\n description:\n \"Roving tabindex, focus rings, and arrow-key navigation built into every interactive primitive.\",\n icon: Keyboard,\n href: \"#keyboard\",\n },\n] as const;\n\nexport function FeatureGrid3Column({ className }: FeatureGrid3ColumnProps) {\n const headingId = React.useId();\n const reduceMotion = useReducedMotion();\n\n return (\n <section\n aria-labelledby={headingId}\n className={cn(\n \"w-full max-w-5xl rounded-3xl border border-border/60 bg-card/40 p-8 text-foreground backdrop-blur-xl sm:p-10\",\n className,\n )}\n >\n <header className=\"mb-8 max-w-2xl\">\n <p className=\"text-xs font-semibold uppercase tracking-[0.18em] text-primary\">\n Why teams choose us\n </p>\n <h2\n id={headingId}\n className=\"mt-2 text-2xl font-semibold tracking-tight sm:text-3xl\"\n >\n Everything you need to ship interfaces faster\n </h2>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n Six foundations that move the needle on the metrics product teams care\n about — without trading off design quality.\n </p>\n </header>\n\n <ul\n className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\"\n >\n {FEATURES.map((feature, index) => {\n const Icon = feature.icon;\n return (\n <motion.li\n key={feature.title}\n initial={reduceMotion ? false : { opacity: 0, y: 12 }}\n whileInView={reduceMotion ? undefined : { opacity: 1, y: 0 }}\n viewport={{ once: true, margin: \"-40px\" }}\n transition={\n reduceMotion\n ? undefined\n : { duration: 0.35, delay: index * 0.06, ease: \"easeOut\" }\n }\n >\n <article\n className={cn(\n \"group h-full rounded-2xl border border-border/60 bg-background/60 p-5 transition-all\",\n reduceMotion\n ? \"hover:border-border\"\n : \"hover:-translate-y-0.5 hover:border-border hover:shadow-sm\",\n )}\n >\n <span\n aria-hidden=\"true\"\n className=\"inline-flex h-10 w-10 items-center justify-center rounded-xl border border-primary/30 bg-primary/10 text-primary\"\n >\n <Icon className=\"h-5 w-5\" />\n </span>\n <h3 className=\"mt-4 text-base font-semibold text-foreground\">\n {feature.title}\n </h3>\n <p className=\"mt-1.5 text-sm leading-relaxed text-muted-foreground\">\n {feature.description}\n </p>\n <a\n href={feature.href}\n className={cn(\n \"mt-4 inline-flex items-center gap-1 text-sm font-medium text-primary\",\n \"transition-colors hover:text-primary/80\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background\",\n )}\n >\n Learn more\n <ArrowUpRight\n className={cn(\n \"h-4 w-4 transition-transform\",\n !reduceMotion && \"group-hover:translate-x-0.5 group-hover:-translate-y-0.5\",\n )}\n aria-hidden=\"true\"\n />\n </a>\n </article>\n </motion.li>\n );\n })}\n </ul>\n </section>\n );\n}\n"
|
|
9937
9999
|
}
|
|
@@ -10431,7 +10493,7 @@
|
|
|
10431
10493
|
],
|
|
10432
10494
|
"files": [
|
|
10433
10495
|
{
|
|
10434
|
-
"path": "components/blocks/footer-docs-basic
|
|
10496
|
+
"path": "components/blocks/footer-docs-basic.tsx",
|
|
10435
10497
|
"type": "registry:block",
|
|
10436
10498
|
"content": "\"use client\";\n\nimport React from \"react\";\nimport { BookOpen } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface FooterDocsBasicProps {\n className?: string;\n}\n\ninterface Column {\n title: string;\n links: string[];\n}\n\nconst COLUMNS: Column[] = [\n { title: \"Docs\", links: [\"Introduction\", \"Installation\", \"Quick start\", \"Changelog\"] },\n { title: \"Guides\", links: [\"Authentication\", \"Theming\", \"Deployment\", \"Migrations\"] },\n { title: \"API\", links: [\"REST reference\", \"Webhooks\", \"Rate limits\", \"SDKs\"] },\n { title: \"Community\", links: [\"GitHub\", \"Discord\", \"Stack Overflow\", \"Roadmap\"] },\n];\n\nexport function FooterDocsBasic({ className }: FooterDocsBasicProps) {\n return (\n <footer\n aria-label=\"Documentation footer\"\n className={cn(\"w-full border-t border-border bg-background text-foreground\", className)}\n >\n <div className=\"mx-auto max-w-6xl px-6 py-12\">\n <div className=\"grid grid-cols-2 gap-8 md:grid-cols-4\">\n {COLUMNS.map((column) => (\n <nav key={column.title} aria-label={column.title}>\n <h3 className=\"text-sm font-semibold text-foreground\">{column.title}</h3>\n <ul className=\"mt-4 space-y-3\">\n {column.links.map((link) => (\n <li key={link}>\n <a\n href=\"/\"\n className=\"text-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n >\n {link}\n </a>\n </li>\n ))}\n </ul>\n </nav>\n ))}\n </div>\n\n <div className=\"mt-10 flex flex-col items-center justify-between gap-4 border-t border-border pt-6 sm:flex-row\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex h-7 w-7 items-center justify-center rounded-md bg-primary text-primary-foreground\">\n <BookOpen className=\"h-4 w-4\" />\n </span>\n <span className=\"text-sm font-semibold text-foreground\">Acme Docs</span>\n </div>\n <p className=\"text-sm text-muted-foreground\">© 2026 Acme Inc. All rights reserved.</p>\n </div>\n </div>\n </footer>\n );\n}\n\nexport default FooterDocsBasic;\n"
|
|
10437
10499
|
}
|
|
@@ -10457,7 +10519,7 @@
|
|
|
10457
10519
|
],
|
|
10458
10520
|
"files": [
|
|
10459
10521
|
{
|
|
10460
|
-
"path": "components/blocks/footer-ecommerce-basic
|
|
10522
|
+
"path": "components/blocks/footer-ecommerce-basic.tsx",
|
|
10461
10523
|
"type": "registry:block",
|
|
10462
10524
|
"content": "\"use client\";\n\nimport React, { useState } from \"react\";\nimport { ShoppingBag, CreditCard } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface FooterEcommerceBasicProps {\n className?: string;\n}\n\ninterface Column {\n title: string;\n links: string[];\n}\n\nconst COLUMNS: Column[] = [\n { title: \"Shop\", links: [\"New arrivals\", \"Best sellers\", \"Sale\", \"Gift cards\"] },\n { title: \"Support\", links: [\"Shipping\", \"Returns\", \"Order status\", \"Contact us\"] },\n { title: \"Company\", links: [\"About\", \"Careers\", \"Sustainability\", \"Press\"] },\n];\n\nconst PAYMENTS = [\"Visa\", \"Mastercard\", \"Amex\", \"PayPal\"];\n\nexport function FooterEcommerceBasic({ className }: FooterEcommerceBasicProps) {\n const [email, setEmail] = useState(\"\");\n\n return (\n <footer\n aria-label=\"Store footer\"\n className={cn(\"w-full border-t border-border bg-background text-foreground\", className)}\n >\n <div className=\"mx-auto max-w-6xl px-6 py-12\">\n <div className=\"grid grid-cols-1 gap-10 md:grid-cols-2 lg:grid-cols-4\">\n {COLUMNS.map((column) => (\n <nav key={column.title} aria-label={column.title}>\n <h3 className=\"text-sm font-semibold text-foreground\">{column.title}</h3>\n <ul className=\"mt-4 space-y-3\">\n {column.links.map((link) => (\n <li key={link}>\n <a\n href=\"/\"\n className=\"text-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n >\n {link}\n </a>\n </li>\n ))}\n </ul>\n </nav>\n ))}\n\n <div>\n <h3 className=\"text-sm font-semibold text-foreground\">Stay in the loop</h3>\n <p className=\"mt-4 text-sm text-muted-foreground\">\n Get early access to drops and exclusive offers.\n </p>\n <form\n className=\"mt-4 flex gap-2\"\n onSubmit={(e) => e.preventDefault()}\n >\n <label htmlFor=\"footer-newsletter-email\" className=\"sr-only\">\n Email address\n </label>\n <input\n id=\"footer-newsletter-email\"\n type=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"you@example.com\"\n className=\"h-10 min-w-0 flex-1 rounded-lg border border-border bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n />\n <button\n type=\"submit\"\n className=\"inline-flex h-10 items-center justify-center rounded-lg bg-primary px-4 text-sm font-semibold text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n >\n Subscribe\n </button>\n </form>\n </div>\n </div>\n\n <div className=\"mt-10 flex flex-col items-center justify-between gap-4 border-t border-border pt-6 sm:flex-row\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex h-7 w-7 items-center justify-center rounded-md bg-primary text-primary-foreground\">\n <ShoppingBag className=\"h-4 w-4\" />\n </span>\n <span className=\"text-sm font-semibold text-foreground\">Marrow & Co.</span>\n </div>\n\n <div className=\"flex items-center gap-2\" aria-label=\"Accepted payment methods\">\n {PAYMENTS.map((method) => (\n <span\n key={method}\n className=\"inline-flex h-7 items-center gap-1 rounded-md border border-border bg-muted px-2 text-xs font-medium text-muted-foreground\"\n >\n <CreditCard className=\"h-3 w-3\" aria-hidden=\"true\" />\n {method}\n </span>\n ))}\n </div>\n </div>\n\n <p className=\"mt-6 text-center text-sm text-muted-foreground sm:text-left\">\n © 2026 Marrow & Co. All rights reserved.\n </p>\n </div>\n </footer>\n );\n}\n\nexport default FooterEcommerceBasic;\n"
|
|
10463
10525
|
}
|
|
@@ -10543,7 +10605,7 @@
|
|
|
10543
10605
|
],
|
|
10544
10606
|
"files": [
|
|
10545
10607
|
{
|
|
10546
|
-
"path": "components/blocks/footer-saas-basic
|
|
10608
|
+
"path": "components/blocks/footer-saas-basic.tsx",
|
|
10547
10609
|
"type": "registry:block",
|
|
10548
10610
|
"content": "\"use client\";\n\nimport React from \"react\";\nimport { Hexagon, Twitter, Github, Linkedin, Youtube } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface FooterSaasBasicProps {\n className?: string;\n}\n\ninterface Column {\n title: string;\n links: string[];\n}\n\nconst COLUMNS: Column[] = [\n { title: \"Product\", links: [\"Features\", \"Integrations\", \"Pricing\", \"Changelog\"] },\n { title: \"Company\", links: [\"About\", \"Blog\", \"Careers\", \"Contact\"] },\n { title: \"Resources\", links: [\"Documentation\", \"Help center\", \"Community\", \"Status\"] },\n { title: \"Legal\", links: [\"Privacy\", \"Terms\", \"Security\", \"Cookies\"] },\n];\n\nconst SOCIALS = [\n { label: \"Twitter\", icon: Twitter },\n { label: \"GitHub\", icon: Github },\n { label: \"LinkedIn\", icon: Linkedin },\n { label: \"YouTube\", icon: Youtube },\n];\n\nexport function FooterSaasBasic({ className }: FooterSaasBasicProps) {\n return (\n <footer\n aria-label=\"Site footer\"\n className={cn(\"w-full border-t border-border bg-background text-foreground\", className)}\n >\n <div className=\"mx-auto max-w-6xl px-6 py-12\">\n <div className=\"grid grid-cols-2 gap-10 md:grid-cols-3 lg:grid-cols-6\">\n <div className=\"col-span-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"flex h-8 w-8 items-center justify-center rounded-lg bg-primary text-primary-foreground\">\n <Hexagon className=\"h-4 w-4\" />\n </span>\n <span className=\"text-base font-semibold text-foreground\">Lumen</span>\n </div>\n <p className=\"mt-4 max-w-xs text-sm text-muted-foreground\">\n The all-in-one platform that helps modern teams ship faster and scale with confidence.\n </p>\n </div>\n\n {COLUMNS.map((column) => (\n <nav key={column.title} aria-label={column.title}>\n <h3 className=\"text-sm font-semibold text-foreground\">{column.title}</h3>\n <ul className=\"mt-4 space-y-3\">\n {column.links.map((link) => (\n <li key={link}>\n <a\n href=\"/\"\n className=\"text-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n >\n {link}\n </a>\n </li>\n ))}\n </ul>\n </nav>\n ))}\n </div>\n\n <div className=\"mt-10 flex flex-col items-center justify-between gap-4 border-t border-border pt-6 sm:flex-row\">\n <p className=\"text-sm text-muted-foreground\">© 2026 Lumen Technologies, Inc.</p>\n <div className=\"flex items-center gap-1\">\n {SOCIALS.map(({ label, icon: Icon }) => (\n <a\n key={label}\n href=\"/\"\n aria-label={label}\n className=\"flex h-9 w-9 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n >\n <Icon className=\"h-4 w-4\" />\n </a>\n ))}\n </div>\n </div>\n </div>\n </footer>\n );\n}\n\nexport default FooterSaasBasic;\n"
|
|
10549
10611
|
}
|
|
@@ -15025,7 +15087,7 @@
|
|
|
15025
15087
|
],
|
|
15026
15088
|
"files": [
|
|
15027
15089
|
{
|
|
15028
|
-
"path": "components/blocks/
|
|
15090
|
+
"path": "components/blocks/pricing-3-tier-section.tsx",
|
|
15029
15091
|
"type": "registry:block",
|
|
15030
15092
|
"content": "\"use client\";\n\nimport React, { useState } from \"react\";\nimport { motion, useReducedMotion } from \"framer-motion\";\nimport { Check, Sparkles } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface Pricing3TierSectionProps {\n className?: string;\n}\n\ntype Billing = \"monthly\" | \"annual\";\n\ninterface Tier {\n name: string;\n tagline: string;\n monthly: number;\n annual: number;\n features: string[];\n cta: string;\n highlighted?: boolean;\n}\n\nconst TIERS: Tier[] = [\n {\n name: \"Starter\",\n tagline: \"For indie builders\",\n monthly: 19,\n annual: 15,\n features: [\"3 active projects\", \"10 GB storage\", \"Community support\"],\n cta: \"Start free\",\n },\n {\n name: \"Pro\",\n tagline: \"For growing teams\",\n monthly: 49,\n annual: 39,\n features: [\n \"Unlimited projects\",\n \"100 GB storage\",\n \"Priority support\",\n \"Advanced analytics\",\n ],\n cta: \"Start 14-day trial\",\n highlighted: true,\n },\n {\n name: \"Enterprise\",\n tagline: \"For scaling orgs\",\n monthly: 99,\n annual: 79,\n features: [\n \"Everything in Pro\",\n \"SSO + SCIM\",\n \"Dedicated CSM\",\n \"99.99% uptime SLA\",\n ],\n cta: \"Contact sales\",\n },\n];\n\nexport function Pricing3TierSection({ className }: Pricing3TierSectionProps) {\n const reduced = useReducedMotion();\n const [billing, setBilling] = useState<Billing>(\"annual\");\n const headingId = React.useId();\n\n return (\n <section\n aria-labelledby={headingId}\n className={cn(\n \"relative isolate w-full overflow-hidden rounded-3xl border border-border/50 bg-card/30 px-6 py-16 backdrop-blur-xl sm:px-10 sm:py-20\",\n className,\n )}\n >\n <div aria-hidden className=\"pointer-events-none absolute inset-0 -z-10\">\n <div className=\"absolute left-1/2 top-0 h-72 w-[36rem] -translate-x-1/2 rounded-full bg-primary/15 blur-3xl\" />\n <div className=\"absolute -right-32 bottom-0 h-72 w-72 rounded-full bg-accent/10 blur-3xl\" />\n </div>\n\n <div className=\"relative mx-auto max-w-5xl\">\n <div className=\"mx-auto max-w-2xl text-center\">\n <motion.span\n initial={reduced ? false : { opacity: 0, y: -8 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.4 }}\n className=\"inline-flex items-center gap-2 rounded-full border border-primary/20 bg-primary/10 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-primary\"\n >\n <Sparkles className=\"h-3.5 w-3.5\" aria-hidden />\n Simple pricing\n </motion.span>\n\n <motion.h2\n id={headingId}\n initial={reduced ? false : { opacity: 0, y: 14 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.55, delay: 0.06 }}\n className=\"mt-5 text-balance text-3xl font-semibold leading-[1.05] tracking-tight text-foreground sm:text-4xl md:text-5xl\"\n >\n Plans that scale{\" \"}\n <span className=\"bg-gradient-to-br from-primary via-primary to-accent bg-clip-text text-transparent\">\n with you\n </span>\n </motion.h2>\n\n <motion.p\n initial={reduced ? false : { opacity: 0, y: 14 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.55, delay: 0.14 }}\n className=\"mx-auto mt-4 max-w-md text-pretty text-base leading-relaxed text-muted-foreground\"\n >\n Start free, upgrade when you outgrow the limits. Cancel anytime.\n </motion.p>\n\n <motion.div\n initial={reduced ? false : { opacity: 0, y: 14 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.55, delay: 0.22 }}\n className=\"mt-7 inline-flex items-center gap-1 rounded-full border border-border/60 bg-background/50 p-1\"\n role=\"tablist\"\n aria-label=\"Billing cycle\"\n >\n {([\"monthly\", \"annual\"] as const).map((t) => (\n <button\n key={t}\n type=\"button\"\n role=\"tab\"\n aria-selected={billing === t}\n onClick={() => setBilling(t)}\n className={cn(\n \"rounded-full px-4 py-1.5 text-xs font-semibold capitalize transition-all focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/40\",\n billing === t\n ? \"bg-primary text-primary-foreground shadow\"\n : \"text-muted-foreground hover:text-foreground\",\n )}\n >\n {t}\n {t === \"annual\" && (\n <span className=\"ml-1.5 rounded-full bg-emerald-400/15 px-1.5 py-0.5 text-[10px] font-bold text-emerald-400\">\n −20%\n </span>\n )}\n </button>\n ))}\n </motion.div>\n </div>\n\n <div className=\"mt-12 grid grid-cols-1 gap-6 md:grid-cols-3\">\n {TIERS.map((tier, i) => (\n <motion.div\n key={tier.name}\n initial={reduced ? false : { opacity: 0, y: 16 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.5, delay: 0.3 + i * 0.08 }}\n className={cn(\n \"group relative flex flex-col rounded-2xl border bg-card p-6 shadow-sm transition-all hover:-translate-y-0.5 hover:shadow-md md:p-8\",\n tier.highlighted\n ? \"border-primary/40 bg-gradient-to-b from-primary/5 to-card shadow-lg shadow-primary/10\"\n : \"border-border/60\",\n )}\n >\n {tier.highlighted && (\n <span className=\"absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-primary px-3 py-1 text-[10px] font-bold uppercase tracking-wider text-primary-foreground shadow-lg shadow-primary/30\">\n Most popular\n </span>\n )}\n\n <div>\n <h3 className=\"text-sm font-bold uppercase tracking-wider text-foreground\">\n {tier.name}\n </h3>\n <p className=\"mt-1 text-sm text-muted-foreground\">{tier.tagline}</p>\n <div className=\"mt-5 flex items-baseline gap-1.5\">\n <span className=\"text-4xl font-semibold tracking-tight text-foreground md:text-5xl\">\n ${billing === \"annual\" ? tier.annual : tier.monthly}\n </span>\n <span className=\"text-sm text-muted-foreground\">/mo</span>\n </div>\n {billing === \"annual\" && (\n <p className=\"mt-1 text-xs text-muted-foreground\">\n Billed annually\n </p>\n )}\n </div>\n\n <ul className=\"mt-6 flex-1 space-y-3 text-sm text-foreground/90\">\n {tier.features.map((f) => (\n <li key={f} className=\"flex items-start gap-2.5\">\n <Check\n className={cn(\n \"mt-0.5 h-4 w-4 flex-shrink-0\",\n tier.highlighted ? \"text-primary\" : \"text-foreground/70\",\n )}\n aria-hidden\n />\n <span>{f}</span>\n </li>\n ))}\n </ul>\n\n <button\n type=\"button\"\n className={cn(\n \"mt-8 inline-flex w-full items-center justify-center gap-1.5 rounded-xl px-5 py-3 text-sm font-semibold transition-all focus:outline-none focus-visible:ring-2\",\n tier.highlighted\n ? \"bg-primary text-primary-foreground shadow-lg shadow-primary/25 hover:brightness-110 focus-visible:ring-primary/50\"\n : \"border border-border bg-background text-foreground hover:bg-accent focus-visible:ring-primary/40\",\n )}\n >\n {tier.cta}\n </button>\n </motion.div>\n ))}\n </div>\n </div>\n </section>\n );\n}\n\nexport default Pricing3TierSection;\n"
|
|
15031
15093
|
}
|
|
@@ -15600,18 +15662,35 @@
|
|
|
15600
15662
|
"name": "product-card",
|
|
15601
15663
|
"type": "registry:block",
|
|
15602
15664
|
"dependencies": [
|
|
15665
|
+
"class-variance-authority",
|
|
15666
|
+
"framer-motion",
|
|
15603
15667
|
"lucide-react"
|
|
15604
15668
|
],
|
|
15605
15669
|
"files": [
|
|
15606
15670
|
{
|
|
15607
15671
|
"path": "components/blocks/product-card.tsx",
|
|
15608
15672
|
"type": "registry:block",
|
|
15609
|
-
"content": "\"use client\";\n\nimport React from 'react';\nimport { motion } from 'framer-motion';\nimport { Card, CardContent } from '
|
|
15673
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { motion } from 'framer-motion';\nimport { Card, CardContent } from './card';\nimport { Button } from './button';\nimport { Star, ShoppingCart, Heart } from 'lucide-react';\nimport { usePrefersReducedMotion } from \"./use-animation-hooks\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface Product {\n id: string;\n title: string;\n price: number;\n category?: string;\n rating?: number;\n reviewsCount?: number;\n imageUrl?: string;\n badge?: string;\n description?: string;\n}\n\nexport interface ProductCardProps {\n product: Product;\n onAddToCart?: (product: Product) => void;\n onFavoriteClick?: (product: Product) => void;\n /** When true, mark this card as favorited (filled heart). */\n isFavorited?: boolean;\n className?: string;\n /** Disable Framer Motion entry — useful when grid handles its own reveal. */\n noMotion?: boolean;\n}\n\nexport function ProductCard({\n product,\n onAddToCart,\n onFavoriteClick,\n isFavorited = false,\n className,\n noMotion = false,\n}: ProductCardProps) {\n const { title, price, rating = 4.5, reviewsCount = 42, imageUrl, badge, category, description } = product;\n const reduced = usePrefersReducedMotion();\n const useMotion = !noMotion && !reduced;\n\n return (\n <motion.div\n initial={useMotion ? { opacity: 0, y: 12 } : false}\n whileInView={useMotion ? { opacity: 1, y: 0 } : undefined}\n viewport={{ once: true, margin: '-40px' }}\n transition={{ duration: 0.45, ease: [0.22, 1, 0.36, 1] }}\n className=\"flex\"\n >\n <Card className={cn('group overflow-hidden flex flex-col w-full justify-between border border-border bg-card transition-all duration-300 hover:-translate-y-0.5 hover:shadow-md motion-reduce:hover:translate-y-0', className)}>\n <div className=\"relative aspect-square w-full overflow-hidden bg-muted/30 border-b border-border\">\n {imageUrl ? (\n <img\n src={imageUrl}\n alt={title}\n className=\"h-full w-full object-cover transition-transform duration-500 group-hover:scale-[1.04] motion-reduce:transition-none motion-reduce:group-hover:scale-100\"\n loading=\"lazy\"\n decoding=\"async\"\n />\n ) : (\n <div className=\"h-full w-full flex items-center justify-center text-muted-foreground text-xs bg-muted/40\" aria-hidden>\n No image available\n </div>\n )}\n {badge && (\n <span className=\"absolute top-3 left-3 bg-primary text-primary-foreground text-[10px] font-bold uppercase px-2 py-0.5 rounded shadow-sm\">\n {badge}\n </span>\n )}\n <button\n type=\"button\"\n aria-label={isFavorited ? `Remove ${title} from favorites` : `Add ${title} to favorites`}\n aria-pressed={isFavorited}\n onClick={(e) => {\n e.stopPropagation();\n if (onFavoriteClick) onFavoriteClick(product);\n }}\n className={cn(\n 'absolute top-3 right-3 p-2 bg-background/95 hover:bg-background border border-border rounded-full shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50',\n isFavorited ? 'text-rose-500' : 'text-muted-foreground hover:text-rose-500'\n )}\n >\n <Heart className={cn('h-4 w-4', isFavorited ? 'fill-current' : 'fill-none')} aria-hidden />\n </button>\n </div>\n\n <CardContent className=\"p-5 flex-1 flex flex-col justify-between gap-4\">\n <div className=\"space-y-1\">\n {category && (\n <span className=\"text-[10px] uppercase font-bold tracking-wider text-primary\">\n {category}\n </span>\n )}\n <h3 className=\"text-base font-bold text-foreground line-clamp-1 group-hover:text-primary transition-colors\">\n {title}\n </h3>\n {description && (\n <p className=\"text-xs text-muted-foreground line-clamp-2 leading-relaxed min-h-[32px]\">\n {description}\n </p>\n )}\n </div>\n\n <div>\n {/* Rating */}\n <div className=\"flex items-center gap-1 mb-3\" aria-label={`Rated ${rating} out of 5 from ${reviewsCount} reviews`}>\n <div className=\"flex items-center text-amber-400\" aria-hidden>\n {Array.from({ length: 5 }).map((_, i) => (\n <Star\n key={i}\n className={cn(\n 'h-3.5 w-3.5 fill-current',\n i < Math.floor(rating) ? 'text-amber-400' : 'text-muted/40'\n )}\n />\n ))}\n </div>\n <span className=\"text-[11px] text-muted-foreground\">({reviewsCount})</span>\n </div>\n\n {/* Pricing & Add to Cart */}\n <div className=\"flex items-center justify-between gap-4 mt-auto\">\n <span className=\"text-lg font-extrabold text-foreground\">${price.toFixed(2)}</span>\n <Button\n size=\"sm\"\n className=\"gap-1.5 opacity-90 group-hover:opacity-100 transition-opacity\"\n aria-label={`Add ${title} to cart`}\n onClick={() => {\n if (onAddToCart) onAddToCart(product);\n }}\n >\n <ShoppingCart className=\"h-3.5 w-3.5\" aria-hidden />\n Add\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n </motion.div>\n );\n}\n"
|
|
15610
15674
|
},
|
|
15611
15675
|
{
|
|
15612
15676
|
"path": "components/blocks/use-animation-hooks.ts",
|
|
15613
15677
|
"type": "registry:file",
|
|
15614
15678
|
"content": "import { useEffect, useRef, useState } from 'react';\nimport type { RefObject } from 'react';\n\nexport const usePrefersReducedMotion = (): boolean => {\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);\n\n useEffect(() => {\n const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n setPrefersReducedMotion(mediaQuery.matches);\n\n const listener = (event: MediaQueryListEvent) => {\n setPrefersReducedMotion(event.matches);\n };\n\n mediaQuery.addEventListener('change', listener);\n return () => {\n mediaQuery.removeEventListener('change', listener);\n };\n }, []);\n\n return prefersReducedMotion;\n};\n\nexport const useAnimateOnScroll = (options?: IntersectionObserverInit) => {\n const [ref, setRef] = useState<HTMLElement | null>(null);\n const [isIntersecting, setIsIntersecting] = useState(false);\n\n useEffect(() => {\n if (!ref) return;\n\n const observer = new IntersectionObserver(([entry]) => {\n if (entry) {\n setIsIntersecting(entry.isIntersecting);\n }\n }, options);\n\n observer.observe(ref);\n return () => {\n observer.unobserve(ref);\n };\n }, [ref, options]);\n\n return [setRef, isIntersecting] as const;\n};\n\nexport function useInView<T extends Element = HTMLDivElement>(\n options: IntersectionObserverInit = { rootMargin: '100px 0px', threshold: 0 },\n): [RefObject<T | null>, boolean] {\n const ref = useRef<T | null>(null);\n const [inView, setInView] = useState(false);\n useEffect(() => {\n const el = ref.current;\n if (!el || typeof window === 'undefined' || !('IntersectionObserver' in window)) {\n setInView(true);\n return;\n }\n const io = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting), options);\n io.observe(el);\n return () => io.disconnect();\n }, [options]);\n return [ref, inView];\n}\n\nexport function useShouldAnimate<T extends Element = HTMLDivElement>(): [RefObject<T | null>, boolean] {\n const reduced = usePrefersReducedMotion();\n const [ref, inView] = useInView<T>();\n return [ref, inView && !reduced];\n}\n"
|
|
15679
|
+
},
|
|
15680
|
+
{
|
|
15681
|
+
"path": "components/blocks/card.tsx",
|
|
15682
|
+
"type": "registry:block",
|
|
15683
|
+
"content": "import React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nconst Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n const isInteractive =\n typeof props.onClick === 'function' || props.tabIndex !== undefined || props.role === 'button';\n return (\n <div\n ref={ref}\n className={cn(\n 'rounded-lg border border-border bg-card text-card-foreground shadow-sm transition-all duration-150 ease-out',\n isInteractive &&\n 'cursor-pointer hover:shadow-md hover:border-border/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:scale-[0.99] motion-reduce:active:scale-100 motion-reduce:transition-none',\n className\n )}\n {...props}\n />\n );\n }\n);\nCard.displayName = 'Card';\n\nconst CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex flex-col space-y-1.5 p-6', className)}\n {...props}\n />\n )\n);\nCardHeader.displayName = 'CardHeader';\n\nconst CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(\n ({ className, children, ...props }, ref) =>\n children == null || children === '' ? null : (\n <h3\n ref={ref}\n className={cn('text-2xl font-semibold leading-none tracking-tight', className)}\n {...props}\n >\n {children}\n </h3>\n )\n);\nCardTitle.displayName = 'CardTitle';\n\nconst CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(\n ({ className, ...props }, ref) => (\n <p\n ref={ref}\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n )\n);\nCardDescription.displayName = 'CardDescription';\n\nconst CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />\n )\n);\nCardContent.displayName = 'CardContent';\n\nconst CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center p-6 pt-0 border-t border-border mt-6', className)}\n {...props}\n />\n )\n);\nCardFooter.displayName = 'CardFooter';\n\nexport { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };\n"
|
|
15684
|
+
},
|
|
15685
|
+
{
|
|
15686
|
+
"path": "components/blocks/button.tsx",
|
|
15687
|
+
"type": "registry:block",
|
|
15688
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from \"@/lib/utils\";\nimport { Spinner } from './spinner';\n\nconst buttonVariants = cva(\n 'relative inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-all duration-150 ease-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:scale-[0.98] motion-reduce:active:scale-100 motion-reduce:transition-none disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:shrink-0',\n {\n variants: {\n variant: {\n default: 'bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 shadow-sm hover:shadow',\n destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80 shadow-sm hover:shadow',\n outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground hover:border-accent active:bg-accent/80',\n secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70',\n ghost: 'hover:bg-accent hover:text-accent-foreground active:bg-accent/80',\n link: 'text-primary underline-offset-4 hover:underline active:text-primary/80',\n },\n size: {\n default: 'h-10 px-4 py-2',\n sm: 'h-9 rounded-md px-3',\n lg: 'h-11 rounded-md px-8',\n icon: 'h-10 w-10',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n }\n);\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n loading?: boolean;\n loadingLabel?: string;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n variant,\n size,\n asChild: _asChild = false,\n loading = false,\n loadingLabel,\n disabled,\n children,\n ...props\n },\n ref\n ) => {\n const isDisabled = disabled || loading;\n return (\n <button\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n disabled={isDisabled}\n aria-busy={loading || undefined}\n data-loading={loading || undefined}\n {...props}\n >\n {loading && (\n <Spinner\n size={size === 'lg' ? 'default' : 'sm'}\n label={loadingLabel ?? 'Loading'}\n aria-hidden={!loadingLabel || undefined}\n />\n )}\n {loading && loadingLabel ? <span>{loadingLabel}</span> : children}\n </button>\n );\n }\n);\nButton.displayName = 'Button';\n\nexport { Button, buttonVariants };\n"
|
|
15689
|
+
},
|
|
15690
|
+
{
|
|
15691
|
+
"path": "components/blocks/spinner.tsx",
|
|
15692
|
+
"type": "registry:block",
|
|
15693
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from \"@/lib/utils\";\n\nconst spinnerVariants = cva(\n 'inline-block animate-spin rounded-full border-current border-solid border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]',\n {\n variants: {\n size: {\n xs: 'h-3 w-3 border',\n sm: 'h-4 w-4 border-2',\n default: 'h-5 w-5 border-2',\n lg: 'h-8 w-8 border-[3px]',\n xl: 'h-12 w-12 border-4',\n },\n },\n defaultVariants: {\n size: 'default',\n },\n }\n);\n\nexport interface SpinnerProps\n extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'role'>,\n VariantProps<typeof spinnerVariants> {\n label?: string;\n}\n\nconst Spinner = React.forwardRef<HTMLSpanElement, SpinnerProps>(\n ({ className, size, label = 'Loading', ...props }, ref) => {\n return (\n <span\n ref={ref}\n role=\"status\"\n aria-live=\"polite\"\n className={cn(spinnerVariants({ size }), className)}\n {...props}\n >\n <span className=\"sr-only\">{label}</span>\n </span>\n );\n }\n);\nSpinner.displayName = 'Spinner';\n\nexport { Spinner, spinnerVariants };\n"
|
|
15615
15694
|
}
|
|
15616
15695
|
]
|
|
15617
15696
|
},
|
|
@@ -15702,12 +15781,27 @@
|
|
|
15702
15781
|
{
|
|
15703
15782
|
"path": "components/blocks/product-grid.tsx",
|
|
15704
15783
|
"type": "registry:block",
|
|
15705
|
-
"content": "\"use client\";\n\nimport React, { useState, useMemo } from 'react';\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { ProductCard, type Product } from './product-card';\nimport { SectionHeader } from '
|
|
15784
|
+
"content": "\"use client\";\n\nimport React, { useState, useMemo } from 'react';\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { ProductCard, type Product } from './product-card';\nimport { SectionHeader } from './section-header';\nimport { Container } from './container';\nimport { Input } from './input';\nimport { usePrefersReducedMotion } from \"./use-animation-hooks\";\nimport { Search, SlidersHorizontal } from 'lucide-react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface ProductGridProps {\n tag?: string;\n title?: string;\n description?: string;\n products?: Product[];\n onAddToCart?: (product: Product) => void;\n onFavoriteClick?: (product: Product) => void;\n}\n\nconst defaultProducts: Product[] = [\n { id: '1', title: 'Lerpa UI Pro Bundle', price: 99.0, category: 'Templates', rating: 4.9, reviewsCount: 154, badge: 'Best Seller', description: 'Complete full-stack template pack including databases, telemetry dashboards, and multi-region SSO setups.' },\n { id: '2', title: 'Neo-Console Keyboard', price: 149.0, category: 'Hardware', rating: 4.7, reviewsCount: 68, description: 'Haptic mechanical custom layout keyboards designed explicitly for low-latency terminal speed operations.' },\n { id: '3', title: 'Quantum Telemetry Client', price: 49.0, category: 'Software', rating: 4.5, reviewsCount: 32, badge: 'New', description: 'Advanced local-first client telemetry reporting system offering zero-latency stream data pipelines.' },\n { id: '4', title: 'Developer Coffee Mug v2', price: 24.0, category: 'Merchandise', rating: 4.8, reviewsCount: 112, description: 'Double-walled thermal steel workspace mugs ensuring warm drinks during late compile operations.' },\n { id: '5', title: 'Edge Compiler License', price: 199.0, category: 'Software', rating: 5.0, reviewsCount: 45, description: 'Unlimited seat license for high-performance compiler chains targeting local edge clusters.' },\n { id: '6', title: 'Visual Grid Designer', price: 79.0, category: 'Templates', rating: 4.3, reviewsCount: 18, description: 'Drag-and-drop grid dashboard visualizer exporting clean Tailwind code blocks programmatically.' },\n];\n\nexport function ProductGrid({\n tag = 'Our Marketplace Store',\n title = 'Engineered Developer Gear',\n description = 'Enhance your workplace stack with our collection of high-performance gear, licenses, and full-stack software templates.',\n products = defaultProducts,\n onAddToCart,\n onFavoriteClick,\n}: ProductGridProps) {\n const reduced = usePrefersReducedMotion();\n const [searchTerm, setSearchTerm] = useState('');\n const [selectedCategory, setSelectedCategory] = useState('All');\n\n const categories = useMemo(() => {\n const list = new Set(products.map((p) => p.category).filter(Boolean));\n return ['All', ...Array.from(list)];\n }, [products]);\n\n const filteredProducts = useMemo(() => {\n return products.filter((p) => {\n const matchSearch = p.title.toLowerCase().includes(searchTerm.toLowerCase()) || \n p.description?.toLowerCase().includes(searchTerm.toLowerCase());\n const matchCategory = selectedCategory === 'All' || p.category === selectedCategory;\n return matchSearch && matchCategory;\n });\n }, [products, searchTerm, selectedCategory]);\n\n return (\n <section className=\"py-20 bg-background\">\n <Container>\n <SectionHeader tag={tag} title={title} description={description} align=\"left\" />\n\n {/* Filter Controls Bar */}\n <div className=\"flex flex-col sm:flex-row items-center gap-4 justify-between border border-border bg-card rounded-lg p-4 mb-8\">\n <div className=\"relative w-full sm:max-w-xs\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground\" />\n <Input\n type=\"text\"\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n placeholder=\"Search gear...\"\n className=\"pl-9\"\n />\n </div>\n\n <div className=\"flex items-center gap-2 overflow-x-auto w-full sm:w-auto pb-1 sm:pb-0 scrollbar-none\">\n <SlidersHorizontal className=\"h-4.5 w-4.5 text-muted-foreground shrink-0 hidden md:block\" />\n {categories.map((cat) => (\n <button\n key={cat}\n onClick={() => setSelectedCategory(cat || 'All')}\n className={cn(\n 'px-3 py-1.5 rounded-full text-xs font-semibold whitespace-nowrap transition-all border border-border',\n selectedCategory === cat\n ? 'bg-primary text-primary-foreground border-transparent'\n : 'bg-background hover:bg-muted text-muted-foreground hover:text-foreground'\n )}\n >\n {cat}\n </button>\n ))}\n </div>\n </div>\n\n {/* Catalog Grid */}\n {filteredProducts.length > 0 ? (\n <motion.div\n layout\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6\"\n >\n <AnimatePresence mode=\"popLayout\">\n {filteredProducts.map((product) => (\n <motion.div\n key={product.id}\n layout\n initial={reduced ? false : { opacity: 0, scale: 0.9 }}\n animate={{ opacity: 1, scale: 1 }}\n exit={reduced ? { opacity: 0 } : { opacity: 0, scale: 0.9 }}\n transition={{ duration: reduced ? 0 : 0.3 }}\n >\n <ProductCard\n product={product}\n noMotion\n onAddToCart={onAddToCart}\n onFavoriteClick={onFavoriteClick}\n />\n </motion.div>\n ))}\n </AnimatePresence>\n </motion.div>\n ) : (\n <div className=\"flex flex-col items-center justify-center p-12 border border-dashed border-border rounded-xl text-center\">\n <p className=\"text-sm text-muted-foreground mb-1\">No products found matching filters.</p>\n <button\n onClick={() => {\n setSearchTerm('');\n setSelectedCategory('All');\n }}\n className=\"text-xs font-bold text-primary underline\"\n >\n Reset Search Filter\n </button>\n </div>\n )}\n </Container>\n </section>\n );\n}\n"
|
|
15706
15785
|
},
|
|
15707
15786
|
{
|
|
15708
15787
|
"path": "components/blocks/use-animation-hooks.ts",
|
|
15709
15788
|
"type": "registry:file",
|
|
15710
15789
|
"content": "import { useEffect, useRef, useState } from 'react';\nimport type { RefObject } from 'react';\n\nexport const usePrefersReducedMotion = (): boolean => {\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);\n\n useEffect(() => {\n const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n setPrefersReducedMotion(mediaQuery.matches);\n\n const listener = (event: MediaQueryListEvent) => {\n setPrefersReducedMotion(event.matches);\n };\n\n mediaQuery.addEventListener('change', listener);\n return () => {\n mediaQuery.removeEventListener('change', listener);\n };\n }, []);\n\n return prefersReducedMotion;\n};\n\nexport const useAnimateOnScroll = (options?: IntersectionObserverInit) => {\n const [ref, setRef] = useState<HTMLElement | null>(null);\n const [isIntersecting, setIsIntersecting] = useState(false);\n\n useEffect(() => {\n if (!ref) return;\n\n const observer = new IntersectionObserver(([entry]) => {\n if (entry) {\n setIsIntersecting(entry.isIntersecting);\n }\n }, options);\n\n observer.observe(ref);\n return () => {\n observer.unobserve(ref);\n };\n }, [ref, options]);\n\n return [setRef, isIntersecting] as const;\n};\n\nexport function useInView<T extends Element = HTMLDivElement>(\n options: IntersectionObserverInit = { rootMargin: '100px 0px', threshold: 0 },\n): [RefObject<T | null>, boolean] {\n const ref = useRef<T | null>(null);\n const [inView, setInView] = useState(false);\n useEffect(() => {\n const el = ref.current;\n if (!el || typeof window === 'undefined' || !('IntersectionObserver' in window)) {\n setInView(true);\n return;\n }\n const io = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting), options);\n io.observe(el);\n return () => io.disconnect();\n }, [options]);\n return [ref, inView];\n}\n\nexport function useShouldAnimate<T extends Element = HTMLDivElement>(): [RefObject<T | null>, boolean] {\n const reduced = usePrefersReducedMotion();\n const [ref, inView] = useInView<T>();\n return [ref, inView && !reduced];\n}\n"
|
|
15790
|
+
},
|
|
15791
|
+
{
|
|
15792
|
+
"path": "components/blocks/section-header.tsx",
|
|
15793
|
+
"type": "registry:block",
|
|
15794
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface SectionHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n tag?: string;\n title: string;\n description?: string;\n align?: 'left' | 'center' | 'right';\n actions?: React.ReactNode;\n}\n\nconst SectionHeader = React.forwardRef<HTMLDivElement, SectionHeaderProps>(\n ({ className, tag, title, description, align = 'left', actions, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn(\n 'flex flex-col md:flex-row md:items-end justify-between gap-6 mb-8 md:mb-12',\n align === 'center' && 'text-center md:flex-col md:items-center',\n align === 'right' && 'text-right md:flex-row-reverse',\n className\n )}\n {...props}\n >\n <div className={cn('flex-1 max-w-3xl', align === 'center' && 'mx-auto')}>\n {tag && (\n <span className=\"inline-block text-sm font-semibold tracking-wider uppercase text-primary mb-2\">\n {tag}\n </span>\n )}\n {title && (\n <h2 className=\"text-3xl font-bold tracking-tight sm:text-4xl text-foreground\">\n {title}\n </h2>\n )}\n {description && (\n <p className=\"mt-4 text-lg text-muted-foreground leading-relaxed\">\n {description}\n </p>\n )}\n </div>\n {actions && (\n <div\n className={cn(\n 'flex flex-wrap gap-3 shrink-0',\n align === 'center' && 'justify-center'\n )}\n >\n {actions}\n </div>\n )}\n </div>\n );\n }\n);\nSectionHeader.displayName = 'SectionHeader';\n\nexport { SectionHeader };\n"
|
|
15795
|
+
},
|
|
15796
|
+
{
|
|
15797
|
+
"path": "components/blocks/container.tsx",
|
|
15798
|
+
"type": "registry:block",
|
|
15799
|
+
"content": "import React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> {\n as?: React.ElementType;\n clean?: boolean;\n}\n\nconst Container = React.forwardRef<HTMLDivElement, ContainerProps>(\n ({ className, as: Component = 'div', clean = false, ...props }, ref) => {\n return (\n <Component\n ref={ref}\n className={cn(\n !clean && 'mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8',\n className\n )}\n {...props}\n />\n );\n }\n);\nContainer.displayName = 'Container';\n\nexport { Container };\n"
|
|
15800
|
+
},
|
|
15801
|
+
{
|
|
15802
|
+
"path": "components/blocks/input.tsx",
|
|
15803
|
+
"type": "registry:block",
|
|
15804
|
+
"content": "import React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {\n error?: boolean | string;\n}\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n ({ className, type = 'text', error, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, id, ...props }, ref) => {\n const hasError = Boolean(error) || ariaInvalid === true || ariaInvalid === 'true';\n const errorId = typeof error === 'string' && id ? `${id}-error` : undefined;\n return (\n <>\n <input\n id={id}\n type={type}\n aria-invalid={hasError || undefined}\n aria-describedby={cn(ariaDescribedBy, errorId) || undefined}\n data-state={hasError ? 'error' : undefined}\n className={cn(\n 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background transition-colors duration-150 ease-out file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground hover:border-input/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 disabled:pointer-events-none',\n hasError &&\n 'border-destructive text-destructive placeholder:text-destructive/60 focus-visible:ring-destructive',\n className\n )}\n ref={ref}\n {...props}\n />\n {typeof error === 'string' && error.length > 0 && (\n <p id={errorId} className=\"mt-1 text-xs text-destructive\" role=\"alert\">\n {error}\n </p>\n )}\n </>\n );\n }\n);\nInput.displayName = 'Input';\n\nexport { Input };\n"
|
|
15711
15805
|
}
|
|
15712
15806
|
],
|
|
15713
15807
|
"registryDependencies": [
|
|
@@ -16130,13 +16224,24 @@
|
|
|
16130
16224
|
"name": "prompt-input",
|
|
16131
16225
|
"type": "registry:block",
|
|
16132
16226
|
"dependencies": [
|
|
16227
|
+
"class-variance-authority",
|
|
16133
16228
|
"lucide-react"
|
|
16134
16229
|
],
|
|
16135
16230
|
"files": [
|
|
16136
16231
|
{
|
|
16137
16232
|
"path": "components/blocks/prompt-input.tsx",
|
|
16138
16233
|
"type": "registry:block",
|
|
16139
|
-
"content": "import React, { useState, useEffect, useRef } from 'react';\nimport { Button } from '
|
|
16234
|
+
"content": "import React, { useState, useEffect, useRef } from 'react';\nimport { Button } from './button';\nimport { ArrowUp, Sparkles, Paperclip } from 'lucide-react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface PromptInputProps {\n onSubmit?: (prompt: string) => void;\n placeholders?: string[];\n maxLength?: number;\n className?: string;\n}\n\nconst defaultPlaceholders = [\n 'Generate an interactive pricing section in React...',\n 'Build a dashboard layout with real-time Area charts...',\n 'Create a high-fidelity SaaS landing hero template...',\n 'Formulate accessibilities checklist for custom modals...',\n];\n\nexport function PromptInput({\n onSubmit,\n placeholders = defaultPlaceholders,\n maxLength = 1000,\n className,\n}: PromptInputProps) {\n const [value, setValue] = useState('');\n const [placeholderIdx, setPlaceholderIdx] = useState(0);\n const textareaRef = useRef<HTMLTextAreaElement | null>(null);\n\n // Rotate placeholders every 4 seconds\n useEffect(() => {\n const timer = setInterval(() => {\n setPlaceholderIdx((prev) => (prev + 1) % placeholders.length);\n }, 4000);\n return () => clearInterval(timer);\n }, [placeholders]);\n\n // Auto-resize height\n useEffect(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n textarea.style.height = 'auto';\n textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`;\n }, [value]);\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n };\n\n const handleSubmit = () => {\n if (!value.trim()) return;\n if (onSubmit) onSubmit(value);\n setValue('');\n };\n\n return (\n <div className={cn('relative w-full max-w-2xl mx-auto rounded-xl border border-border bg-card shadow-lg p-3 focus-within:ring-2 focus-within:ring-primary focus-within:border-transparent transition-all', className)}>\n <div className=\"flex items-start gap-3\">\n <div className=\"mt-2 text-primary/80 shrink-0\">\n <Sparkles className=\"h-4.5 w-4.5 animate-pulse\" />\n </div>\n <textarea\n ref={textareaRef}\n value={value}\n onChange={(e) => setValue(e.target.value.slice(0, maxLength))}\n onKeyDown={handleKeyDown}\n placeholder={placeholders[placeholderIdx]}\n rows={1}\n className=\"flex-1 w-full bg-transparent resize-none border-0 p-1 text-sm outline-none placeholder:text-muted-foreground focus:ring-0 leading-relaxed min-h-[36px] max-h-[200px]\"\n />\n </div>\n\n <div className=\"flex items-center justify-between mt-3 pt-2 border-t border-border/60\">\n <div className=\"flex items-center gap-1.5\">\n <Button variant=\"ghost\" size=\"icon\" aria-label=\"Attach file\" className=\"h-8 w-8 text-muted-foreground hover:text-foreground\">\n <Paperclip className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n <span className=\"text-[10px] text-muted-foreground/60 font-semibold uppercase\">\n Context Ready\n </span>\n </div>\n\n <div className=\"flex items-center gap-3\">\n <span className={cn('text-[11px]', value.length > maxLength * 0.9 ? 'text-destructive font-bold' : 'text-muted-foreground/80')}>\n {value.length} / {maxLength}\n </span>\n <Button\n onClick={handleSubmit}\n size=\"icon\"\n aria-label=\"Send prompt\"\n className=\"h-8 w-8 shrink-0 rounded-lg shadow-sm\"\n disabled={!value.trim()}\n >\n <ArrowUp className=\"h-4 w-4\" aria-hidden=\"true\" />\n </Button>\n </div>\n </div>\n </div>\n );\n}\n"
|
|
16235
|
+
},
|
|
16236
|
+
{
|
|
16237
|
+
"path": "components/blocks/button.tsx",
|
|
16238
|
+
"type": "registry:block",
|
|
16239
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from \"@/lib/utils\";\nimport { Spinner } from './spinner';\n\nconst buttonVariants = cva(\n 'relative inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-all duration-150 ease-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:scale-[0.98] motion-reduce:active:scale-100 motion-reduce:transition-none disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:shrink-0',\n {\n variants: {\n variant: {\n default: 'bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 shadow-sm hover:shadow',\n destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80 shadow-sm hover:shadow',\n outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground hover:border-accent active:bg-accent/80',\n secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70',\n ghost: 'hover:bg-accent hover:text-accent-foreground active:bg-accent/80',\n link: 'text-primary underline-offset-4 hover:underline active:text-primary/80',\n },\n size: {\n default: 'h-10 px-4 py-2',\n sm: 'h-9 rounded-md px-3',\n lg: 'h-11 rounded-md px-8',\n icon: 'h-10 w-10',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n }\n);\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n loading?: boolean;\n loadingLabel?: string;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n variant,\n size,\n asChild: _asChild = false,\n loading = false,\n loadingLabel,\n disabled,\n children,\n ...props\n },\n ref\n ) => {\n const isDisabled = disabled || loading;\n return (\n <button\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n disabled={isDisabled}\n aria-busy={loading || undefined}\n data-loading={loading || undefined}\n {...props}\n >\n {loading && (\n <Spinner\n size={size === 'lg' ? 'default' : 'sm'}\n label={loadingLabel ?? 'Loading'}\n aria-hidden={!loadingLabel || undefined}\n />\n )}\n {loading && loadingLabel ? <span>{loadingLabel}</span> : children}\n </button>\n );\n }\n);\nButton.displayName = 'Button';\n\nexport { Button, buttonVariants };\n"
|
|
16240
|
+
},
|
|
16241
|
+
{
|
|
16242
|
+
"path": "components/blocks/spinner.tsx",
|
|
16243
|
+
"type": "registry:block",
|
|
16244
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from \"@/lib/utils\";\n\nconst spinnerVariants = cva(\n 'inline-block animate-spin rounded-full border-current border-solid border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]',\n {\n variants: {\n size: {\n xs: 'h-3 w-3 border',\n sm: 'h-4 w-4 border-2',\n default: 'h-5 w-5 border-2',\n lg: 'h-8 w-8 border-[3px]',\n xl: 'h-12 w-12 border-4',\n },\n },\n defaultVariants: {\n size: 'default',\n },\n }\n);\n\nexport interface SpinnerProps\n extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'role'>,\n VariantProps<typeof spinnerVariants> {\n label?: string;\n}\n\nconst Spinner = React.forwardRef<HTMLSpanElement, SpinnerProps>(\n ({ className, size, label = 'Loading', ...props }, ref) => {\n return (\n <span\n ref={ref}\n role=\"status\"\n aria-live=\"polite\"\n className={cn(spinnerVariants({ size }), className)}\n {...props}\n >\n <span className=\"sr-only\">{label}</span>\n </span>\n );\n }\n);\nSpinner.displayName = 'Spinner';\n\nexport { Spinner, spinnerVariants };\n"
|
|
16140
16245
|
}
|
|
16141
16246
|
]
|
|
16142
16247
|
},
|
|
@@ -16853,6 +16958,7 @@
|
|
|
16853
16958
|
"name": "saa-s-hero",
|
|
16854
16959
|
"type": "registry:block",
|
|
16855
16960
|
"dependencies": [
|
|
16961
|
+
"class-variance-authority",
|
|
16856
16962
|
"framer-motion",
|
|
16857
16963
|
"lucide-react"
|
|
16858
16964
|
],
|
|
@@ -16860,12 +16966,27 @@
|
|
|
16860
16966
|
{
|
|
16861
16967
|
"path": "components/blocks/saa-s-hero.tsx",
|
|
16862
16968
|
"type": "registry:block",
|
|
16863
|
-
"content": "\"use client\";\n\nimport React from 'react';\nimport { motion } from \"framer-motion\";\nimport { Button } from '
|
|
16969
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { motion } from \"framer-motion\";\nimport { Button } from './button';\nimport { Container } from './container';\nimport { usePrefersReducedMotion } from \"./use-animation-hooks\";\nimport { ChevronRight, Play } from 'lucide-react';\n\nexport interface SaaSHeroProps {\n tag?: string;\n title?: string;\n description?: string;\n primaryAction?: { label: string; onClick?: () => void };\n secondaryAction?: { label: string; onClick?: () => void };\n screenshotUrl?: string;\n}\n\nexport function SaaSHero({\n tag = 'Introducing Lerpa UI v1.0',\n title = 'Build beautiful interfaces in seconds, not hours.',\n description = 'A collection of production-ready, fully interactive Tailwind components and blocks designed to accelerate your development workflow.',\n primaryAction = { label: 'Get Started for Free' },\n secondaryAction = { label: 'Watch Video Demo' },\n}: SaaSHeroProps) {\n const reduced = usePrefersReducedMotion();\n return (\n <section className=\"relative overflow-hidden py-20 sm:py-32 bg-background\">\n <Container className=\"relative\">\n <div className=\"flex flex-col items-center text-center max-w-4xl mx-auto\">\n {/* Tag */}\n <motion.div\n initial={reduced ? false : { opacity: 0, y: -10 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.5 }}\n className=\"inline-flex items-center gap-1.5 rounded-full px-3.5 py-1.5 text-xs font-semibold bg-primary/10 text-primary border border-primary/20 backdrop-blur-sm mb-6 cursor-pointer hover:bg-primary/15 transition-colors\"\n >\n {tag} <ChevronRight className=\"h-3 w-3\" />\n </motion.div>\n\n {/* Heading */}\n <motion.h1\n initial={reduced ? false : { opacity: 0, y: 20 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.6, delay: reduced ? 0 : 0.1 }}\n className=\"text-4xl sm:text-6xl font-extrabold tracking-tight text-foreground leading-[1.1] mb-6\"\n >\n {title}\n </motion.h1>\n\n {/* Description */}\n <motion.p\n initial={reduced ? false : { opacity: 0, y: 20 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.6, delay: reduced ? 0 : 0.2 }}\n className=\"text-lg sm:text-xl text-muted-foreground leading-relaxed max-w-2xl mb-10\"\n >\n {description}\n </motion.p>\n\n {/* Call to Actions */}\n <motion.div\n initial={reduced ? false : { opacity: 0, y: 20 }}\n animate={{ opacity: 1, y: 0 }}\n transition={{ duration: 0.6, delay: reduced ? 0 : 0.3 }}\n className=\"flex flex-col sm:flex-row items-center justify-center gap-4 w-full sm:w-auto mb-16\"\n >\n <Button size=\"lg\" className=\"w-full sm:w-auto font-semibold gap-2 shadow-lg shadow-primary/20\" onClick={primaryAction.onClick}>\n {primaryAction.label}\n </Button>\n <Button variant=\"outline\" size=\"lg\" className=\"w-full sm:w-auto font-semibold gap-2\" onClick={secondaryAction.onClick}>\n <Play className=\"h-4 w-4 fill-current text-muted-foreground\" /> {secondaryAction.label}\n </Button>\n </motion.div>\n\n {/* Animated Product Screenshot / Demo Container */}\n <motion.div\n initial={reduced ? false : { opacity: 0, y: 40, scale: 0.95 }}\n animate={{ opacity: 1, y: 0, scale: 1 }}\n transition={reduced ? { duration: 0 } : { duration: 0.8, delay: 0.4, type: 'spring', stiffness: 100, damping: 20 }}\n className=\"w-full max-w-5xl rounded-xl border border-border bg-card shadow-2xl overflow-hidden aspect-[16/9] relative\"\n >\n <div className=\"absolute inset-0 bg-gradient-to-tr from-primary/5 via-transparent to-primary/5 pointer-events-none\" />\n \n {/* Mock browser chrome */}\n <div className=\"flex items-center gap-2 px-4 py-3 border-b border-border bg-muted/40\">\n <div className=\"flex gap-1.5\">\n <div className=\"h-3 w-3 rounded-full bg-rose-500/80\" />\n <div className=\"h-3 w-3 rounded-full bg-amber-500/80\" />\n <div className=\"h-3 w-3 rounded-full bg-emerald-500/80\" />\n </div>\n <div className=\"flex-1 mx-4 max-w-md h-6 rounded-md bg-muted/80 border border-border flex items-center justify-center text-[10px] text-muted-foreground font-mono\">\n demo.lerpaui.com/dashboard\n </div>\n </div>\n\n {/* Dashboard Mock Content */}\n <div className=\"p-6 grid grid-cols-12 gap-4 h-full text-left bg-card\">\n <div className=\"col-span-3 border-r border-border pr-4 space-y-4\">\n <div className=\"h-8 rounded-md bg-muted/60 animate-pulse w-3/4\" />\n <div className=\"space-y-2\">\n <div className=\"h-6 rounded-md bg-muted/40 w-full\" />\n <div className=\"h-6 rounded-md bg-muted/40 w-5/6\" />\n <div className=\"h-6 rounded-md bg-muted/40 w-4/5\" />\n </div>\n </div>\n <div className=\"col-span-9 space-y-6\">\n <div className=\"grid grid-cols-3 gap-4\">\n <div className=\"h-24 rounded-lg bg-muted/50 p-4 flex flex-col justify-between\">\n <div className=\"h-4 bg-muted/60 rounded w-1/2\" />\n <div className=\"h-8 bg-muted/80 rounded w-3/4 animate-pulse\" />\n </div>\n <div className=\"h-24 rounded-lg bg-muted/50 p-4 flex flex-col justify-between\">\n <div className=\"h-4 bg-muted/60 rounded w-1/2\" />\n <div className=\"h-8 bg-muted/80 rounded w-2/3 animate-pulse\" />\n </div>\n <div className=\"h-24 rounded-lg bg-muted/50 p-4 flex flex-col justify-between\">\n <div className=\"h-4 bg-muted/60 rounded w-1/3\" />\n <div className=\"h-8 bg-muted/80 rounded w-1/2 animate-pulse\" />\n </div>\n </div>\n <div className=\"h-48 rounded-lg bg-muted/40 flex items-center justify-center text-muted-foreground border border-dashed border-border\">\n Interactive Dashboard Overview\n </div>\n </div>\n </div>\n </motion.div>\n </div>\n </Container>\n </section>\n );\n}\n"
|
|
16864
16970
|
},
|
|
16865
16971
|
{
|
|
16866
16972
|
"path": "components/blocks/use-animation-hooks.ts",
|
|
16867
16973
|
"type": "registry:file",
|
|
16868
16974
|
"content": "import { useEffect, useRef, useState } from 'react';\nimport type { RefObject } from 'react';\n\nexport const usePrefersReducedMotion = (): boolean => {\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);\n\n useEffect(() => {\n const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n setPrefersReducedMotion(mediaQuery.matches);\n\n const listener = (event: MediaQueryListEvent) => {\n setPrefersReducedMotion(event.matches);\n };\n\n mediaQuery.addEventListener('change', listener);\n return () => {\n mediaQuery.removeEventListener('change', listener);\n };\n }, []);\n\n return prefersReducedMotion;\n};\n\nexport const useAnimateOnScroll = (options?: IntersectionObserverInit) => {\n const [ref, setRef] = useState<HTMLElement | null>(null);\n const [isIntersecting, setIsIntersecting] = useState(false);\n\n useEffect(() => {\n if (!ref) return;\n\n const observer = new IntersectionObserver(([entry]) => {\n if (entry) {\n setIsIntersecting(entry.isIntersecting);\n }\n }, options);\n\n observer.observe(ref);\n return () => {\n observer.unobserve(ref);\n };\n }, [ref, options]);\n\n return [setRef, isIntersecting] as const;\n};\n\nexport function useInView<T extends Element = HTMLDivElement>(\n options: IntersectionObserverInit = { rootMargin: '100px 0px', threshold: 0 },\n): [RefObject<T | null>, boolean] {\n const ref = useRef<T | null>(null);\n const [inView, setInView] = useState(false);\n useEffect(() => {\n const el = ref.current;\n if (!el || typeof window === 'undefined' || !('IntersectionObserver' in window)) {\n setInView(true);\n return;\n }\n const io = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting), options);\n io.observe(el);\n return () => io.disconnect();\n }, [options]);\n return [ref, inView];\n}\n\nexport function useShouldAnimate<T extends Element = HTMLDivElement>(): [RefObject<T | null>, boolean] {\n const reduced = usePrefersReducedMotion();\n const [ref, inView] = useInView<T>();\n return [ref, inView && !reduced];\n}\n"
|
|
16975
|
+
},
|
|
16976
|
+
{
|
|
16977
|
+
"path": "components/blocks/button.tsx",
|
|
16978
|
+
"type": "registry:block",
|
|
16979
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from \"@/lib/utils\";\nimport { Spinner } from './spinner';\n\nconst buttonVariants = cva(\n 'relative inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-all duration-150 ease-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:scale-[0.98] motion-reduce:active:scale-100 motion-reduce:transition-none disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:shrink-0',\n {\n variants: {\n variant: {\n default: 'bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 shadow-sm hover:shadow',\n destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/80 shadow-sm hover:shadow',\n outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground hover:border-accent active:bg-accent/80',\n secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70',\n ghost: 'hover:bg-accent hover:text-accent-foreground active:bg-accent/80',\n link: 'text-primary underline-offset-4 hover:underline active:text-primary/80',\n },\n size: {\n default: 'h-10 px-4 py-2',\n sm: 'h-9 rounded-md px-3',\n lg: 'h-11 rounded-md px-8',\n icon: 'h-10 w-10',\n },\n },\n defaultVariants: {\n variant: 'default',\n size: 'default',\n },\n }\n);\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n loading?: boolean;\n loadingLabel?: string;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n variant,\n size,\n asChild: _asChild = false,\n loading = false,\n loadingLabel,\n disabled,\n children,\n ...props\n },\n ref\n ) => {\n const isDisabled = disabled || loading;\n return (\n <button\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n disabled={isDisabled}\n aria-busy={loading || undefined}\n data-loading={loading || undefined}\n {...props}\n >\n {loading && (\n <Spinner\n size={size === 'lg' ? 'default' : 'sm'}\n label={loadingLabel ?? 'Loading'}\n aria-hidden={!loadingLabel || undefined}\n />\n )}\n {loading && loadingLabel ? <span>{loadingLabel}</span> : children}\n </button>\n );\n }\n);\nButton.displayName = 'Button';\n\nexport { Button, buttonVariants };\n"
|
|
16980
|
+
},
|
|
16981
|
+
{
|
|
16982
|
+
"path": "components/blocks/container.tsx",
|
|
16983
|
+
"type": "registry:block",
|
|
16984
|
+
"content": "import React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> {\n as?: React.ElementType;\n clean?: boolean;\n}\n\nconst Container = React.forwardRef<HTMLDivElement, ContainerProps>(\n ({ className, as: Component = 'div', clean = false, ...props }, ref) => {\n return (\n <Component\n ref={ref}\n className={cn(\n !clean && 'mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8',\n className\n )}\n {...props}\n />\n );\n }\n);\nContainer.displayName = 'Container';\n\nexport { Container };\n"
|
|
16985
|
+
},
|
|
16986
|
+
{
|
|
16987
|
+
"path": "components/blocks/spinner.tsx",
|
|
16988
|
+
"type": "registry:block",
|
|
16989
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from \"@/lib/utils\";\n\nconst spinnerVariants = cva(\n 'inline-block animate-spin rounded-full border-current border-solid border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]',\n {\n variants: {\n size: {\n xs: 'h-3 w-3 border',\n sm: 'h-4 w-4 border-2',\n default: 'h-5 w-5 border-2',\n lg: 'h-8 w-8 border-[3px]',\n xl: 'h-12 w-12 border-4',\n },\n },\n defaultVariants: {\n size: 'default',\n },\n }\n);\n\nexport interface SpinnerProps\n extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'role'>,\n VariantProps<typeof spinnerVariants> {\n label?: string;\n}\n\nconst Spinner = React.forwardRef<HTMLSpanElement, SpinnerProps>(\n ({ className, size, label = 'Loading', ...props }, ref) => {\n return (\n <span\n ref={ref}\n role=\"status\"\n aria-live=\"polite\"\n className={cn(spinnerVariants({ size }), className)}\n {...props}\n >\n <span className=\"sr-only\">{label}</span>\n </span>\n );\n }\n);\nSpinner.displayName = 'Spinner';\n\nexport { Spinner, spinnerVariants };\n"
|
|
16869
16990
|
}
|
|
16870
16991
|
]
|
|
16871
16992
|
},
|
|
@@ -17300,12 +17421,27 @@
|
|
|
17300
17421
|
{
|
|
17301
17422
|
"path": "components/blocks/services-grid.tsx",
|
|
17302
17423
|
"type": "registry:block",
|
|
17303
|
-
"content": "\"use client\";\n\nimport React from 'react';\nimport { motion } from \"framer-motion\";\nimport { Card, CardContent } from '
|
|
17424
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { motion } from \"framer-motion\";\nimport { Card, CardContent } from './card';\nimport { SectionHeader } from './section-header';\nimport { Container } from './container';\nimport { usePrefersReducedMotion } from \"./use-animation-hooks\";\nimport { Palette, Code2, Globe, Cpu, ArrowUpRight, Megaphone, Settings } from 'lucide-react';\n\nexport interface ServiceItem {\n id: string;\n title: string;\n description: string;\n icon: React.ReactNode;\n href?: string;\n}\n\nexport interface ServicesGridProps {\n tag?: string;\n title?: string;\n description?: string;\n services?: ServiceItem[];\n}\n\nconst defaultServices: ServiceItem[] = [\n {\n id: '1',\n title: 'Brand Strategy & UI Design',\n description: 'Flawless visual architectures, custom branding kits, and user research designed to position your product as the absolute market leader.',\n icon: <Palette className=\"h-6 w-6 text-primary\" />,\n },\n {\n id: '2',\n title: 'Full-Stack Software Engineering',\n description: 'High-performance React/Next.js architectures, edge telemetry hubs, and secure DB endpoints constructed using strict clean coding conventions.',\n icon: <Code2 className=\"h-6 w-6 text-indigo-500\" />,\n },\n {\n id: '3',\n title: 'Global Performance Tuning',\n description: 'Detailed audit profiles checking bundle compilation sizes, edge routing paths, and database queries to target sub-100ms load responses.',\n icon: <Globe className=\"h-6 w-6 text-emerald-500\" />,\n },\n {\n id: '4',\n title: 'Automated AI Core Pipelines',\n description: 'Custom implementation connecting neural model inferences straight into your database records for predictive workflow automation.',\n icon: <Cpu className=\"h-6 w-6 text-violet-500\" />,\n },\n {\n id: '5',\n title: 'Growth Marketing & Copywriting',\n description: 'Data-driven search optimization, landing page experiments, and persuasive messaging designed to convert cold visitors into core customers.',\n icon: <Megaphone className=\"h-6 w-6 text-amber-500\" />,\n },\n {\n id: '6',\n title: 'Active Retainers & SLA Operations',\n description: 'Round-the-clock monitoring and preventative security patches ensuring 99.9% uptime compliance with corporate SLA guidelines.',\n icon: <Settings className=\"h-6 w-6 text-sky-500\" />,\n },\n];\n\nexport function ServicesGrid({\n tag = 'Premium Design Services',\n title = 'Our Creative Expertise',\n description = 'We combine strategic branding, deep full-stack engineering, and performance optimizations to deliver digital growth.',\n services = defaultServices,\n}: ServicesGridProps) {\n const reduced = usePrefersReducedMotion();\n return (\n <section className=\"py-20 bg-background/50 border-t border-border\">\n <Container>\n <SectionHeader tag={tag} title={title} description={description} align=\"center\" />\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl mx-auto\">\n {services.map((service, index) => (\n <motion.div\n key={service.id}\n initial={reduced ? false : { opacity: 0, y: 30 }}\n whileInView={{ opacity: 1, y: 0 }}\n viewport={{ once: true, margin: '-50px' }}\n transition={{ duration: 0.5, delay: reduced ? 0 : index * 0.08 }}\n className=\"flex\"\n >\n <Card className=\"group relative overflow-hidden w-full border border-border bg-card hover:bg-muted/10 transition-all duration-300 hover:shadow-md hover:-translate-y-1\">\n <CardContent className=\"p-8 flex flex-col justify-between h-full gap-6\">\n <div className=\"flex items-start justify-between\">\n <div className=\"p-3 bg-muted rounded-xl border border-border/80 text-foreground group-hover:scale-105 transition-transform\">\n {service.icon}\n </div>\n <button\n type=\"button\"\n aria-label={`Learn more about ${service.title}`}\n className=\"p-1.5 rounded-full bg-muted border border-border/80 opacity-0 group-hover:opacity-100 group-hover:scale-105 transition-all text-muted-foreground hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50\"\n >\n <ArrowUpRight className=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n </div>\n\n <div className=\"space-y-2\">\n <h3 className=\"text-xl font-bold text-foreground group-hover:text-primary transition-colors\">\n {service.title}\n </h3>\n <p className=\"text-sm text-muted-foreground leading-relaxed\">\n {service.description}\n </p>\n </div>\n </CardContent>\n </Card>\n </motion.div>\n ))}\n </div>\n </Container>\n </section>\n );\n}\n"
|
|
17304
17425
|
},
|
|
17305
17426
|
{
|
|
17306
17427
|
"path": "components/blocks/use-animation-hooks.ts",
|
|
17307
17428
|
"type": "registry:file",
|
|
17308
17429
|
"content": "import { useEffect, useRef, useState } from 'react';\nimport type { RefObject } from 'react';\n\nexport const usePrefersReducedMotion = (): boolean => {\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);\n\n useEffect(() => {\n const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\n setPrefersReducedMotion(mediaQuery.matches);\n\n const listener = (event: MediaQueryListEvent) => {\n setPrefersReducedMotion(event.matches);\n };\n\n mediaQuery.addEventListener('change', listener);\n return () => {\n mediaQuery.removeEventListener('change', listener);\n };\n }, []);\n\n return prefersReducedMotion;\n};\n\nexport const useAnimateOnScroll = (options?: IntersectionObserverInit) => {\n const [ref, setRef] = useState<HTMLElement | null>(null);\n const [isIntersecting, setIsIntersecting] = useState(false);\n\n useEffect(() => {\n if (!ref) return;\n\n const observer = new IntersectionObserver(([entry]) => {\n if (entry) {\n setIsIntersecting(entry.isIntersecting);\n }\n }, options);\n\n observer.observe(ref);\n return () => {\n observer.unobserve(ref);\n };\n }, [ref, options]);\n\n return [setRef, isIntersecting] as const;\n};\n\nexport function useInView<T extends Element = HTMLDivElement>(\n options: IntersectionObserverInit = { rootMargin: '100px 0px', threshold: 0 },\n): [RefObject<T | null>, boolean] {\n const ref = useRef<T | null>(null);\n const [inView, setInView] = useState(false);\n useEffect(() => {\n const el = ref.current;\n if (!el || typeof window === 'undefined' || !('IntersectionObserver' in window)) {\n setInView(true);\n return;\n }\n const io = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting), options);\n io.observe(el);\n return () => io.disconnect();\n }, [options]);\n return [ref, inView];\n}\n\nexport function useShouldAnimate<T extends Element = HTMLDivElement>(): [RefObject<T | null>, boolean] {\n const reduced = usePrefersReducedMotion();\n const [ref, inView] = useInView<T>();\n return [ref, inView && !reduced];\n}\n"
|
|
17430
|
+
},
|
|
17431
|
+
{
|
|
17432
|
+
"path": "components/blocks/card.tsx",
|
|
17433
|
+
"type": "registry:block",
|
|
17434
|
+
"content": "import React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nconst Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n const isInteractive =\n typeof props.onClick === 'function' || props.tabIndex !== undefined || props.role === 'button';\n return (\n <div\n ref={ref}\n className={cn(\n 'rounded-lg border border-border bg-card text-card-foreground shadow-sm transition-all duration-150 ease-out',\n isInteractive &&\n 'cursor-pointer hover:shadow-md hover:border-border/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 active:scale-[0.99] motion-reduce:active:scale-100 motion-reduce:transition-none',\n className\n )}\n {...props}\n />\n );\n }\n);\nCard.displayName = 'Card';\n\nconst CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex flex-col space-y-1.5 p-6', className)}\n {...props}\n />\n )\n);\nCardHeader.displayName = 'CardHeader';\n\nconst CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(\n ({ className, children, ...props }, ref) =>\n children == null || children === '' ? null : (\n <h3\n ref={ref}\n className={cn('text-2xl font-semibold leading-none tracking-tight', className)}\n {...props}\n >\n {children}\n </h3>\n )\n);\nCardTitle.displayName = 'CardTitle';\n\nconst CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(\n ({ className, ...props }, ref) => (\n <p\n ref={ref}\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n )\n);\nCardDescription.displayName = 'CardDescription';\n\nconst CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />\n )\n);\nCardContent.displayName = 'CardContent';\n\nconst CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn('flex items-center p-6 pt-0 border-t border-border mt-6', className)}\n {...props}\n />\n )\n);\nCardFooter.displayName = 'CardFooter';\n\nexport { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };\n"
|
|
17435
|
+
},
|
|
17436
|
+
{
|
|
17437
|
+
"path": "components/blocks/section-header.tsx",
|
|
17438
|
+
"type": "registry:block",
|
|
17439
|
+
"content": "\"use client\";\n\nimport React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface SectionHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n tag?: string;\n title: string;\n description?: string;\n align?: 'left' | 'center' | 'right';\n actions?: React.ReactNode;\n}\n\nconst SectionHeader = React.forwardRef<HTMLDivElement, SectionHeaderProps>(\n ({ className, tag, title, description, align = 'left', actions, ...props }, ref) => {\n return (\n <div\n ref={ref}\n className={cn(\n 'flex flex-col md:flex-row md:items-end justify-between gap-6 mb-8 md:mb-12',\n align === 'center' && 'text-center md:flex-col md:items-center',\n align === 'right' && 'text-right md:flex-row-reverse',\n className\n )}\n {...props}\n >\n <div className={cn('flex-1 max-w-3xl', align === 'center' && 'mx-auto')}>\n {tag && (\n <span className=\"inline-block text-sm font-semibold tracking-wider uppercase text-primary mb-2\">\n {tag}\n </span>\n )}\n {title && (\n <h2 className=\"text-3xl font-bold tracking-tight sm:text-4xl text-foreground\">\n {title}\n </h2>\n )}\n {description && (\n <p className=\"mt-4 text-lg text-muted-foreground leading-relaxed\">\n {description}\n </p>\n )}\n </div>\n {actions && (\n <div\n className={cn(\n 'flex flex-wrap gap-3 shrink-0',\n align === 'center' && 'justify-center'\n )}\n >\n {actions}\n </div>\n )}\n </div>\n );\n }\n);\nSectionHeader.displayName = 'SectionHeader';\n\nexport { SectionHeader };\n"
|
|
17440
|
+
},
|
|
17441
|
+
{
|
|
17442
|
+
"path": "components/blocks/container.tsx",
|
|
17443
|
+
"type": "registry:block",
|
|
17444
|
+
"content": "import React from 'react';\nimport { cn } from \"@/lib/utils\";\n\nexport interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> {\n as?: React.ElementType;\n clean?: boolean;\n}\n\nconst Container = React.forwardRef<HTMLDivElement, ContainerProps>(\n ({ className, as: Component = 'div', clean = false, ...props }, ref) => {\n return (\n <Component\n ref={ref}\n className={cn(\n !clean && 'mx-auto w-full max-w-7xl px-4 sm:px-6 lg:px-8',\n className\n )}\n {...props}\n />\n );\n }\n);\nContainer.displayName = 'Container';\n\nexport { Container };\n"
|
|
17309
17445
|
}
|
|
17310
17446
|
]
|
|
17311
17447
|
},
|