nexoreui-cli 0.1.0 → 0.1.3

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/dist/index.js CHANGED
@@ -26,6 +26,101 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  // src/commands/add.ts
27
27
  var fs3 = __toESM(require("fs"));
28
28
  var path3 = __toESM(require("path"));
29
+ var readline = __toESM(require("readline"));
30
+ var import_child_process = require("child_process");
31
+
32
+ // src/utils/detect.ts
33
+ var fs = __toESM(require("fs"));
34
+ var path = __toESM(require("path"));
35
+ function detectProject(cwd = process.cwd()) {
36
+ let packageManager = "npm";
37
+ let projectType = "unknown";
38
+ let hasSrcDir = false;
39
+ let currentDir = cwd;
40
+ let baseDir = cwd;
41
+ while (currentDir !== path.parse(currentDir).root) {
42
+ if (fs.existsSync(path.join(currentDir, "package.json"))) {
43
+ baseDir = currentDir;
44
+ break;
45
+ }
46
+ currentDir = path.dirname(currentDir);
47
+ }
48
+ if (fs.existsSync(path.join(baseDir, "pnpm-lock.yaml"))) {
49
+ packageManager = "pnpm";
50
+ } else if (fs.existsSync(path.join(baseDir, "yarn.lock"))) {
51
+ packageManager = "yarn";
52
+ } else if (fs.existsSync(path.join(baseDir, "bun.lockb")) || fs.existsSync(path.join(baseDir, "bun.lock"))) {
53
+ packageManager = "bun";
54
+ }
55
+ if (fs.existsSync(path.join(baseDir, "src"))) {
56
+ hasSrcDir = true;
57
+ }
58
+ try {
59
+ const packageJsonPath = path.join(baseDir, "package.json");
60
+ if (fs.existsSync(packageJsonPath)) {
61
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
62
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
63
+ if (deps["next"]) {
64
+ projectType = "next";
65
+ } else if (deps["vite"] || deps["@tailwindcss/vite"]) {
66
+ projectType = "vite";
67
+ } else if (deps["react-scripts"]) {
68
+ projectType = "cra";
69
+ }
70
+ }
71
+ } catch (err) {
72
+ }
73
+ return {
74
+ packageManager,
75
+ projectType,
76
+ hasSrcDir,
77
+ baseDir
78
+ };
79
+ }
80
+
81
+ // src/utils/copy.ts
82
+ var fs2 = __toESM(require("fs"));
83
+ var path2 = __toESM(require("path"));
84
+ var CN_TEMPLATE = `import { type ClassValue, clsx } from "clsx"
85
+ import { twMerge } from "tailwind-merge"
86
+
87
+ export function cn(...inputs: ClassValue[]) {
88
+ return twMerge(clsx(inputs))
89
+ }
90
+ `;
91
+ function ensureDir(dirPath) {
92
+ if (!fs2.existsSync(dirPath)) {
93
+ fs2.mkdirSync(dirPath, { recursive: true });
94
+ }
95
+ }
96
+ function getRelativeImportPath(fromDir, toFile) {
97
+ let relativePath = path2.relative(fromDir, toFile);
98
+ relativePath = relativePath.replace(/\\/g, "/");
99
+ relativePath = relativePath.replace(/\.(ts|tsx|js|jsx)$/, "");
100
+ if (!relativePath.startsWith(".")) {
101
+ relativePath = "./" + relativePath;
102
+ }
103
+ return relativePath;
104
+ }
105
+ function ensureCnUtil(utilsPath) {
106
+ const dir = path2.dirname(utilsPath);
107
+ ensureDir(dir);
108
+ if (!fs2.existsSync(utilsPath)) {
109
+ fs2.writeFileSync(utilsPath, CN_TEMPLATE, "utf8");
110
+ return true;
111
+ }
112
+ return false;
113
+ }
114
+ function copyComponentFile(content, targetFilePath, utilsFilePath) {
115
+ const targetDir = path2.dirname(targetFilePath);
116
+ ensureDir(targetDir);
117
+ const relativeImport = getRelativeImportPath(targetDir, utilsFilePath);
118
+ const rewrittenContent = content.replace(
119
+ /['"]\.\.\/utils\/cn['"]/g,
120
+ `"${relativeImport}"`
121
+ );
122
+ fs2.writeFileSync(targetFilePath, rewrittenContent, "utf8");
123
+ }
29
124
 
30
125
  // src/registry/button.ts
31
126
  var button = {
@@ -34,7 +129,8 @@ var button = {
34
129
  "class-variance-authority",
35
130
  "clsx",
36
131
  "tailwind-merge",
37
- "framer-motion"
132
+ "framer-motion",
133
+ "lucide-react"
38
134
  ],
39
135
  fileName: "button.tsx",
40
136
  content: `'use client';
@@ -42,28 +138,34 @@ var button = {
42
138
  import * as React from 'react';
43
139
  import { cva, type VariantProps } from 'class-variance-authority';
44
140
  import { cn } from '../utils/cn';
45
- import { motion, HTMLMotionProps, AnimatePresence } from 'framer-motion';
141
+ import { motion, HTMLMotionProps } from 'framer-motion';
142
+ import { Loader2 } from 'lucide-react';
46
143
 
47
144
  const buttonVariants = cva(
48
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 active:scale-95",
145
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 active:scale-95",
49
146
  {
50
147
  variants: {
51
148
  variant: {
52
- default: "bg-gradient-to-br from-primary to-primary/80 text-primary-foreground shadow-lg shadow-primary/20 hover:shadow-xl hover:shadow-primary/30",
149
+ default: "bg-gradient-to-br from-primary to-primary/80 text-primary-foreground shadow-lg shadow-primary/10 hover:shadow-xl hover:shadow-primary/20",
53
150
  secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 border border-border/50",
54
- destructive: "bg-gradient-to-br from-destructive to-destructive/80 text-destructive-foreground shadow-lg shadow-destructive/20 hover:shadow-xl hover:shadow-destructive/30",
151
+ destructive: "bg-gradient-to-br from-destructive to-destructive/80 text-destructive-foreground shadow-lg shadow-destructive/10 hover:shadow-xl hover:shadow-destructive/20",
55
152
  outline: "border-2 border-input bg-background hover:bg-accent hover:text-accent-foreground hover:border-accent",
56
153
  ghost: "hover:bg-accent hover:text-accent-foreground",
57
154
  link: "text-primary underline-offset-4 hover:underline",
58
- // New WOW Variants
155
+ // Premium variants
59
156
  premium: "bg-gradient-to-r from-violet-600 via-pink-600 to-orange-500 text-white shadow-lg shadow-purple-500/20 hover:shadow-xl hover:shadow-purple-500/30",
60
- neon: "bg-background border-2 border-primary text-foreground shadow-[0_0_15px_rgba(var(--primary-rgb),0.5)] hover:shadow-[0_0_25px_rgba(var(--primary-rgb),0.7)]",
157
+ neon: "bg-background border-2 border-primary text-foreground shadow-[0_0_15px_rgba(var(--primary-rgb),0.3)] hover:shadow-[0_0_25px_rgba(var(--primary-rgb),0.5)]",
61
158
  glass: "backdrop-blur-md bg-white/10 dark:bg-black/20 border border-white/20 dark:border-white/10 text-foreground hover:bg-white/20 dark:hover:bg-black/30 shadow-lg",
62
159
  shimmer: "relative overflow-hidden bg-slate-900 text-white dark:bg-white dark:text-black",
160
+ // New requested variants
161
+ gradient: "bg-gradient-to-r from-indigo-500 via-purple-500 to-violet-600 text-white shadow-lg shadow-indigo-500/20 hover:shadow-xl hover:shadow-indigo-500/30 hover:opacity-95",
162
+ glow: "bg-primary text-primary-foreground shadow-[0_0_12px_rgba(var(--primary-rgb),0.3)] hover:shadow-[0_0_24px_rgba(var(--primary-rgb),0.6)] border border-primary/20",
163
+ magnetic: "bg-gradient-to-br from-violet-600 to-indigo-600 text-white shadow-md hover:shadow-lg",
164
+ loading: "bg-primary/80 text-primary-foreground/80 pointer-events-none cursor-wait",
63
165
  },
64
166
  size: {
65
167
  default: "h-10 px-5 py-2",
66
- sm: "h-9 rounded-md px-3 text-xs",
168
+ sm: "h-9 rounded-lg px-3 text-xs",
67
169
  lg: "h-11 rounded-xl px-8 text-base",
68
170
  icon: "h-10 w-10 rounded-full",
69
171
  },
@@ -75,23 +177,70 @@ const buttonVariants = cva(
75
177
  }
76
178
  );
77
179
 
180
+ /**
181
+ * Props for the Button component
182
+ */
78
183
  export interface ButtonProps
79
184
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
80
185
  VariantProps<typeof buttonVariants> {
81
- /** Enable hover/tap motion animation @default true */
186
+ /**
187
+ * Enable hover/tap spring motion animation
188
+ * @default true
189
+ */
82
190
  animate?: boolean;
83
- /** Enable shimmer light effect across the button */
191
+ /**
192
+ * Enable shimmer light animation effect
193
+ * @default false
194
+ */
84
195
  shimmer?: boolean;
85
- /** Enable neon glow effect */
196
+ /**
197
+ * Enable neon glow effect
198
+ * @default false
199
+ */
86
200
  glow?: boolean;
87
- children?: React.ReactNode;
88
- className?: string;
201
+ /**
202
+ * Display loading spinner icon and disable actions
203
+ * @default false
204
+ */
205
+ isLoading?: boolean;
89
206
  }
90
207
 
91
208
  const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
92
- ({ className, variant, size, animate = true, shimmer = false, glow = false, children, ...props }, ref) => {
93
-
209
+ (
210
+ {
211
+ className,
212
+ variant,
213
+ size,
214
+ animate = true,
215
+ shimmer = false,
216
+ glow = false,
217
+ isLoading = false,
218
+ children,
219
+ ...props
220
+ },
221
+ ref
222
+ ) => {
94
223
  const isShimmer = variant === 'shimmer' || shimmer;
224
+ const isMagnetic = variant === 'magnetic';
225
+ const isGlow = variant === 'glow' || glow;
226
+
227
+ // Track mouse coords for magnetic hover movement
228
+ const [magneticPos, setMagneticPos] = React.useState({ x: 0, y: 0 });
229
+
230
+ const handleMouseMove = (e: React.MouseEvent<HTMLButtonElement>) => {
231
+ if (!isMagnetic) return;
232
+ const { clientX, clientY, currentTarget } = e;
233
+ const { left, top, width, height } = currentTarget.getBoundingClientRect();
234
+ const x = clientX - (left + width / 2);
235
+ const y = clientY - (top + height / 2);
236
+ // spring weight multiplier
237
+ setMagneticPos({ x: x * 0.35, y: y * 0.35 });
238
+ };
239
+
240
+ const handleMouseLeave = () => {
241
+ if (!isMagnetic) return;
242
+ setMagneticPos({ x: 0, y: 0 });
243
+ };
95
244
 
96
245
  const buttonContent = (
97
246
  <>
@@ -109,45 +258,240 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
109
258
  style={{ transform: 'skewX(-20deg)' }}
110
259
  />
111
260
  )}
112
- <span className="relative z-10 flex items-center gap-2">{children}</span>
261
+ <span className="relative z-10 flex items-center justify-center gap-2">
262
+ {isLoading && <Loader2 className="animate-spin h-4 w-4 shrink-0" />}
263
+ {children}
264
+ </span>
113
265
  </>
114
266
  );
115
267
 
268
+ const activeVariant = isLoading ? "loading" : variant;
269
+
270
+ // Disable button if loading
271
+ const disabledState = props.disabled || isLoading;
272
+
273
+ // Destructure custom props to avoid passing invalid props down to HTML element
274
+ const { ...htmlProps } = props;
275
+
276
+ // Setup base styles
277
+ const resolvedClassName = cn(
278
+ buttonVariants({ variant: activeVariant, size, className }),
279
+ isShimmer && "relative overflow-hidden",
280
+ isGlow && "shadow-[0_0_15px_rgba(var(--primary-rgb),0.4)]"
281
+ );
282
+
116
283
  if (!animate) {
117
284
  return (
118
285
  <button
119
- className={cn(buttonVariants({ variant, size, className }), isShimmer && "relative overflow-hidden")}
120
286
  ref={ref}
121
- {...props}
287
+ disabled={disabledState}
288
+ className={resolvedClassName}
289
+ {...(htmlProps as React.ButtonHTMLAttributes<HTMLButtonElement>)}
122
290
  >
123
291
  {buttonContent}
124
292
  </button>
125
293
  );
126
294
  }
127
295
 
128
- // Destructure HTML-only event handlers that shouldn't go to motion.button
129
- const { onDrag, onDragStart, onDragEnd, onAnimationStart, ...motionSafeProps } = props;
130
-
131
296
  return (
132
297
  <motion.button
133
- className={cn(buttonVariants({ variant, size, className }))}
134
298
  ref={ref}
135
- whileHover={{
136
- scale: 1.03,
137
- y: -1,
299
+ disabled={disabledState}
300
+ className={resolvedClassName}
301
+ onMouseMove={handleMouseMove}
302
+ onMouseLeave={handleMouseLeave}
303
+ animate={isMagnetic ? { x: magneticPos.x, y: magneticPos.y } : undefined}
304
+ whileHover={{
305
+ scale: isMagnetic ? 1.02 : 1.03,
306
+ y: isMagnetic ? 0 : -1.5,
307
+ shadow: isGlow ? "0 0 25px rgba(var(--primary-rgb), 0.7)" : undefined,
138
308
  }}
139
309
  whileTap={{ scale: 0.97 }}
140
- transition={{ type: "spring", stiffness: 400, damping: 15 }}
141
- {...motionSafeProps as HTMLMotionProps<"button">}
310
+ transition={{
311
+ type: "spring",
312
+ stiffness: 350,
313
+ damping: 20,
314
+ }}
315
+ {...(htmlProps as any)}
142
316
  >
143
317
  {buttonContent}
144
318
  </motion.button>
145
319
  );
146
320
  }
147
321
  );
322
+
148
323
  Button.displayName = "Button";
149
324
 
150
325
  export { Button, buttonVariants };
326
+
327
+ // ----------------------------------------------------
328
+ // Merged button components for backward compatibility
329
+ // ----------------------------------------------------
330
+
331
+ export const NeonButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
332
+ ({ children, ...props }, ref) => (
333
+ <Button ref={ref} variant="neon" glow={true} {...props}>
334
+ {children}
335
+ </Button>
336
+ )
337
+ );
338
+ NeonButton.displayName = "NeonButton";
339
+
340
+ export const ThreeDButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
341
+ ({ children, className, ...props }, ref) => (
342
+ <Button
343
+ ref={ref}
344
+ className={cn(
345
+ "shadow-[0_5px_0_hsl(var(--primary-dark,240_5.9%_30%))] hover:shadow-[0_2px_0_hsl(var(--primary-dark,240_5.9%_30%))] active:translate-y-[3px] active:shadow-[0_0px_0_transparent] transition-all",
346
+ className
347
+ )}
348
+ {...props}
349
+ >
350
+ {children}
351
+ </Button>
352
+ )
353
+ );
354
+ ThreeDButton.displayName = "ThreeDButton";
355
+
356
+ export const RippleButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
357
+ ({ children, className, ...props }, ref) => (
358
+ <Button
359
+ ref={ref}
360
+ className={cn(
361
+ "relative overflow-hidden group active:scale-95 transition-transform",
362
+ className
363
+ )}
364
+ {...props}
365
+ >
366
+ <span className="absolute inset-0 bg-white/20 scale-0 rounded-full group-active:scale-[2] transition-transform duration-500 origin-center"></span>
367
+ <span className="relative z-10">{children}</span>
368
+ </Button>
369
+ )
370
+ );
371
+ RippleButton.displayName = "RippleButton";
372
+
373
+ export const CyberpunkButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
374
+ ({ children, className, ...props }, ref) => (
375
+ <Button
376
+ ref={ref}
377
+ className={cn(
378
+ "bg-yellow-400 text-black font-extrabold uppercase tracking-widest border-2 border-black hover:bg-black hover:text-yellow-400 hover:border-yellow-400 transition-colors shadow-[4px_4px_0_0_#000] rounded-none hover:shadow-[4px_4px_0_0_#fff]",
379
+ className
380
+ )}
381
+ {...props}
382
+ >
383
+ {children}
384
+ </Button>
385
+ )
386
+ );
387
+ CyberpunkButton.displayName = "CyberpunkButton";
388
+
389
+ export const MagneticButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
390
+ ({ children, ...props }, ref) => (
391
+ <Button ref={ref} variant="magnetic" {...props}>
392
+ {children}
393
+ </Button>
394
+ )
395
+ );
396
+ MagneticButton.displayName = "MagneticButton";
397
+
398
+ export const ShimmerButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
399
+ ({ children, ...props }, ref) => (
400
+ <Button ref={ref} variant="shimmer" shimmer={true} {...props}>
401
+ {children}
402
+ </Button>
403
+ )
404
+ );
405
+ ShimmerButton.displayName = "ShimmerButton";
406
+
407
+ export const BorderBeamButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
408
+ ({ children, className, ...props }, ref) => (
409
+ <Button
410
+ ref={ref}
411
+ variant="outline"
412
+ className={cn(
413
+ "relative overflow-hidden border border-border group",
414
+ className
415
+ )}
416
+ {...props}
417
+ >
418
+ <div className="absolute inset-0 bg-gradient-to-r from-primary to-transparent opacity-0 group-hover:opacity-20 transition-opacity"></div>
419
+ <div className="absolute top-0 left-0 w-full h-[2px] bg-primary scale-x-0 group-hover:scale-x-100 transition-transform origin-left"></div>
420
+ <span className="relative z-10">{children}</span>
421
+ </Button>
422
+ )
423
+ );
424
+ BorderBeamButton.displayName = "BorderBeamButton";
425
+
426
+ export const LoadingButton = React.forwardRef<HTMLButtonElement, ButtonProps & { isLoading?: boolean }>(
427
+ ({ children, isLoading = true, ...props }, ref) => (
428
+ <Button ref={ref} isLoading={isLoading} {...props}>
429
+ {children}
430
+ </Button>
431
+ )
432
+ );
433
+ LoadingButton.displayName = "LoadingButton";
434
+
435
+ export const DestructiveGlowButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
436
+ ({ children, ...props }, ref) => (
437
+ <Button ref={ref} variant="destructive" glow={true} {...props}>
438
+ {children}
439
+ </Button>
440
+ )
441
+ );
442
+ DestructiveGlowButton.displayName = "DestructiveGlowButton";
443
+
444
+ export const GhostOutlineButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
445
+ ({ children, ...props }, ref) => (
446
+ <Button ref={ref} variant="outline" {...props}>
447
+ {children}
448
+ </Button>
449
+ )
450
+ );
451
+ GhostOutlineButton.displayName = "GhostOutlineButton";
452
+
453
+ export const GlowButton = React.forwardRef<HTMLButtonElement, ButtonProps & { glowColor?: string }>(
454
+ ({ children, glowColor = "rgba(139, 92, 246, 0.15)", className, ...props }, ref) => (
455
+ <div className="relative group inline-block">
456
+ <div
457
+ className="absolute -inset-0.5 bg-gradient-to-r from-primary to-purple-600 rounded-lg blur opacity-75 group-hover:opacity-100 transition duration-1000 group-hover:duration-200"
458
+ style={{ backgroundColor: glowColor }}
459
+ />
460
+ <Button ref={ref} className={cn("relative bg-background", className)} {...props}>
461
+ {children}
462
+ </Button>
463
+ </div>
464
+ )
465
+ );
466
+ GlowButton.displayName = "GlowButton";
467
+
468
+ export const ShinyButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
469
+ ({ children, ...props }, ref) => (
470
+ <Button ref={ref} shimmer={true} {...props}>
471
+ {children}
472
+ </Button>
473
+ )
474
+ );
475
+ ShinyButton.displayName = "ShinyButton";
476
+
477
+ export const GradientButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
478
+ ({ children, ...props }, ref) => (
479
+ <Button ref={ref} variant="gradient" {...props}>
480
+ {children}
481
+ </Button>
482
+ )
483
+ );
484
+ GradientButton.displayName = "GradientButton";
485
+
486
+ export const GlassButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
487
+ ({ children, ...props }, ref) => (
488
+ <Button ref={ref} variant="glass" {...props}>
489
+ {children}
490
+ </Button>
491
+ )
492
+ );
493
+ GlassButton.displayName = "GlassButton";
494
+
151
495
  `
152
496
  };
153
497
 
@@ -162,13 +506,15 @@ var modal = {
162
506
  "lucide-react",
163
507
  "framer-motion"
164
508
  ],
165
- componentsDependencies: ["button"],
509
+ componentsDependencies: [
510
+ "button"
511
+ ],
166
512
  fileName: "modal.tsx",
167
513
  content: `"use client"
168
514
 
169
515
  import * as React from "react"
170
516
  import * as DialogPrimitive from "@radix-ui/react-dialog"
171
- import { X, AlertTriangle, CheckCircle } from "lucide-react"
517
+ import { X, AlertTriangle, CheckCircle, Star } from "lucide-react"
172
518
  import { cn } from "../utils/cn"
173
519
  import { Button } from "./button"
174
520
 
@@ -198,7 +544,7 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
198
544
  const DialogContent = React.forwardRef<
199
545
  React.ElementRef<typeof DialogPrimitive.Content>,
200
546
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
201
- >((({ className, children, ...props }, ref) => (
547
+ >(({ className, children, ...props }, ref) => (
202
548
  <DialogPortal>
203
549
  <DialogOverlay />
204
550
  <DialogPrimitive.Content
@@ -416,6 +762,307 @@ export function Modal({
416
762
  </Dialog>
417
763
  )
418
764
  }
765
+
766
+ // ============================================
767
+ // Backward compatibility wrappers & variants
768
+ // ============================================
769
+
770
+ export interface BasicModalProps {
771
+ isOpen?: boolean;
772
+ onOpenChange?: (open: boolean) => void;
773
+ trigger?: React.ReactNode;
774
+ title?: string;
775
+ description?: string;
776
+ children?: React.ReactNode;
777
+ confirmText?: string;
778
+ cancelText?: string;
779
+ onConfirm?: () => void;
780
+ onCancel?: () => void;
781
+ className?: string;
782
+ }
783
+
784
+ export const BasicModal = ({
785
+ isOpen,
786
+ onOpenChange,
787
+ trigger,
788
+ title = "Basic Modal",
789
+ description = "This is a simple modal dialog that can be used for various purposes.",
790
+ children,
791
+ confirmText = "Confirm",
792
+ cancelText = "Cancel",
793
+ onConfirm,
794
+ onCancel,
795
+ className = ""
796
+ }: BasicModalProps) => {
797
+ return (
798
+ <Modal
799
+ isOpen={isOpen}
800
+ onOpenChange={onOpenChange}
801
+ trigger={trigger}
802
+ title={title}
803
+ description={description}
804
+ confirmText={confirmText}
805
+ cancelText={cancelText}
806
+ onConfirm={onConfirm}
807
+ onCancel={onCancel}
808
+ className={className}
809
+ variant="default"
810
+ >
811
+ {children}
812
+ </Modal>
813
+ )
814
+ }
815
+
816
+ export interface InteractiveGlassModalProps {
817
+ isOpen?: boolean;
818
+ onOpenChange?: (open: boolean) => void;
819
+ trigger?: React.ReactNode;
820
+ icon?: React.ReactNode;
821
+ title?: string;
822
+ description?: string;
823
+ children?: React.ReactNode;
824
+ confirmText?: string;
825
+ cancelText?: string;
826
+ onConfirm?: () => void;
827
+ onCancel?: () => void;
828
+ className?: string;
829
+ }
830
+
831
+ export const InteractiveGlassModal = ({
832
+ isOpen,
833
+ onOpenChange,
834
+ trigger,
835
+ icon = <Star className="w-6 h-6 text-yellow-300" />,
836
+ title = "Premium Glass Effect",
837
+ description = "This modal uses full glassmorphism for a stunning visual effect.",
838
+ children,
839
+ confirmText = "Upgrade Now",
840
+ cancelText = "Maybe Later",
841
+ onConfirm,
842
+ onCancel,
843
+ className = ""
844
+ }: InteractiveGlassModalProps) => {
845
+ return (
846
+ <Modal
847
+ isOpen={isOpen}
848
+ onOpenChange={onOpenChange}
849
+ trigger={trigger}
850
+ title={<span className="flex items-center gap-2">{icon} {title}</span>}
851
+ description={description}
852
+ confirmText={confirmText}
853
+ cancelText={cancelText}
854
+ onConfirm={onConfirm}
855
+ onCancel={onCancel}
856
+ className={className}
857
+ variant="glass"
858
+ >
859
+ {children}
860
+ </Modal>
861
+ )
862
+ }
863
+
864
+ export interface DangerModalProps {
865
+ isOpen?: boolean;
866
+ onOpenChange?: (open: boolean) => void;
867
+ trigger?: React.ReactNode;
868
+ icon?: React.ReactNode;
869
+ title?: string;
870
+ description?: string;
871
+ children?: React.ReactNode;
872
+ confirmText?: string;
873
+ cancelText?: string;
874
+ onConfirm?: () => void;
875
+ onCancel?: () => void;
876
+ className?: string;
877
+ }
878
+
879
+ export const DangerModal = ({
880
+ isOpen,
881
+ onOpenChange,
882
+ trigger,
883
+ icon = <X className="w-8 h-8" />,
884
+ title = "Are you absolutely sure?",
885
+ description = "This action cannot be undone. This will permanently delete your account and remove your data from our servers.",
886
+ children,
887
+ confirmText = "Delete",
888
+ cancelText = "Cancel",
889
+ onConfirm,
890
+ onCancel,
891
+ className = ""
892
+ }: DangerModalProps) => {
893
+ return (
894
+ <Modal
895
+ isOpen={isOpen}
896
+ onOpenChange={onOpenChange}
897
+ trigger={trigger}
898
+ title={title}
899
+ description={description}
900
+ confirmText={confirmText}
901
+ cancelText={cancelText}
902
+ onConfirm={onConfirm}
903
+ onCancel={onCancel}
904
+ className={className}
905
+ variant="destructive"
906
+ >
907
+ {children}
908
+ </Modal>
909
+ )
910
+ }
911
+
912
+ export interface GlassModalProps {
913
+ trigger?: React.ReactNode
914
+ title?: React.ReactNode
915
+ description?: React.ReactNode
916
+ children?: React.ReactNode
917
+ open?: boolean
918
+ onOpenChange?: (open: boolean) => void
919
+ }
920
+
921
+ export function GlassModal({ trigger, title = "Glass Modal", description, children, open, onOpenChange }: GlassModalProps) {
922
+ return (
923
+ <Modal
924
+ isOpen={open}
925
+ onOpenChange={onOpenChange}
926
+ trigger={trigger}
927
+ title={title}
928
+ description={description}
929
+ variant="glass"
930
+ >
931
+ {children}
932
+ </Modal>
933
+ )
934
+ }
935
+
936
+ export interface AlertModalProps {
937
+ trigger?: React.ReactNode
938
+ title?: string
939
+ description?: string
940
+ onConfirm?: () => void
941
+ onCancel?: () => void
942
+ confirmText?: string
943
+ cancelText?: string
944
+ children?: React.ReactNode
945
+ open?: boolean
946
+ onOpenChange?: (open: boolean) => void
947
+ }
948
+
949
+ export function AlertModal({
950
+ trigger,
951
+ title = "Are you absolutely sure?",
952
+ description,
953
+ onConfirm,
954
+ onCancel,
955
+ confirmText = "Confirm",
956
+ cancelText = "Cancel",
957
+ children,
958
+ open,
959
+ onOpenChange
960
+ }: AlertModalProps) {
961
+ return (
962
+ <Modal
963
+ isOpen={open}
964
+ onOpenChange={onOpenChange}
965
+ trigger={trigger}
966
+ title={title}
967
+ description={description}
968
+ confirmText={confirmText}
969
+ cancelText={cancelText}
970
+ onConfirm={onConfirm}
971
+ onCancel={onCancel}
972
+ variant="destructive"
973
+ >
974
+ {children}
975
+ </Modal>
976
+ )
977
+ }
978
+
979
+ export interface SuccessModalProps {
980
+ trigger?: React.ReactNode
981
+ title?: string
982
+ description?: string
983
+ children?: React.ReactNode
984
+ open?: boolean
985
+ onOpenChange?: (open: boolean) => void
986
+ confirmText?: string
987
+ onConfirm?: () => void
988
+ }
989
+
990
+ export function SuccessModal({
991
+ trigger,
992
+ title = "Success!",
993
+ description,
994
+ children,
995
+ open,
996
+ onOpenChange,
997
+ confirmText = "Awesome",
998
+ onConfirm
999
+ }: SuccessModalProps) {
1000
+ return (
1001
+ <Modal
1002
+ isOpen={open}
1003
+ onOpenChange={onOpenChange}
1004
+ trigger={trigger}
1005
+ title={title}
1006
+ description={description}
1007
+ confirmText={confirmText}
1008
+ onConfirm={onConfirm}
1009
+ variant="success"
1010
+ >
1011
+ {children}
1012
+ </Modal>
1013
+ )
1014
+ }
1015
+
1016
+ export interface CommandPaletteModalProps {
1017
+ trigger: React.ReactNode
1018
+ open?: boolean
1019
+ onOpenChange?: (open: boolean) => void
1020
+ }
1021
+
1022
+ export function CommandPaletteModal({ trigger, open, onOpenChange }: CommandPaletteModalProps) {
1023
+ return (
1024
+ <Dialog open={open} onOpenChange={onOpenChange}>
1025
+ <DialogTrigger asChild>{trigger}</DialogTrigger>
1026
+ <DialogContent className="p-0 overflow-hidden sm:max-w-[600px] gap-0">
1027
+ <div className="flex items-center border-b px-3">
1028
+ <svg className="mr-2 h-4 w-4 shrink-0 opacity-50" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
1029
+ <input className="flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50" placeholder="Type a command or search..." />
1030
+ </div>
1031
+ <div className="max-h-[300px] overflow-y-auto p-2">
1032
+ <div className="px-2 py-1.5 text-xs font-medium text-muted-foreground">Suggestions</div>
1033
+ <div className="flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground hover:bg-accent/50">
1034
+ Calendar
1035
+ </div>
1036
+ <div className="flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground hover:bg-accent/50">
1037
+ Search Emoji
1038
+ </div>
1039
+ <div className="flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground hover:bg-accent/50">
1040
+ Calculator
1041
+ </div>
1042
+ </div>
1043
+ </DialogContent>
1044
+ </Dialog>
1045
+ )
1046
+ }
1047
+
1048
+ export interface BottomSheetSimulatedProps {
1049
+ trigger: React.ReactNode
1050
+ children?: React.ReactNode
1051
+ open?: boolean
1052
+ onOpenChange?: (open: boolean) => void
1053
+ }
1054
+
1055
+ export function BottomSheetSimulated({ trigger, children, open, onOpenChange }: BottomSheetSimulatedProps) {
1056
+ return (
1057
+ <Dialog open={open} onOpenChange={onOpenChange}>
1058
+ <DialogTrigger asChild>{trigger}</DialogTrigger>
1059
+ <DialogContent className="sm:max-w-full sm:h-[50vh] sm:rounded-b-none sm:rounded-t-[10px] fixed bottom-0 top-auto translate-y-0 data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom">
1060
+ <div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
1061
+ {children}
1062
+ </DialogContent>
1063
+ </Dialog>
1064
+ )
1065
+ }
419
1066
  `
420
1067
  };
421
1068
 
@@ -426,18 +1073,20 @@ var card = {
426
1073
  "class-variance-authority",
427
1074
  "clsx",
428
1075
  "tailwind-merge",
429
- "framer-motion"
1076
+ "framer-motion",
1077
+ "lucide-react"
430
1078
  ],
431
1079
  fileName: "card.tsx",
432
1080
  content: `'use client';
433
1081
 
434
1082
  import * as React from "react"
435
1083
  import { cn } from "../utils/cn"
436
- import { motion, HTMLMotionProps } from "framer-motion"
1084
+ import { motion, useMotionValue, useSpring, useTransform, HTMLMotionProps } from "framer-motion"
437
1085
  import { cva, type VariantProps } from "class-variance-authority"
1086
+ import { Heart, Share2, MapPin, Star } from "lucide-react"
438
1087
 
439
1088
  const cardVariants = cva(
440
- "rounded-2xl text-card-foreground transition-all duration-300",
1089
+ "rounded-2xl text-card-foreground transition-all duration-300 overflow-hidden",
441
1090
  {
442
1091
  variants: {
443
1092
  variant: {
@@ -445,11 +1094,16 @@ const cardVariants = cva(
445
1094
  glass: "backdrop-blur-md bg-white/10 dark:bg-black/20 border border-white/20 dark:border-white/10 shadow-lg",
446
1095
  gradient: "bg-gradient-to-br from-violet-500/10 via-pink-500/10 to-orange-500/10 border border-purple-500/20 shadow-lg shadow-purple-500/5",
447
1096
  glow: "bg-card border-2 border-primary/20 shadow-[0_0_15px_rgba(var(--primary-rgb),0.1)] hover:shadow-[0_0_25px_rgba(var(--primary-rgb),0.2)]",
1097
+ // New variants
1098
+ bento: "border border-border/60 bg-gradient-to-br from-card to-muted/20 text-card-foreground shadow-md hover:shadow-lg hover:border-primary/30 relative",
1099
+ spotlight: "border bg-card text-card-foreground relative hover:border-primary/20",
1100
+ flip: "bg-transparent border-0 shadow-none overflow-visible relative",
1101
+ tilt: "border bg-card text-card-foreground shadow-md",
448
1102
  },
449
1103
  hover: {
450
1104
  none: "",
451
- lift: "hover:-translate-y-1 hover:shadow-lg",
452
- glow: "hover:border-primary/50 hover:shadow-[0_0_20px_rgba(var(--primary-rgb),0.15)]",
1105
+ lift: "hover:-translate-y-1.5 hover:shadow-lg",
1106
+ glow: "hover:border-primary/50 hover:shadow-[0_0_25px_rgba(var(--primary-rgb),0.25)]",
453
1107
  }
454
1108
  },
455
1109
  defaultVariants: {
@@ -459,43 +1113,112 @@ const cardVariants = cva(
459
1113
  }
460
1114
  )
461
1115
 
1116
+ /**
1117
+ * Props for the Card component
1118
+ */
462
1119
  export interface CardProps
463
- extends Omit<React.HTMLAttributes<HTMLDivElement>, keyof HTMLMotionProps<"div">>,
1120
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'>,
464
1121
  VariantProps<typeof cardVariants> {
465
1122
  /**
466
- * Whether to enable hover animations
1123
+ * Whether to enable hover spring animations
467
1124
  * @default true
468
1125
  */
469
1126
  animate?: boolean;
470
1127
  /**
471
- * The title of the card
1128
+ * The main title of the card
472
1129
  */
473
1130
  title?: React.ReactNode;
474
1131
  /**
475
- * The description of the card
1132
+ * The subtitle or description of the card
476
1133
  */
477
1134
  description?: React.ReactNode;
478
1135
  /**
479
- * The footer content of the card
1136
+ * Content to render in the card footer area
480
1137
  */
481
1138
  footer?: React.ReactNode;
482
1139
  /**
483
- * An image URL to display at the top of the card
1140
+ * An optional image URL to display at the top of the card
484
1141
  */
485
1142
  image?: string;
486
- }
487
-
488
- const Card = React.forwardRef<HTMLDivElement, CardProps & HTMLMotionProps<"div">>(
489
- ({ className, variant, hover, animate = true, title, description, footer, image, children, ...props }, ref) => {
1143
+ /**
1144
+ * HTML alternative text for the image
1145
+ */
1146
+ imageAlt?: string;
1147
+ /**
1148
+ * Back content displayed when using the \`flip\` variant on hover
1149
+ */
1150
+ backContent?: React.ReactNode;
1151
+ /**
1152
+ * Custom radial spotlight background color (e.g., rgba(168, 85, 247, 0.15))
1153
+ * @default "rgba(139, 92, 246, 0.15)"
1154
+ */
1155
+ spotlightColor?: string;
1156
+ }
1157
+
1158
+ const Card = React.forwardRef<HTMLDivElement, CardProps>(
1159
+ (
1160
+ {
1161
+ className,
1162
+ variant,
1163
+ hover,
1164
+ animate = true,
1165
+ title,
1166
+ description,
1167
+ footer,
1168
+ image,
1169
+ imageAlt,
1170
+ backContent,
1171
+ spotlightColor = "rgba(139, 92, 246, 0.15)",
1172
+ children,
1173
+ ...props
1174
+ },
1175
+ ref
1176
+ ) => {
490
1177
  const isCompound = !title && !description && !footer && !image;
491
1178
 
492
- const content = isCompound ? (
1179
+ // Feature toggles based on variants
1180
+ const isSpotlight = variant === "spotlight";
1181
+ const isFlip = variant === "flip";
1182
+ const isTilt = variant === "tilt";
1183
+
1184
+ // Spotlight mouse tracking state
1185
+ const [mousePos, setMousePos] = React.useState({ x: 0, y: 0 });
1186
+ const handleMouseMoveSpotlight = (e: React.MouseEvent<HTMLDivElement>) => {
1187
+ if (!isSpotlight) return;
1188
+ const { currentTarget, clientX, clientY } = e;
1189
+ const { left, top } = currentTarget.getBoundingClientRect();
1190
+ setMousePos({ x: clientX - left, y: clientY - top });
1191
+ };
1192
+
1193
+ // Tilt mouse tracking state
1194
+ const [tiltPos, setTiltPos] = React.useState({ rotateX: 0, rotateY: 0 });
1195
+ const handleMouseMoveTilt = (e: React.MouseEvent<HTMLDivElement>) => {
1196
+ if (!isTilt) return;
1197
+ const { currentTarget, clientX, clientY } = e;
1198
+ const { left, top, width, height } = currentTarget.getBoundingClientRect();
1199
+ const x = clientX - left;
1200
+ const y = clientY - top;
1201
+ const maxTilt = 12; // degrees max rotation
1202
+ const rotateX = ((y - height / 2) / (height / 2)) * -maxTilt;
1203
+ const rotateY = ((x - width / 2) / (width / 2)) * maxTilt;
1204
+ setTiltPos({ rotateX, rotateY });
1205
+ };
1206
+
1207
+ const handleMouseLeaveTilt = () => {
1208
+ if (!isTilt) return;
1209
+ setTiltPos({ rotateX: 0, rotateY: 0 });
1210
+ };
1211
+
1212
+ // Flip card hover state
1213
+ const [isFlipped, setIsFlipped] = React.useState(false);
1214
+
1215
+ const baseContent = isCompound ? (
493
1216
  children
494
1217
  ) : (
495
1218
  <>
496
1219
  {image && (
497
1220
  <div className="relative w-full h-48 overflow-hidden rounded-t-2xl">
498
- <img src={image} alt={typeof title === 'string' ? title : 'Card image'} className="object-cover w-full h-full" />
1221
+ <img src={image} alt={imageAlt || (typeof title === 'string' ? title : 'Card image')} className="object-cover w-full h-full transition-transform duration-300 hover:scale-105" />
499
1222
  </div>
500
1223
  )}
501
1224
  {(title || description) && (
@@ -509,16 +1232,78 @@ const Card = React.forwardRef<HTMLDivElement, CardProps & HTMLMotionProps<"div">
509
1232
  </>
510
1233
  );
511
1234
 
512
- if (animate) {
1235
+ // Destructure custom props to avoid DOM validation warnings
1236
+ const { ...htmlProps } = props;
1237
+
1238
+ // Flip Variant Render
1239
+ if (isFlip) {
1240
+ return (
1241
+ <div
1242
+ ref={ref}
1243
+ className={cn(cardVariants({ variant, hover, className }), "perspective-1000 w-full h-full")}
1244
+ onMouseEnter={() => setIsFlipped(true)}
1245
+ onMouseLeave={() => setIsFlipped(false)}
1246
+ {...(htmlProps as React.HTMLAttributes<HTMLDivElement>)}
1247
+ >
1248
+ <motion.div
1249
+ className="relative w-full h-full transition-all duration-500 preserve-3d"
1250
+ animate={{ rotateY: isFlipped ? 180 : 0 }}
1251
+ transition={{ type: "spring", stiffness: 300, damping: 22 }}
1252
+ >
1253
+ {/* Front Face */}
1254
+ <div className="absolute inset-0 backface-hidden border bg-card text-card-foreground rounded-2xl shadow-sm flex flex-col justify-between overflow-hidden">
1255
+ {baseContent}
1256
+ </div>
1257
+
1258
+ {/* Back Face */}
1259
+ <div className="absolute inset-0 backface-hidden rotate-y-180 border bg-gradient-to-br from-primary/10 to-primary/5 text-card-foreground rounded-2xl shadow-sm flex flex-col p-6 items-center justify-center text-center overflow-hidden">
1260
+ {backContent || (
1261
+ <div className="text-sm font-medium text-muted-foreground">
1262
+ Flip side content placeholder
1263
+ </div>
1264
+ )}
1265
+ </div>
1266
+ </motion.div>
1267
+ </div>
1268
+ );
1269
+ }
1270
+
1271
+ // Spotlight Variant Render Extra Element
1272
+ const spotlightEffect = isSpotlight && (
1273
+ <div
1274
+ className="pointer-events-none absolute -inset-px rounded-2xl opacity-0 hover:opacity-100 group-hover:opacity-100 transition-opacity duration-300"
1275
+ style={{
1276
+ background: \`radial-gradient(400px circle at \${mousePos.x}px \${mousePos.y}px, \${spotlightColor}, transparent 80%)\`,
1277
+ }}
1278
+ />
1279
+ );
1280
+
1281
+ // Build the resolved element attributes
1282
+ const cardClass = cn(cardVariants({ variant, hover: isFlip || isTilt ? "none" : hover, className }), isSpotlight && "group");
1283
+
1284
+ if (animate || isTilt) {
513
1285
  return (
514
1286
  <motion.div
515
1287
  ref={ref}
516
- className={cn(cardVariants({ variant, hover, className }))}
517
- whileHover={{ scale: 1.01 }}
1288
+ className={cardClass}
1289
+ onMouseMove={(e) => {
1290
+ if (isSpotlight) handleMouseMoveSpotlight(e);
1291
+ if (isTilt) handleMouseMoveTilt(e);
1292
+ }}
1293
+ onMouseLeave={() => {
1294
+ if (isTilt) handleMouseLeaveTilt();
1295
+ }}
1296
+ animate={
1297
+ isTilt
1298
+ ? { rotateX: tiltPos.rotateX, rotateY: tiltPos.rotateY }
1299
+ : undefined
1300
+ }
1301
+ whileHover={isTilt ? undefined : { scale: 1.015 }}
518
1302
  transition={{ type: "spring", stiffness: 300, damping: 20 }}
519
- {...props}
1303
+ {...(htmlProps as any)}
520
1304
  >
521
- {content as React.ReactNode}
1305
+ {spotlightEffect}
1306
+ {baseContent}
522
1307
  </motion.div>
523
1308
  );
524
1309
  }
@@ -526,10 +1311,10 @@ const Card = React.forwardRef<HTMLDivElement, CardProps & HTMLMotionProps<"div">
526
1311
  return (
527
1312
  <div
528
1313
  ref={ref}
529
- className={cn(cardVariants({ variant, hover, className }))}
530
- {...(props as React.HTMLAttributes<HTMLDivElement>)}
1314
+ className={cardClass}
1315
+ {...(htmlProps as React.HTMLAttributes<HTMLDivElement>)}
531
1316
  >
532
- {content as React.ReactNode}
1317
+ {baseContent}
533
1318
  </div>
534
1319
  );
535
1320
  }
@@ -554,7 +1339,7 @@ const CardTitle = React.forwardRef<
554
1339
  >(({ className, ...props }, ref) => (
555
1340
  <h3
556
1341
  ref={ref}
557
- className="font-semibold leading-none tracking-tight text-xl bg-gradient-to-br from-foreground to-foreground/70 bg-clip-text text-transparent"
1342
+ className={cn("font-semibold leading-none tracking-tight text-xl bg-gradient-to-br from-foreground to-foreground/75 bg-clip-text text-transparent", className)}
558
1343
  {...props}
559
1344
  />
560
1345
  ))
@@ -566,7 +1351,7 @@ const CardDescription = React.forwardRef<
566
1351
  >(({ className, ...props }, ref) => (
567
1352
  <p
568
1353
  ref={ref}
569
- className={cn("text-sm text-muted-foreground", className)}
1354
+ className={cn("text-sm text-muted-foreground leading-relaxed mt-1", className)}
570
1355
  {...props}
571
1356
  />
572
1357
  ))
@@ -576,7 +1361,7 @@ const CardContent = React.forwardRef<
576
1361
  HTMLDivElement,
577
1362
  React.HTMLAttributes<HTMLDivElement>
578
1363
  >(({ className, ...props }, ref) => (
579
- <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
1364
+ <div ref={ref} className={cn("p-6 pt-0 leading-relaxed text-sm text-foreground/90", className)} {...props} />
580
1365
  ))
581
1366
  CardContent.displayName = "CardContent"
582
1367
 
@@ -586,13 +1371,199 @@ const CardFooter = React.forwardRef<
586
1371
  >(({ className, ...props }, ref) => (
587
1372
  <div
588
1373
  ref={ref}
589
- className={cn("flex items-center p-6 pt-0", className)}
1374
+ className={cn("flex items-center p-6 pt-0 border-t border-border/10 mt-auto", className)}
590
1375
  {...props}
591
1376
  />
592
1377
  ))
593
1378
  CardFooter.displayName = "CardFooter"
594
1379
 
595
1380
  export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
1381
+
1382
+ export const GlassCard = React.forwardRef<HTMLDivElement, CardProps>(
1383
+ ({ children, ...props }, ref) => (
1384
+ <Card ref={ref} variant="glass" {...props}>
1385
+ {children}
1386
+ </Card>
1387
+ )
1388
+ );
1389
+ GlassCard.displayName = "GlassCard";
1390
+
1391
+ export const GlowCard = React.forwardRef<HTMLDivElement, CardProps>(
1392
+ ({ children, ...props }, ref) => (
1393
+ <Card ref={ref} variant="glow" {...props}>
1394
+ {children}
1395
+ </Card>
1396
+ )
1397
+ );
1398
+ GlowCard.displayName = "GlowCard";
1399
+
1400
+ export const GradientCard = React.forwardRef<HTMLDivElement, CardProps>(
1401
+ ({ children, ...props }, ref) => (
1402
+ <Card ref={ref} variant="gradient" {...props}>
1403
+ {children}
1404
+ </Card>
1405
+ )
1406
+ );
1407
+ GradientCard.displayName = "GradientCard";
1408
+
1409
+ export const HoverCard = React.forwardRef<HTMLDivElement, CardProps>(
1410
+ ({ children, ...props }, ref) => (
1411
+ <Card ref={ref} hover="lift" {...props}>
1412
+ {children}
1413
+ </Card>
1414
+ )
1415
+ );
1416
+ HoverCard.displayName = "HoverCard";
1417
+
1418
+ export const SpotlightCard = React.forwardRef<HTMLDivElement, CardProps>(
1419
+ ({ children, ...props }, ref) => (
1420
+ <Card ref={ref} variant="spotlight" {...props}>
1421
+ {children}
1422
+ </Card>
1423
+ )
1424
+ );
1425
+ SpotlightCard.displayName = "SpotlightCard";
1426
+
1427
+ // ============================================
1428
+ // Consolidated Legacy/Special Cards for Compatibility
1429
+ // ============================================
1430
+
1431
+ export const ImageCard = ({ src, title, subtitle, imageUrl, description }: any) => {
1432
+ const finalSrc = src || imageUrl;
1433
+ const finalTitle = title;
1434
+ const finalSubtitle = subtitle || description;
1435
+ return (
1436
+ <div className="group relative overflow-hidden rounded-xl border bg-card text-card-foreground">
1437
+ <div className="aspect-[4/3] w-full bg-muted overflow-hidden">
1438
+ {finalSrc ? (
1439
+ <img
1440
+ src={finalSrc}
1441
+ className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
1442
+ alt={typeof finalTitle === "string" ? finalTitle : "Image"}
1443
+ />
1444
+ ) : (
1445
+ <div className="w-full h-full bg-muted-foreground/20" />
1446
+ )}
1447
+ </div>
1448
+ <div className="p-4">
1449
+ <h3 className="font-semibold text-lg">{finalTitle}</h3>
1450
+ <p className="text-sm text-muted-foreground">{finalSubtitle}</p>
1451
+ </div>
1452
+ </div>
1453
+ );
1454
+ };
1455
+
1456
+ export const ProfileCard = ({ name, role, avatar }: any) => (
1457
+ <div className="flex flex-col items-center p-6 text-center rounded-xl border bg-card shadow-sm">
1458
+ <div className="w-24 h-24 rounded-full bg-muted mb-4 overflow-hidden border-4 border-background shadow-md">
1459
+ {avatar && <img src={avatar} className="w-full h-full object-cover" alt={name} />}
1460
+ </div>
1461
+ <h3 className="text-xl font-bold">{name}</h3>
1462
+ <p className="text-sm text-muted-foreground mb-4">{role}</p>
1463
+ <button className="px-6 py-2 bg-primary text-primary-foreground rounded-full font-medium w-full hover:bg-primary/90 transition-colors">Follow</button>
1464
+ </div>
1465
+ )
1466
+
1467
+ export const ProductCard = ({ title, price, category, src }: any) => (
1468
+ <div className="rounded-xl border bg-card p-4 flex flex-col gap-3 group">
1469
+ <div className="aspect-square w-full rounded-lg bg-muted overflow-hidden relative">
1470
+ {src && <img src={src} className="w-full h-full object-cover" alt={title} />}
1471
+ <button className="absolute top-2 right-2 p-2 bg-background/50 backdrop-blur rounded-full hover:bg-background transition-colors"><Heart className="w-4 h-4" /></button>
1472
+ </div>
1473
+ <div>
1474
+ <p className="text-xs text-muted-foreground mb-1">{category}</p>
1475
+ <h3 className="font-medium truncate">{title}</h3>
1476
+ <p className="font-bold text-lg mt-1">\${price}</p>
1477
+ </div>
1478
+ </div>
1479
+ )
1480
+
1481
+ export const ArticleCard = ({ title, excerpt, date }: any) => (
1482
+ <div className="p-6 rounded-xl border bg-card flex flex-col gap-4 hover:shadow-md transition-shadow cursor-pointer">
1483
+ <span className="text-xs font-medium text-primary">{date}</span>
1484
+ <h3 className="text-xl font-bold leading-tight">{title}</h3>
1485
+ <p className="text-muted-foreground line-clamp-3">{excerpt}</p>
1486
+ <div className="mt-auto pt-4 border-t flex items-center justify-between text-sm">
1487
+ <span className="font-medium">Read more \u2192</span>
1488
+ <button><Share2 className="w-4 h-4 text-muted-foreground hover:text-foreground" /></button>
1489
+ </div>
1490
+ </div>
1491
+ )
1492
+
1493
+ export const StatCardSimple = ({ label, value, trend }: any) => (
1494
+ <div className="p-5 rounded-xl border bg-card">
1495
+ <p className="text-sm font-medium text-muted-foreground mb-2">{label}</p>
1496
+ <div className="flex items-end justify-between">
1497
+ <h4 className="text-3xl font-bold">{value}</h4>
1498
+ <span className={\`text-sm font-medium \${trend?.startsWith('+') ? 'text-green-500' : 'text-red-500'}\`}>{trend}</span>
1499
+ </div>
1500
+ </div>
1501
+ )
1502
+
1503
+ export const PricingCardBasic = ({ name, price, features }: any) => (
1504
+ <div className="p-6 rounded-xl border bg-card flex flex-col items-center text-center">
1505
+ <h3 className="text-xl font-medium mb-2">{name}</h3>
1506
+ <div className="mb-6"><span className="text-4xl font-bold">\${price}</span><span className="text-muted-foreground">/mo</span></div>
1507
+ <ul className="space-y-3 w-full mb-8 text-sm">
1508
+ {features?.map((f: string, i: number) => <li key={i} className="text-muted-foreground border-b pb-2 last:border-0">{f}</li>)}
1509
+ </ul>
1510
+ <button className="w-full py-2 bg-primary text-primary-foreground rounded-lg font-medium mt-auto">Subscribe</button>
1511
+ </div>
1512
+ )
1513
+
1514
+ export const WeatherCard = ({ city, temp, condition }: any) => (
1515
+ <div className="p-6 rounded-xl border bg-gradient-to-br from-blue-500 to-cyan-400 text-white shadow-lg">
1516
+ <div className="flex justify-between items-start mb-8">
1517
+ <div>
1518
+ <h3 className="text-2xl font-bold">{city}</h3>
1519
+ <p className="opacity-80">{condition}</p>
1520
+ </div>
1521
+ <div className="text-5xl font-light">{temp}\xB0</div>
1522
+ </div>
1523
+ <div className="flex gap-4 opacity-90 text-sm">
1524
+ <span>H: {temp + 4}\xB0</span>
1525
+ <span>L: {temp - 3}\xB0</span>
1526
+ </div>
1527
+ </div>
1528
+ )
1529
+
1530
+ export const EventCard = ({ title, date, location }: any) => (
1531
+ <div className="flex p-4 rounded-xl border bg-card gap-4">
1532
+ <div className="flex flex-col items-center justify-center bg-primary/10 text-primary rounded-lg px-4 py-2 min-w-[70px]">
1533
+ <span className="text-xs uppercase font-bold">{date?.split(' ')[0]}</span>
1534
+ <span className="text-2xl font-black">{date?.split(' ')[1]}</span>
1535
+ </div>
1536
+ <div className="flex flex-col justify-center">
1537
+ <h3 className="font-bold text-lg leading-tight mb-1">{title}</h3>
1538
+ <div className="flex items-center text-sm text-muted-foreground gap-1">
1539
+ <MapPin className="w-3 h-3" /> {location}
1540
+ </div>
1541
+ </div>
1542
+ </div>
1543
+ )
1544
+
1545
+ export const TestimonialCardBasic = ({ text, author }: any) => (
1546
+ <div className="p-6 rounded-xl border bg-muted/30 italic relative">
1547
+ <span className="absolute top-4 left-4 text-4xl text-primary/20 font-serif">"</span>
1548
+ <p className="relative z-10 text-muted-foreground mb-4 pt-4">{text}</p>
1549
+ <div className="flex items-center gap-2">
1550
+ <div className="w-8 h-8 rounded-full bg-primary/20" />
1551
+ <span className="font-semibold text-sm not-italic">{author}</span>
1552
+ </div>
1553
+ </div>
1554
+ )
1555
+
1556
+ export const InteractiveCard = ({ title, description }: any) => (
1557
+ <div className="group p-6 rounded-xl border bg-card hover:bg-primary hover:text-primary-foreground transition-all duration-300 cursor-pointer">
1558
+ <div className="w-12 h-12 rounded-lg bg-primary/10 text-primary group-hover:bg-primary-foreground/20 group-hover:text-primary-foreground flex items-center justify-center mb-4 transition-colors">
1559
+ <Star className="w-6 h-6" />
1560
+ </div>
1561
+ <h3 className="text-xl font-bold mb-2">{title}</h3>
1562
+ <p className="text-muted-foreground group-hover:text-primary-foreground/80 transition-colors">{description}</p>
1563
+ </div>
1564
+ )
1565
+
1566
+
596
1567
  `
597
1568
  };
598
1569
 
@@ -603,7 +1574,8 @@ var alert = {
603
1574
  "class-variance-authority",
604
1575
  "clsx",
605
1576
  "tailwind-merge",
606
- "framer-motion"
1577
+ "framer-motion",
1578
+ "lucide-react"
607
1579
  ],
608
1580
  fileName: "alert.tsx",
609
1581
  content: `"use client"
@@ -611,10 +1583,11 @@ var alert = {
611
1583
  import * as React from "react"
612
1584
  import { cva, type VariantProps } from "class-variance-authority"
613
1585
  import { cn } from "../utils/cn"
614
- import { motion, HTMLMotionProps } from "framer-motion"
1586
+ import { motion, AnimatePresence } from "framer-motion"
1587
+ import { AlertCircle, Info, CheckCircle2, XCircle, Cookie, BellRing, WifiOff, AlertTriangle, X } from "lucide-react"
615
1588
 
616
1589
  const alertVariants = cva(
617
- "relative w-full rounded-xl border p-4 [&>svg~*]:pl-7 [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 transition-all duration-300",
1590
+ "relative w-full rounded-2xl border p-4 [&>svg~*]:pl-7 [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 transition-all duration-300 shadow-sm",
618
1591
  {
619
1592
  variants: {
620
1593
  variant: {
@@ -624,6 +1597,12 @@ const alertVariants = cva(
624
1597
  warning: "border-amber-500/20 bg-amber-500/10 text-amber-600 dark:text-amber-400 [&>svg]:text-amber-600 dark:[&>svg]:text-amber-400",
625
1598
  info: "border-blue-500/20 bg-blue-500/10 text-blue-600 dark:text-blue-400 [&>svg]:text-blue-600 dark:[&>svg]:text-blue-400",
626
1599
  glass: "backdrop-blur-md bg-white/5 dark:bg-black/20 border-white/10 dark:border-white/5 text-foreground",
1600
+ floating: "fixed bottom-5 right-5 z-50 max-w-sm bg-card/95 backdrop-blur-md border border-border/80 text-foreground shadow-[0_10px_30px_rgba(0,0,0,0.25)] animate-slide-up",
1601
+ minimal: "border-0 bg-muted/40 p-3 text-sm rounded-xl text-foreground hover:bg-muted/60 shadow-none [&>svg]:top-3.5",
1602
+ neon: "border-purple-500/50 bg-purple-500/10 text-purple-200 shadow-[0_0_15px_rgba(168,85,247,0.3)] [&>svg]:text-purple-400",
1603
+ cyberpunk: "border-l-4 border-yellow-400 bg-black text-yellow-400 shadow-[4px_4px_0_0_rgba(250,204,21,1)] rounded-none font-mono uppercase [&>svg]:text-yellow-400",
1604
+ gradient: "bg-gradient-to-r from-blue-500/10 to-indigo-500/10 border-blue-500/20 text-foreground [&>svg]:text-blue-500",
1605
+ banner: "w-full bg-indigo-600 text-white border-0 shadow-md rounded-none sm:rounded-xl [&>svg]:text-white",
627
1606
  },
628
1607
  },
629
1608
  defaultVariants: {
@@ -635,76 +1614,85 @@ const alertVariants = cva(
635
1614
  export interface AlertProps
636
1615
  extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'>,
637
1616
  VariantProps<typeof alertVariants> {
638
- /**
639
- * Whether to enable entry animations
640
- * @default true
641
- */
642
1617
  animate?: boolean;
643
- /**
644
- * The title of the alert
645
- */
646
1618
  title?: React.ReactNode;
647
- /**
648
- * The description of the alert
649
- */
650
1619
  description?: React.ReactNode;
651
- /**
652
- * An optional icon to display
653
- */
654
1620
  icon?: React.ReactNode;
655
- /**
656
- * Whether the alert can be dismissed
657
- */
658
1621
  dismissible?: boolean;
659
- /**
660
- * Callback function called when the alert is dismissed
661
- */
662
1622
  onDismiss?: () => void;
1623
+ actionText?: string;
1624
+ onAction?: () => void;
663
1625
  }
664
1626
 
665
1627
  const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
666
- ({ className, variant, animate = true, title, description, icon, dismissible, onDismiss, children, ...props }, ref) => {
667
- const isCompound = !title && !description && !icon;
1628
+ (
1629
+ {
1630
+ className,
1631
+ variant,
1632
+ animate = true,
1633
+ title,
1634
+ description,
1635
+ icon,
1636
+ dismissible = false,
1637
+ onDismiss,
1638
+ actionText,
1639
+ onAction,
1640
+ children,
1641
+ ...props
1642
+ },
1643
+ ref
1644
+ ) => {
668
1645
  const [isOpen, setIsOpen] = React.useState(true);
669
1646
 
670
1647
  if (!isOpen) return null;
671
1648
 
672
- const content = isCompound ? (
673
- children
674
- ) : (
1649
+ const isMinimal = variant === "minimal";
1650
+ const isBanner = variant === "banner";
1651
+
1652
+ const content = (
675
1653
  <>
676
- {icon && <div className="absolute left-4 top-4">{icon}</div>}
677
- <div className={cn(icon ? "pl-7" : "")}>
1654
+ {icon && <div className={cn("absolute left-4", isMinimal ? "top-3" : "top-4")}>{icon}</div>}
1655
+ <div className={cn(icon ? "pl-7" : "", "pr-8")}>
678
1656
  {title && <AlertTitle>{title}</AlertTitle>}
679
1657
  {description && <AlertDescription>{description}</AlertDescription>}
680
1658
  {!title && !description && children}
681
1659
  </div>
1660
+ {isBanner && actionText && (
1661
+ <button
1662
+ onClick={onAction}
1663
+ className="absolute right-12 top-1/2 -translate-y-1/2 text-xs font-bold bg-white text-indigo-600 px-3 py-1.5 rounded-md hover:bg-indigo-50 transition-colors"
1664
+ >
1665
+ {actionText}
1666
+ </button>
1667
+ )}
682
1668
  {dismissible && (
683
1669
  <button
684
1670
  onClick={() => {
685
1671
  setIsOpen(false);
686
1672
  onDismiss?.();
687
1673
  }}
688
- className="absolute right-4 top-4 opacity-70 hover:opacity-100 transition-opacity"
1674
+ className="absolute right-4 top-4 opacity-50 hover:opacity-100 transition-opacity p-0.5 rounded-md hover:bg-muted"
689
1675
  aria-label="Dismiss"
690
1676
  >
691
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
1677
+ <X className="h-4 w-4" />
692
1678
  </button>
693
1679
  )}
694
1680
  </>
695
1681
  );
696
1682
 
1683
+ const alertClass = cn(alertVariants({ variant }), className);
1684
+
697
1685
  if (animate) {
698
1686
  return (
699
1687
  <motion.div
700
1688
  ref={ref}
701
1689
  role="alert"
702
- initial={{ opacity: 0, y: 10 }}
703
- animate={{ opacity: 1, y: 0 }}
704
- exit={{ opacity: 0, y: 10 }}
705
- transition={{ duration: 0.3 }}
706
- className={cn(alertVariants({ variant }), className)}
707
- {...(props as unknown as HTMLMotionProps<"div">)}
1690
+ initial={{ opacity: 0, y: variant === "floating" ? 30 : 15, scale: variant === "floating" ? 0.95 : 1 }}
1691
+ animate={{ opacity: 1, y: 0, scale: 1 }}
1692
+ exit={{ opacity: 0, y: variant === "floating" ? 20 : 10, scale: 0.95 }}
1693
+ transition={{ type: "spring", stiffness: 350, damping: 24 }}
1694
+ className={alertClass}
1695
+ {...(props as any)}
708
1696
  >
709
1697
  {content}
710
1698
  </motion.div>
@@ -715,8 +1703,8 @@ const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
715
1703
  <div
716
1704
  ref={ref}
717
1705
  role="alert"
718
- className={cn(alertVariants({ variant }), className)}
719
- {...props}
1706
+ className={alertClass}
1707
+ {...(props as React.HTMLAttributes<HTMLDivElement>)}
720
1708
  >
721
1709
  {content}
722
1710
  </div>
@@ -731,7 +1719,7 @@ const AlertTitle = React.forwardRef<
731
1719
  >(({ className, ...props }, ref) => (
732
1720
  <h5
733
1721
  ref={ref}
734
- className={cn("mb-1 font-semibold leading-none tracking-tight text-base", className)}
1722
+ className={cn("mb-1 font-semibold leading-none tracking-tight text-base text-foreground", className)}
735
1723
  {...props}
736
1724
  />
737
1725
  ))
@@ -743,13 +1731,137 @@ const AlertDescription = React.forwardRef<
743
1731
  >(({ className, ...props }, ref) => (
744
1732
  <div
745
1733
  ref={ref}
746
- className={cn("text-sm opacity-90 [&_p]:leading-relaxed", className)}
1734
+ className={cn("text-sm text-muted-foreground leading-relaxed mt-1 opacity-90 [&_p]:leading-relaxed", className)}
747
1735
  {...props}
748
1736
  />
749
1737
  ))
750
1738
  AlertDescription.displayName = "AlertDescription"
751
1739
 
1740
+ // ----------------------------------------------------
1741
+ // Merged subcomponents and wrappers
1742
+ // ----------------------------------------------------
1743
+
1744
+ // 1. ToastAlertWrapper
1745
+ export const ToastAlertWrapper = ({ children, className, title, description, time }: any) => (
1746
+ <div className={cn("max-w-sm w-full bg-background border border-border/80 shadow-xl rounded-2xl p-4 flex gap-4 items-start relative", className)}>
1747
+ <CheckCircle2 className="h-5 w-5 text-green-500 shrink-0 mt-0.5" />
1748
+ <div className="flex-1">
1749
+ {title && <h4 className="font-semibold text-sm">{title}</h4>}
1750
+ {description && <p className="text-sm text-muted-foreground mt-1">{description}</p>}
1751
+ {children}
1752
+ </div>
1753
+ {time && <span className="text-xs text-muted-foreground/60">{time}</span>}
1754
+ </div>
1755
+ )
1756
+
1757
+ // 2. CookieAlert
1758
+ export const CookieAlert = ({ onAccept, onDecline }: { onAccept?: () => void; onDecline?: () => void }) => {
1759
+ const [visible, setVisible] = React.useState(true)
1760
+ if (!visible) return null
1761
+ return (
1762
+ <div className="max-w-md bg-card border rounded-2xl p-6 shadow-2xl space-y-4">
1763
+ <div className="flex items-center gap-3">
1764
+ <Cookie className="h-6 w-6 text-orange-500 animate-bounce" />
1765
+ <h4 className="font-bold text-lg">Cookie Preferences</h4>
1766
+ </div>
1767
+ <p className="text-sm text-muted-foreground">
1768
+ We use cookies to improve your experience. By continuing to visit this site you agree to our use of cookies.
1769
+ </p>
1770
+ <div className="flex gap-3 pt-2">
1771
+ <button
1772
+ onClick={() => { setVisible(false); onAccept?.(); }}
1773
+ className="flex-1 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/95 transition-colors rounded-xl text-sm font-semibold shadow-md shadow-primary/10"
1774
+ >
1775
+ Accept All
1776
+ </button>
1777
+ <button
1778
+ onClick={() => { setVisible(false); onDecline?.(); }}
1779
+ className="flex-1 px-4 py-2 border rounded-xl text-sm font-semibold hover:bg-muted transition-colors"
1780
+ >
1781
+ Decline
1782
+ </button>
1783
+ </div>
1784
+ </div>
1785
+ )
1786
+ }
1787
+
1788
+ // 3. OfflineBanner
1789
+ export const OfflineBanner = () => (
1790
+ <div className="w-full bg-red-600 text-white p-3.5 flex justify-center items-center gap-2 text-sm font-semibold shadow-md sm:rounded-xl">
1791
+ <WifiOff className="w-4 h-4 animate-pulse" /> You are currently offline. Some features may be unavailable.
1792
+ </div>
1793
+ )
1794
+
1795
+ // 4. RateLimitAlert
1796
+ export const RateLimitAlert = () => (
1797
+ <div className="border border-orange-500/30 bg-orange-500/10 p-5 rounded-2xl flex gap-3 items-start max-w-md shadow-sm">
1798
+ <AlertTriangle className="w-5 h-5 text-orange-500 shrink-0 mt-0.5 animate-pulse" />
1799
+ <div className="flex-1">
1800
+ <h4 className="font-bold text-orange-600 dark:text-orange-400">Rate Limit Exceeded</h4>
1801
+ <p className="text-sm text-orange-600/80 dark:text-orange-400/80 mt-1 mb-4">
1802
+ You have made too many requests. Please wait 45 seconds before trying again.
1803
+ </p>
1804
+ <div className="w-full h-1.5 bg-orange-500/20 rounded-full overflow-hidden">
1805
+ <motion.div animate={{ width: ["100%", "0%"] }} transition={{ duration: 45, ease: "linear" }} className="h-full bg-orange-500" />
1806
+ </div>
1807
+ </div>
1808
+ </div>
1809
+ )
1810
+
1811
+ // Re-export original/merged components
752
1812
  export { Alert, AlertTitle, AlertDescription }
1813
+ export const CyberAlert = ({ title, description, variant, ...props }: any) => (
1814
+ <Alert variant="cyberpunk" title={title} description={description} {...props} />
1815
+ )
1816
+ export const SoftAlert = ({ title, description, variant, ...props }: any) => (
1817
+ <Alert variant={variant === "success" ? "success" : "info"} title={title} description={description} {...props} />
1818
+ )
1819
+ export const MinimalAlert = ({ title, description, variant, ...props }: any) => (
1820
+ <Alert variant="minimal" title={title} description={description} {...props} />
1821
+ )
1822
+ export const LeftBorderAlert = ({ title, description, variant, ...props }: any) => (
1823
+ <Alert variant={variant === "warning" ? "warning" : "default"} className="border-l-4 border-l-primary" title={title} description={description} {...props} />
1824
+ )
1825
+ export const IconTopAlert = ({ title, description, variant, ...props }: any) => (
1826
+ <div className="flex flex-col items-center text-center p-6 bg-card border rounded-2xl" {...props}>
1827
+ <div className="h-12 w-12 rounded-full bg-destructive/10 text-destructive flex items-center justify-center mb-4">
1828
+ <AlertCircle className="h-6 w-6" />
1829
+ </div>
1830
+ <h4 className="font-bold text-lg mb-2">{title}</h4>
1831
+ <p className="text-sm text-muted-foreground">{description}</p>
1832
+ </div>
1833
+ )
1834
+ export const SolidAlert = ({ title, description, variant, ...props }: any) => {
1835
+ const bgClasses: Record<string, string> = {
1836
+ error: "bg-red-600 text-white border-0",
1837
+ success: "bg-emerald-600 text-white border-0",
1838
+ warning: "bg-amber-500 text-black border-0",
1839
+ default: "bg-primary text-primary-foreground border-0",
1840
+ }
1841
+ const bgClass = bgClasses[variant] || bgClasses.default
1842
+ return (
1843
+ <div className={cn("p-4 rounded-xl shadow-lg flex gap-3 items-start", bgClass)} {...props}>
1844
+ <Info className="h-5 w-5 shrink-0 mt-0.5" />
1845
+ <div>
1846
+ <h4 className="font-bold">{title}</h4>
1847
+ <p className="text-sm opacity-90 mt-1">{description}</p>
1848
+ </div>
1849
+ </div>
1850
+ )
1851
+ }
1852
+ export const BannerAlert = ({ message, variant, ...props }: any) => (
1853
+ <Alert variant="banner" title={message} {...props} />
1854
+ )
1855
+ export const NeonAlert = ({ title, description, variant, ...props }: any) => (
1856
+ <Alert variant="neon" title={title} description={description} {...props} />
1857
+ )
1858
+ export const GlassAlert = ({ title, description, variant, ...props }: any) => (
1859
+ <Alert variant="glass" title={title} description={description} {...props} />
1860
+ )
1861
+ export const DismissibleAlert = ({ variant, title, description, ...props }: any) => (
1862
+ <Alert variant={variant} title={title || "Attention"} description={description || "Action required"} dismissible={true} {...props} />
1863
+ )
1864
+
753
1865
  `
754
1866
  };
755
1867
 
@@ -760,7 +1872,8 @@ var badge = {
760
1872
  "class-variance-authority",
761
1873
  "clsx",
762
1874
  "tailwind-merge",
763
- "framer-motion"
1875
+ "framer-motion",
1876
+ "lucide-react"
764
1877
  ],
765
1878
  fileName: "badge.tsx",
766
1879
  content: `'use client';
@@ -768,7 +1881,7 @@ var badge = {
768
1881
  import * as React from "react"
769
1882
  import { cva, type VariantProps } from "class-variance-authority"
770
1883
  import { cn } from "../utils/cn"
771
- import { motion } from "framer-motion"
1884
+ import { Star } from "lucide-react"
772
1885
 
773
1886
  const badgeVariants = cva(
774
1887
  "inline-flex items-center gap-1 rounded-full border font-semibold transition-all focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 cursor-default",
@@ -779,9 +1892,8 @@ const badgeVariants = cva(
779
1892
  secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
780
1893
  destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
781
1894
  outline: "text-foreground border-border hover:bg-accent",
782
- // New WOW Variants
783
1895
  gradient: "border-transparent bg-gradient-to-r from-violet-500 to-pink-500 text-white shadow-sm",
784
- neon: "border-primary/50 bg-primary/10 text-primary shadow-[0_0_10px_rgba(var(--primary-rgb),0.3)]",
1896
+ neon: "border-purple-500/50 bg-purple-500/10 text-purple-400 shadow-[0_0_10px_rgba(168,85,247,0.3)]",
785
1897
  success: "border-transparent bg-emerald-500/20 text-emerald-600 dark:text-emerald-400",
786
1898
  },
787
1899
  size: {
@@ -800,19 +1912,12 @@ const badgeVariants = cva(
800
1912
  export interface BadgeProps
801
1913
  extends React.HTMLAttributes<HTMLDivElement>,
802
1914
  VariantProps<typeof badgeVariants> {
803
- /**
804
- * Whether to show a pulse animation on the dot
805
- * @default false
806
- */
807
1915
  pulse?: boolean;
808
- /**
809
- * Whether to show a dot indicator
810
- * @default false
811
- */
812
1916
  dot?: boolean;
1917
+ text?: string;
813
1918
  }
814
1919
 
815
- function Badge({ className, variant, size, pulse = false, dot = false, children, ...props }: BadgeProps) {
1920
+ function Badge({ className, variant, size, pulse = false, dot = false, children, text, ...props }: BadgeProps) {
816
1921
  const showDot = dot || pulse;
817
1922
 
818
1923
  return (
@@ -825,12 +1930,355 @@ function Badge({ className, variant, size, pulse = false, dot = false, children,
825
1930
  <span className="relative inline-flex rounded-full h-2 w-2 bg-current"></span>
826
1931
  </span>
827
1932
  )}
828
- {children}
1933
+ {children || text}
829
1934
  </div>
830
1935
  )
831
1936
  }
832
1937
 
833
- export { Badge, badgeVariants }
1938
+ export { Badge, badgeVariants }`
1939
+ };
1940
+
1941
+ // src/registry/morphing-geometry.ts
1942
+ var morphingGeometry = {
1943
+ name: "morphing-geometry",
1944
+ dependencies: [
1945
+ "clsx",
1946
+ "tailwind-merge",
1947
+ "framer-motion",
1948
+ "lucide-react"
1949
+ ],
1950
+ fileName: "morphing-geometry.tsx",
1951
+ content: `'use client';
1952
+
1953
+ import * as React from 'react';
1954
+ import { motion, HTMLMotionProps } from 'framer-motion';
1955
+ import { Sparkles } from 'lucide-react';
1956
+ import { cn } from '../utils/cn';
1957
+
1958
+ export type MorphingShape = 'pill' | 'circle' | 'square' | 'squircle' | 'custom';
1959
+ export type MorphingVariant = 'gradient' | 'aurora' | 'neon' | 'glass' | 'outline' | 'subtle';
1960
+ export type MorphingColor = 'violet' | 'cyan' | 'emerald' | 'rose' | 'amber' | 'rainbow' | 'mono';
1961
+ export type MorphingSize = 'sm' | 'md' | 'lg' | 'xl' | 'custom';
1962
+
1963
+ export interface MorphingGeometryProps extends Omit<HTMLMotionProps<'div'>, 'children'> {
1964
+ shape?: MorphingShape;
1965
+ radius?: number | string;
1966
+ variant?: MorphingVariant;
1967
+ color?: MorphingColor;
1968
+ size?: MorphingSize;
1969
+ dimension?: number;
1970
+ spin?: boolean;
1971
+ spinDuration?: number;
1972
+ interactive?: boolean;
1973
+ glow?: boolean;
1974
+ icon?: React.ReactNode;
1975
+ children?: React.ReactNode;
1976
+ }
1977
+
1978
+ export const MorphingGeometry = React.forwardRef<HTMLDivElement, MorphingGeometryProps>(
1979
+ (
1980
+ {
1981
+ shape = 'squircle',
1982
+ radius,
1983
+ variant = 'gradient',
1984
+ color = 'violet',
1985
+ size = 'md',
1986
+ dimension,
1987
+ spin = false,
1988
+ spinDuration = 6,
1989
+ interactive = false,
1990
+ glow = true,
1991
+ icon,
1992
+ children,
1993
+ className,
1994
+ style,
1995
+ onClick,
1996
+ ...props
1997
+ },
1998
+ ref
1999
+ ) => {
2000
+ return (
2001
+ <motion.div
2002
+ ref={ref}
2003
+ animate={spin ? { rotate: [0, 90, 180, 270, 360] } : { rotate: 0 }}
2004
+ transition={{ rotate: { duration: spinDuration, repeat: Infinity, ease: 'linear' }, borderRadius: { duration: 0.4 } }}
2005
+ className={cn('relative flex items-center justify-center select-none overflow-hidden transition-all duration-300 w-24 h-24', className)}
2006
+ style={{ borderRadius: radius || '24%', ...style }}
2007
+ {...props}
2008
+ >
2009
+ <div className="relative z-10 flex flex-col items-center justify-center p-2 text-center">
2010
+ {icon || children || <Sparkles className="w-6 h-6 text-white" />}
2011
+ </div>
2012
+ </motion.div>
2013
+ );
2014
+ }
2015
+ );
2016
+
2017
+ MorphingGeometry.displayName = 'MorphingGeometry';
2018
+ export default MorphingGeometry;
2019
+ `
2020
+ };
2021
+
2022
+ // src/registry/aurora-border-fx.ts
2023
+ var auroraBorderFX = {
2024
+ name: "aurora-border-fx",
2025
+ dependencies: [
2026
+ "clsx",
2027
+ "tailwind-merge",
2028
+ "framer-motion",
2029
+ "lucide-react"
2030
+ ],
2031
+ fileName: "aurora-border-fx.tsx",
2032
+ content: `'use client';
2033
+
2034
+ import * as React from 'react';
2035
+ import { motion, useReducedMotion } from 'framer-motion';
2036
+ import { Sparkles } from 'lucide-react';
2037
+ import { cn } from '../utils/cn';
2038
+
2039
+ export type AuroraFXColor = 'violet' | 'cyan' | 'emerald' | 'rose' | 'amber' | string;
2040
+ export type AuroraFXGlow = 'none' | 'subtle' | 'medium' | 'strong';
2041
+ export type AuroraFXRadius = 'sm' | 'md' | 'lg' | 'xl' | 'full';
2042
+
2043
+ export interface AuroraColorOption {
2044
+ name: string;
2045
+ hex: string;
2046
+ }
2047
+
2048
+ export const defaultAuroraColors: AuroraColorOption[] = [
2049
+ { name: 'Violet', hex: '#8b5cf6' },
2050
+ { name: 'Cyan', hex: '#06b6d4' },
2051
+ { name: 'Emerald', hex: '#10b981' },
2052
+ { name: 'Rose', hex: '#f43f5e' },
2053
+ { name: 'Amber', hex: '#f59e0b' },
2054
+ ];
2055
+
2056
+ const colorPresetMap: Record<string, string> = {
2057
+ violet: '#8b5cf6',
2058
+ cyan: '#06b6d4',
2059
+ emerald: '#10b981',
2060
+ rose: '#f43f5e',
2061
+ amber: '#f59e0b',
2062
+ };
2063
+
2064
+ const radiusMap: Record<AuroraFXRadius, { outer: string; inner: string }> = {
2065
+ sm: { outer: 'rounded-lg', inner: 'rounded-[calc(0.5rem-1px)]' },
2066
+ md: { outer: 'rounded-xl', inner: 'rounded-[calc(0.75rem-1px)]' },
2067
+ lg: { outer: 'rounded-2xl', inner: 'rounded-[calc(1rem-1px)]' },
2068
+ xl: { outer: 'rounded-3xl', inner: 'rounded-[calc(1.5rem-1.5px)]' },
2069
+ full: { outer: 'rounded-full', inner: 'rounded-full' },
2070
+ };
2071
+
2072
+ const glowOpacityMap: Record<AuroraFXGlow, number> = {
2073
+ none: 0,
2074
+ subtle: 0.25,
2075
+ medium: 0.45,
2076
+ strong: 0.75,
2077
+ };
2078
+
2079
+ export interface AuroraBorderFXProps extends React.HTMLAttributes<HTMLDivElement> {
2080
+ color?: AuroraFXColor;
2081
+ glow?: AuroraFXGlow;
2082
+ radius?: AuroraFXRadius;
2083
+ badgeText?: string;
2084
+ badgeIcon?: React.ReactNode;
2085
+ title?: string;
2086
+ description?: string;
2087
+ showColorPicker?: boolean;
2088
+ colors?: AuroraColorOption[];
2089
+ activeColor?: string;
2090
+ onColorChange?: (colorHex: string) => void;
2091
+ previewSlot?: React.ReactNode;
2092
+ footerSlot?: React.ReactNode;
2093
+ children?: React.ReactNode;
2094
+ }
2095
+
2096
+ export const AuroraBorderFX = React.forwardRef<HTMLDivElement, AuroraBorderFXProps>(
2097
+ (
2098
+ {
2099
+ color = 'violet',
2100
+ glow = 'medium',
2101
+ radius = 'lg',
2102
+ badgeText = 'Aurora Border FX',
2103
+ badgeIcon = <Sparkles className="w-3 h-3" />,
2104
+ title = 'Reactive Aurora Borders',
2105
+ description = 'Smooth multi-color conic gradients that dynamically track and react with zero JavaScript canvas lag.',
2106
+ showColorPicker = true,
2107
+ colors = defaultAuroraColors,
2108
+ activeColor: controlledColor,
2109
+ onColorChange,
2110
+ previewSlot,
2111
+ footerSlot,
2112
+ className,
2113
+ children,
2114
+ ...props
2115
+ },
2116
+ ref
2117
+ ) => {
2118
+ const resolvedInitialColor = colorPresetMap[color] || color || '#8b5cf6';
2119
+ const [internalColor, setInternalColor] = React.useState<string>(resolvedInitialColor);
2120
+
2121
+ React.useEffect(() => {
2122
+ if (colorPresetMap[color]) {
2123
+ setInternalColor(colorPresetMap[color]);
2124
+ } else if (color) {
2125
+ setInternalColor(color);
2126
+ }
2127
+ }, [color]);
2128
+
2129
+ const currentColor = controlledColor !== undefined ? controlledColor : internalColor;
2130
+ const radiusConfig = radiusMap[radius] || radiusMap.lg;
2131
+ const glowOpacity = glowOpacityMap[glow] ?? 0.45;
2132
+
2133
+ const handleSelectColor = (hex: string) => {
2134
+ if (controlledColor === undefined) {
2135
+ setInternalColor(hex);
2136
+ }
2137
+ onColorChange?.(hex);
2138
+ };
2139
+
2140
+ return (
2141
+ <div
2142
+ ref={ref}
2143
+ className={cn(
2144
+ 'relative isolate p-5 sm:p-6 overflow-hidden flex flex-col justify-between group transition-all duration-300',
2145
+ 'border border-border/80 bg-card/60 backdrop-blur-xl shadow-xl',
2146
+ radiusConfig.outer,
2147
+ className
2148
+ )}
2149
+ {...props}
2150
+ >
2151
+ {glow !== 'none' && (
2152
+ <div
2153
+ className="absolute -top-12 -right-12 w-48 h-48 rounded-full blur-[85px] pointer-events-none transition-colors duration-500 -z-10"
2154
+ style={{
2155
+ backgroundColor: currentColor,
2156
+ opacity: glowOpacity,
2157
+ }}
2158
+ />
2159
+ )}
2160
+
2161
+ {glow !== 'none' && glow !== 'subtle' && (
2162
+ <div
2163
+ className="absolute -bottom-10 -left-10 w-40 h-40 rounded-full blur-[90px] pointer-events-none transition-colors duration-700 -z-10"
2164
+ style={{
2165
+ backgroundColor: currentColor,
2166
+ opacity: glowOpacity * 0.4,
2167
+ }}
2168
+ />
2169
+ )}
2170
+
2171
+ {children ? (
2172
+ <div className="relative z-10 w-full h-full">{children}</div>
2173
+ ) : (
2174
+ <div className="relative z-10 flex flex-col justify-between h-full space-y-5">
2175
+ <div className="space-y-3">
2176
+ <div className="flex items-center justify-between gap-3">
2177
+ {badgeText && (
2178
+ <div
2179
+ className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-[11px] font-semibold border transition-all duration-300 shadow-xs"
2180
+ style={{
2181
+ backgroundColor: \`\${currentColor}18\`,
2182
+ borderColor: \`\${currentColor}40\`,
2183
+ color: currentColor,
2184
+ }}
2185
+ >
2186
+ {badgeIcon}
2187
+ <span>{badgeText}</span>
2188
+ </div>
2189
+ )}
2190
+
2191
+ {showColorPicker && colors && colors.length > 0 && (
2192
+ <div className="flex items-center gap-1.5 bg-muted/60 dark:bg-muted/40 p-1 rounded-full border border-border/60 backdrop-blur-md">
2193
+ {colors.map((c) => {
2194
+ const isActive = currentColor.toLowerCase() === c.hex.toLowerCase();
2195
+ return (
2196
+ <button
2197
+ key={c.name}
2198
+ type="button"
2199
+ onClick={() => handleSelectColor(c.hex)}
2200
+ className={cn(
2201
+ 'w-3.5 h-3.5 rounded-full transition-all duration-200 cursor-pointer',
2202
+ isActive
2203
+ ? 'scale-125 ring-2 ring-foreground/40 shadow-xs'
2204
+ : 'hover:scale-110 opacity-70 hover:opacity-100'
2205
+ )}
2206
+ style={{ backgroundColor: c.hex }}
2207
+ title={\`Switch to \${c.name}\`}
2208
+ aria-label={\`Switch glow to \${c.name}\`}
2209
+ />
2210
+ );
2211
+ })}
2212
+ </div>
2213
+ )}
2214
+ </div>
2215
+
2216
+ <div>
2217
+ {title && (
2218
+ <h3 className="text-base sm:text-lg font-bold tracking-tight text-foreground">
2219
+ {title}
2220
+ </h3>
2221
+ )}
2222
+ {description && (
2223
+ <p className="text-xs sm:text-sm text-muted-foreground leading-relaxed mt-1">
2224
+ {description}
2225
+ </p>
2226
+ )}
2227
+ </div>
2228
+ </div>
2229
+
2230
+ <div className="pt-2 flex items-center justify-center">
2231
+ {previewSlot ? (
2232
+ previewSlot
2233
+ ) : (
2234
+ <div
2235
+ className={cn(
2236
+ 'relative p-[1.5px] overflow-hidden transition-all duration-300 w-full max-w-[280px]',
2237
+ radiusConfig.inner
2238
+ )}
2239
+ style={{
2240
+ background: \`linear-gradient(135deg, \${currentColor}, transparent 60%, \${currentColor}90)\`,
2241
+ }}
2242
+ >
2243
+ <div
2244
+ className={cn(
2245
+ 'bg-card/90 dark:bg-card/80 px-4 py-3 flex items-center justify-between backdrop-blur-md shadow-inner',
2246
+ radiusConfig.inner
2247
+ )}
2248
+ >
2249
+ <div className="flex items-center gap-2.5">
2250
+ <div
2251
+ className="w-2.5 h-2.5 rounded-full animate-pulse shrink-0"
2252
+ style={{ backgroundColor: currentColor }}
2253
+ />
2254
+ <span className="text-xs font-mono font-semibold text-foreground">
2255
+ Interactive Aurora Pill
2256
+ </span>
2257
+ </div>
2258
+ <span
2259
+ className="text-[10px] font-mono px-2 py-0.5 rounded-md font-medium border"
2260
+ style={{
2261
+ backgroundColor: \`\${currentColor}12\`,
2262
+ borderColor: \`\${currentColor}30\`,
2263
+ color: currentColor,
2264
+ }}
2265
+ >
2266
+ {currentColor.toUpperCase()}
2267
+ </span>
2268
+ </div>
2269
+ </div>
2270
+ )}
2271
+ </div>
2272
+
2273
+ {footerSlot && <div className="pt-2 border-t border-border/50">{footerSlot}</div>}
2274
+ </div>
2275
+ )}
2276
+ </div>
2277
+ );
2278
+ }
2279
+ );
2280
+
2281
+ AuroraBorderFX.displayName = 'AuroraBorderFX';
834
2282
  `
835
2283
  };
836
2284
 
@@ -840,113 +2288,454 @@ var registry = {
840
2288
  modal,
841
2289
  card,
842
2290
  alert,
843
- badge
2291
+ badge,
2292
+ "morphing-geometry": morphingGeometry,
2293
+ "aurora-border-fx": auroraBorderFX
844
2294
  };
845
2295
 
846
- // src/utils/detect.ts
847
- var fs = __toESM(require("fs"));
848
- var path = __toESM(require("path"));
849
- function detectProject() {
850
- const cwd = process.cwd();
851
- if (fs.existsSync(path.join(cwd, "next.config.ts")) || fs.existsSync(path.join(cwd, "next.config.js")) || fs.existsSync(path.join(cwd, "next.config.mjs"))) {
852
- if (fs.existsSync(path.join(cwd, "app"))) {
853
- return { framework: "Next.js (App Router)", componentsDir: "app/components/ui" };
2296
+ // src/commands/add.ts
2297
+ function askQuestion(query) {
2298
+ const rl = readline.createInterface({
2299
+ input: process.stdin,
2300
+ output: process.stdout
2301
+ });
2302
+ return new Promise(
2303
+ (resolve4) => rl.question(query, (ans) => {
2304
+ rl.close();
2305
+ resolve4(ans);
2306
+ })
2307
+ );
2308
+ }
2309
+ async function addCommand(components, options = {}) {
2310
+ const allRegistryKeys = Object.keys(registry);
2311
+ let targetComponents = [...components];
2312
+ if (options.all || targetComponents.includes("--all")) {
2313
+ targetComponents = allRegistryKeys;
2314
+ console.log(`
2315
+ \x1B[36m\u26A1 Adding all ${targetComponents.length} components from NexoreUI registry...\x1B[0m`);
2316
+ }
2317
+ if (targetComponents.length === 0) {
2318
+ console.error("\x1B[31mError: Please specify components to add or use --all.\x1B[0m");
2319
+ console.log("Example: npx nexoreui add button modal table --all");
2320
+ return;
2321
+ }
2322
+ const project = detectProject(process.cwd());
2323
+ console.log(`
2324
+ \x1B[34mDetected project type:\x1B[0m ${project.projectType.toUpperCase()}`);
2325
+ console.log(`\x1B[34mDetected package manager:\x1B[0m ${project.packageManager}
2326
+ `);
2327
+ let customComponentsDir;
2328
+ let customUtilsFile;
2329
+ try {
2330
+ const configPath = path3.join(project.baseDir, "nexore.json");
2331
+ if (fs3.existsSync(configPath)) {
2332
+ const cfg = JSON.parse(fs3.readFileSync(configPath, "utf8"));
2333
+ if (cfg.aliases?.components) {
2334
+ customComponentsDir = cfg.aliases.components.replace(/^@\//, project.hasSrcDir ? "src/" : "");
2335
+ }
2336
+ if (cfg.aliases?.utils) {
2337
+ const utilBase = cfg.aliases.utils.replace(/^@\//, project.hasSrcDir ? "src/" : "");
2338
+ customUtilsFile = utilBase.endsWith(".ts") || utilBase.endsWith(".js") ? utilBase : `${utilBase}.ts`;
2339
+ }
2340
+ }
2341
+ } catch {
2342
+ }
2343
+ const componentsToInstall = /* @__PURE__ */ new Set();
2344
+ const invalidComponents = [];
2345
+ const queue = [...targetComponents.filter((c) => c !== "--all")];
2346
+ while (queue.length > 0) {
2347
+ const compName = queue.shift();
2348
+ const registryItem = registry[compName];
2349
+ if (!registryItem) {
2350
+ invalidComponents.push(compName);
2351
+ continue;
854
2352
  }
855
- if (fs.existsSync(path.join(cwd, "src", "app"))) {
856
- return { framework: "Next.js (App Router)", componentsDir: "src/app/components/ui" };
2353
+ if (!componentsToInstall.has(compName)) {
2354
+ componentsToInstall.add(compName);
2355
+ if (registryItem.componentsDependencies) {
2356
+ for (const dep of registryItem.componentsDependencies) {
2357
+ queue.push(dep);
2358
+ }
2359
+ }
857
2360
  }
858
- return { framework: "Next.js (Pages Router)", componentsDir: "components/ui" };
859
2361
  }
860
- if (fs.existsSync(path.join(cwd, "vite.config.ts")) || fs.existsSync(path.join(cwd, "vite.config.js"))) {
861
- if (fs.existsSync(path.join(cwd, "src"))) {
862
- return { framework: "Vite", componentsDir: "src/components/ui" };
2362
+ if (invalidComponents.length > 0) {
2363
+ console.error(`\x1B[31mError: Component(s) not found in registry: ${invalidComponents.join(", ")}\x1B[0m`);
2364
+ console.log("Run \x1B[32mnpx nexoreui list\x1B[0m to see all available components.");
2365
+ return;
2366
+ }
2367
+ const defaultComponentsDir = customComponentsDir || (project.hasSrcDir ? "src/components/ui" : "components/ui");
2368
+ const defaultUtilsFile = customUtilsFile || (project.hasSrcDir ? "src/lib/utils.ts" : "lib/utils.ts");
2369
+ let componentsDirInput = defaultComponentsDir;
2370
+ let utilsFileInput = defaultUtilsFile;
2371
+ if (!options.yes && !customComponentsDir) {
2372
+ const compPrompt = await askQuestion(`Where would you like to install the components? (default: ${defaultComponentsDir}): `);
2373
+ componentsDirInput = compPrompt.trim() || defaultComponentsDir;
2374
+ const utilsPrompt = await askQuestion(`Where should we create the utilities file (cn helper)? (default: ${defaultUtilsFile}): `);
2375
+ utilsFileInput = utilsPrompt.trim() || defaultUtilsFile;
2376
+ }
2377
+ const absoluteComponentsDir = path3.resolve(project.baseDir, componentsDirInput);
2378
+ const absoluteUtilsFile = path3.resolve(project.baseDir, utilsFileInput);
2379
+ console.log(`\x1B[33mInstalling components to:\x1B[0m ${absoluteComponentsDir}`);
2380
+ console.log(`\x1B[33mUsing cn helper from:\x1B[0m ${absoluteUtilsFile}
2381
+ `);
2382
+ ensureDir(absoluteComponentsDir);
2383
+ const didCreateCn = ensureCnUtil(absoluteUtilsFile);
2384
+ if (didCreateCn) {
2385
+ console.log(`\x1B[32m\u2714 Created utilities file (cn helper) at:\x1B[0m ${utilsFileInput}`);
2386
+ }
2387
+ const npmDependencies = /* @__PURE__ */ new Set();
2388
+ npmDependencies.add("clsx");
2389
+ npmDependencies.add("tailwind-merge");
2390
+ npmDependencies.add("lucide-react");
2391
+ npmDependencies.add("framer-motion");
2392
+ for (const compName of componentsToInstall) {
2393
+ const registryItem = registry[compName];
2394
+ const targetPath = path3.join(absoluteComponentsDir, registryItem.fileName);
2395
+ copyComponentFile(registryItem.content, targetPath, absoluteUtilsFile);
2396
+ console.log(`\x1B[32m\u2714 Added component:\x1B[0m ${compName} -> ${path3.join(componentsDirInput, registryItem.fileName)}`);
2397
+ registryItem.dependencies.forEach((dep) => npmDependencies.add(dep));
2398
+ }
2399
+ const depsArray = Array.from(npmDependencies);
2400
+ let depsToInstall = [...depsArray];
2401
+ try {
2402
+ const packageJsonPath = path3.join(project.baseDir, "package.json");
2403
+ if (fs3.existsSync(packageJsonPath)) {
2404
+ const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf8"));
2405
+ const existingDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
2406
+ depsToInstall = depsArray.filter((dep) => !existingDeps[dep]);
863
2407
  }
864
- return { framework: "Vite", componentsDir: "components/ui" };
2408
+ } catch {
865
2409
  }
866
- if (fs.existsSync(path.join(cwd, "src"))) {
867
- return { framework: "React", componentsDir: "src/components/ui" };
2410
+ if (depsToInstall.length > 0) {
2411
+ console.log(`
2412
+ \x1B[33mInstalling external dependencies:\x1B[0m ${depsToInstall.join(", ")}...`);
2413
+ let installCmd = "npm install";
2414
+ if (project.packageManager === "pnpm") installCmd = "pnpm add";
2415
+ else if (project.packageManager === "yarn") installCmd = "yarn add";
2416
+ else if (project.packageManager === "bun") installCmd = "bun add";
2417
+ try {
2418
+ (0, import_child_process.execSync)(`${installCmd} ${depsToInstall.join(" ")}`, {
2419
+ stdio: "inherit",
2420
+ cwd: project.baseDir
2421
+ });
2422
+ console.log("\x1B[32m\u2714 Dependencies installed successfully!\x1B[0m");
2423
+ } catch {
2424
+ console.error("\x1B[31mFailed to install dependencies automatically. Please run:\x1B[0m");
2425
+ console.log(` ${installCmd} ${depsToInstall.join(" ")}`);
2426
+ }
868
2427
  }
869
- return { framework: "Unknown", componentsDir: "components/ui" };
2428
+ console.log(`
2429
+ \x1B[32m\x1B[1m\u{1F389} Done! ${componentsToInstall.size} NexoreUI component(s) ready to use.\x1B[0m
2430
+ `);
870
2431
  }
871
2432
 
872
- // src/utils/copy.ts
873
- var fs2 = __toESM(require("fs"));
874
- var path2 = __toESM(require("path"));
875
- function copyComponent(name, entry, outputDir) {
876
- const filePath = path2.join(outputDir, entry.fileName);
877
- const dir = path2.dirname(filePath);
878
- if (!fs2.existsSync(dir)) {
879
- fs2.mkdirSync(dir, { recursive: true });
2433
+ // src/commands/list.ts
2434
+ function listCommand() {
2435
+ console.log("\n\x1B[34m\x1B[1m=== Available NexoreUI Components ===\x1B[0m\n");
2436
+ Object.keys(registry).forEach((name) => {
2437
+ const item = registry[name];
2438
+ console.log(`- \x1B[32m\x1B[1m${name}\x1B[0m (${item.fileName})`);
2439
+ if (item.dependencies.length > 0) {
2440
+ console.log(` \x1B[90mDependencies: ${item.dependencies.join(", ")}\x1B[0m`);
2441
+ }
2442
+ if (item.componentsDependencies && item.componentsDependencies.length > 0) {
2443
+ console.log(` \x1B[33mRequires component: ${item.componentsDependencies.join(", ")}\x1B[0m`);
2444
+ }
2445
+ console.log("");
2446
+ });
2447
+ }
2448
+
2449
+ // src/commands/init.ts
2450
+ var fs5 = __toESM(require("fs"));
2451
+ var path5 = __toESM(require("path"));
2452
+ var readline2 = __toESM(require("readline"));
2453
+
2454
+ // src/utils/config.ts
2455
+ var fs4 = __toESM(require("fs"));
2456
+ var path4 = __toESM(require("path"));
2457
+ var import_child_process2 = require("child_process");
2458
+ var THEME_PALETTES = {
2459
+ indigo: { light: "hsl(250 85% 50%)", dark: "hsl(250 85% 65%)", rgb: "99 60 220" },
2460
+ violet: { light: "hsl(262.1 83.3% 57.8%)", dark: "hsl(263.4 70% 50.4%)", rgb: "139 92 246" },
2461
+ emerald: { light: "hsl(142.1 76.2% 36.3%)", dark: "hsl(142.1 70.6% 45.3%)", rgb: "16 185 129" },
2462
+ rose: { light: "hsl(346.8 77.2% 49.8%)", dark: "hsl(346.8 77.2% 55%)", rgb: "244 63 94" },
2463
+ amber: { light: "hsl(37.7 92.1% 50.2%)", dark: "hsl(37.7 92.1% 55%)", rgb: "245 158 11" },
2464
+ cyan: { light: "hsl(190.4 95% 39%)", dark: "hsl(188.7 94.5% 42.7%)", rgb: "6 182 212" },
2465
+ slate: { light: "hsl(240 5.9% 10%)", dark: "hsl(0 0% 98%)", rgb: "244 244 245" },
2466
+ neon: { light: "hsl(173 80% 40%)", dark: "hsl(173 100% 50%)", rgb: "0 255 220" }
2467
+ };
2468
+ function ensurePathAlias(baseDir, projectType, hasSrcDir) {
2469
+ let updated = false;
2470
+ const tsConfigPath = path4.join(baseDir, "tsconfig.json");
2471
+ const jsConfigPath = path4.join(baseDir, "jsconfig.json");
2472
+ const targetConfig = fs4.existsSync(tsConfigPath) ? tsConfigPath : fs4.existsSync(jsConfigPath) ? jsConfigPath : null;
2473
+ if (targetConfig) {
2474
+ try {
2475
+ const content = fs4.readFileSync(targetConfig, "utf8");
2476
+ const parsed = JSON.parse(content);
2477
+ parsed.compilerOptions = parsed.compilerOptions || {};
2478
+ parsed.compilerOptions.baseUrl = parsed.compilerOptions.baseUrl || ".";
2479
+ parsed.compilerOptions.paths = parsed.compilerOptions.paths || {};
2480
+ const aliasTarget = hasSrcDir ? ["./src/*"] : ["./*"];
2481
+ if (!parsed.compilerOptions.paths["@/*"]) {
2482
+ parsed.compilerOptions.paths["@/*"] = aliasTarget;
2483
+ fs4.writeFileSync(targetConfig, JSON.stringify(parsed, null, 2), "utf8");
2484
+ updated = true;
2485
+ }
2486
+ } catch {
2487
+ }
880
2488
  }
881
- fs2.writeFileSync(filePath, entry.content, "utf-8");
2489
+ if (projectType === "vite") {
2490
+ const viteConfigFiles = ["vite.config.ts", "vite.config.js", "vite.config.mjs"];
2491
+ for (const fileName of viteConfigFiles) {
2492
+ const vitePath = path4.join(baseDir, fileName);
2493
+ if (fs4.existsSync(vitePath)) {
2494
+ let viteContent = fs4.readFileSync(vitePath, "utf8");
2495
+ if (!viteContent.includes("alias") && !viteContent.includes("'@'")) {
2496
+ const hasPathImport = viteContent.includes("from 'path'") || viteContent.includes('from "path"');
2497
+ let headerAdditions = "";
2498
+ if (!hasPathImport) {
2499
+ headerAdditions += `import path from 'path'
2500
+ import { fileURLToPath } from 'url'
2501
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
2502
+ `;
2503
+ }
2504
+ if (viteContent.includes("defineConfig({")) {
2505
+ viteContent = headerAdditions + viteContent.replace(
2506
+ "defineConfig({",
2507
+ `defineConfig({
2508
+ resolve: {
2509
+ alias: {
2510
+ '@': path.resolve(__dirname, './${hasSrcDir ? "src" : "."}'),
2511
+ },
2512
+ },`
2513
+ );
2514
+ fs4.writeFileSync(vitePath, viteContent, "utf8");
2515
+ updated = true;
2516
+ }
2517
+ }
2518
+ break;
2519
+ }
2520
+ }
2521
+ }
2522
+ return updated;
2523
+ }
2524
+ function injectThemeCss(baseDir, cssRelativePath, themeName, radiusValue) {
2525
+ const cssAbsolutePath = path4.join(baseDir, cssRelativePath);
2526
+ const palette = THEME_PALETTES[themeName] || THEME_PALETTES.cyan;
2527
+ const radius = typeof radiusValue === "number" ? radiusValue : parseFloat(radiusValue) || 1;
2528
+ const themeBlock = `
2529
+ @source "../node_modules/nexoreui/dist/**/*.{js,mjs}";
2530
+
2531
+ @theme {
2532
+ --color-background: var(--background);
2533
+ --color-foreground: var(--foreground);
2534
+ --color-card: var(--card);
2535
+ --color-card-foreground: var(--card-foreground);
2536
+ --color-primary: var(--primary);
2537
+ --color-primary-foreground: var(--primary-foreground);
2538
+ --color-border: var(--border);
2539
+ --radius-lg: var(--radius);
2540
+ --radius-md: calc(var(--radius) - 2px);
2541
+ --radius-sm: calc(var(--radius) - 4px);
2542
+ --font-sans: system-ui, -apple-system, sans-serif;
882
2543
  }
883
2544
 
884
- // src/commands/add.ts
885
- async function addCommand(components, options) {
886
- if (components.length === 0) {
887
- console.error("\x1B[31mPlease specify at least one component to add.\x1B[0m");
888
- console.log("Example: npx nexoreui add button modal card");
889
- console.log("Run \x1B[32mnpx nexoreui list\x1B[0m to see all available components.");
890
- process.exit(1);
2545
+ :root {
2546
+ --background: hsl(0 0% 100%);
2547
+ --foreground: hsl(240 10% 3.9%);
2548
+ --card: hsl(0 0% 100%);
2549
+ --card-foreground: hsl(240 10% 3.9%);
2550
+ --primary: ${palette.light};
2551
+ --primary-foreground: hsl(0 0% 100%);
2552
+ --border: hsl(240 5.9% 90%);
2553
+ --radius: ${radius}rem;
2554
+ }
2555
+
2556
+ .dark {
2557
+ --background: hsl(240 10% 3.9%);
2558
+ --foreground: hsl(0 0% 98%);
2559
+ --card: hsl(240 10% 3.9%);
2560
+ --card-foreground: hsl(0 0% 98%);
2561
+ --primary: ${palette.dark};
2562
+ --primary-foreground: hsl(0 0% 100%);
2563
+ --border: hsl(240 3.7% 15.9%);
2564
+ --radius: ${radius}rem;
2565
+ }
2566
+ `;
2567
+ if (fs4.existsSync(cssAbsolutePath)) {
2568
+ const existingContent = fs4.readFileSync(cssAbsolutePath, "utf8");
2569
+ if (!existingContent.includes("--color-primary") && !existingContent.includes("nexoreui/dist")) {
2570
+ fs4.writeFileSync(cssAbsolutePath, existingContent.trim() + "\n" + themeBlock, "utf8");
2571
+ return true;
2572
+ }
2573
+ } else {
2574
+ const cssDir = path4.dirname(cssAbsolutePath);
2575
+ if (!fs4.existsSync(cssDir)) fs4.mkdirSync(cssDir, { recursive: true });
2576
+ fs4.writeFileSync(cssAbsolutePath, `@import "tailwindcss";
2577
+ ` + themeBlock, "utf8");
2578
+ return true;
891
2579
  }
892
- const invalid = components.filter((c) => !registry[c]);
893
- if (invalid.length > 0) {
894
- console.error(`\x1B[31mUnknown component(s): ${invalid.join(", ")}\x1B[0m`);
895
- console.log("Run \x1B[32mnpx nexoreui list\x1B[0m to see all available components.");
896
- process.exit(1);
2580
+ return false;
2581
+ }
2582
+ function installPeerDependencies(baseDir, packageManager, dependencies = ["clsx", "tailwind-merge", "lucide-react", "framer-motion"]) {
2583
+ try {
2584
+ const packageJsonPath = path4.join(baseDir, "package.json");
2585
+ let missingDeps = [...dependencies];
2586
+ if (fs4.existsSync(packageJsonPath)) {
2587
+ const pkg = JSON.parse(fs4.readFileSync(packageJsonPath, "utf8"));
2588
+ const installed = { ...pkg.dependencies, ...pkg.devDependencies };
2589
+ missingDeps = dependencies.filter((dep) => !installed[dep]);
2590
+ }
2591
+ if (missingDeps.length === 0) return true;
2592
+ let installCmd = "npm install";
2593
+ if (packageManager === "pnpm") installCmd = "pnpm add";
2594
+ else if (packageManager === "yarn") installCmd = "yarn add";
2595
+ else if (packageManager === "bun") installCmd = "bun add";
2596
+ console.log(`
2597
+ \x1B[33m\u26A1 Installing peer dependencies:\x1B[0m ${missingDeps.join(", ")}...`);
2598
+ (0, import_child_process2.execSync)(`${installCmd} ${missingDeps.join(" ")}`, {
2599
+ stdio: "inherit",
2600
+ cwd: baseDir
2601
+ });
2602
+ return true;
2603
+ } catch (err) {
2604
+ console.warn("\x1B[33mWarning: Automatic peer dependency installation skipped.\x1B[0m");
2605
+ return false;
897
2606
  }
898
- const projectInfo = detectProject();
899
- const outputDir = path3.resolve(process.cwd(), projectInfo.componentsDir);
2607
+ }
2608
+
2609
+ // src/commands/init.ts
2610
+ function askQuestion2(query) {
2611
+ const rl = readline2.createInterface({
2612
+ input: process.stdin,
2613
+ output: process.stdout
2614
+ });
2615
+ return new Promise(
2616
+ (resolve4) => rl.question(query, (ans) => {
2617
+ rl.close();
2618
+ resolve4(ans);
2619
+ })
2620
+ );
2621
+ }
2622
+ async function initCommand(options = {}) {
900
2623
  console.log(`
901
- \x1B[34m\x1B[1mNexoreUI CLI\x1B[0m`);
902
- console.log(`Detected: \x1B[36m${projectInfo.framework}\x1B[0m project`);
903
- console.log(`Output: \x1B[36m${outputDir}\x1B[0m
2624
+ \x1B[36m\x1B[1m=== Initializing NexoreUI in your project ===\x1B[0m
904
2625
  `);
905
- if (!fs3.existsSync(outputDir)) {
906
- fs3.mkdirSync(outputDir, { recursive: true });
907
- }
908
- let successCount = 0;
909
- for (const name of components) {
910
- const entry = registry[name];
911
- if (!entry) continue;
912
- try {
913
- copyComponent(name, entry, outputDir);
914
- console.log(` \x1B[32m\u2713\x1B[0m ${name} \u2192 ${entry.fileName}`);
915
- successCount++;
916
- } catch (err) {
917
- console.error(` \x1B[31m\u2717\x1B[0m ${name}: ${err.message}`);
2626
+ const project = detectProject(process.cwd());
2627
+ console.log(`\x1B[32m\u2714 Detected Project:\x1B[0m ${project.projectType.toUpperCase()} (${project.packageManager})`);
2628
+ let theme = options.theme || "cyan";
2629
+ let radius = options.radius || "1.0";
2630
+ const defaultComponentsDir = project.hasSrcDir ? "src/components/ui" : "components/ui";
2631
+ const defaultUtilsFile = project.hasSrcDir ? "src/lib/utils.ts" : "lib/utils.ts";
2632
+ const defaultCssFile = project.projectType === "next" ? project.hasSrcDir ? "src/app/globals.css" : "app/globals.css" : project.hasSrcDir ? "src/index.css" : "src/index.css";
2633
+ let componentsDir = defaultComponentsDir;
2634
+ let utilsFile = defaultUtilsFile;
2635
+ if (!options.yes) {
2636
+ if (!options.theme) {
2637
+ const themeAns = await askQuestion2(`Which color theme would you like to use? (cyan, indigo, violet, emerald, rose, amber, slate, neon) [default: cyan]: `);
2638
+ if (themeAns.trim() && THEME_PALETTES[themeAns.trim().toLowerCase()]) {
2639
+ theme = themeAns.trim().toLowerCase();
2640
+ }
918
2641
  }
919
- }
920
- const allDeps = /* @__PURE__ */ new Set();
921
- for (const name of components) {
922
- const entry = registry[name];
923
- if (entry?.dependencies) {
924
- entry.dependencies.forEach((d) => allDeps.add(d));
2642
+ if (!options.radius) {
2643
+ const radiusAns = await askQuestion2(`Which radius value would you like to use? (0, 0.3, 0.5, 0.75, 1.0) [default: 1.0]: `);
2644
+ if (radiusAns.trim()) {
2645
+ radius = radiusAns.trim();
2646
+ }
925
2647
  }
2648
+ const compAns = await askQuestion2(`Where should UI components be created? (default: ${defaultComponentsDir}): `);
2649
+ if (compAns.trim()) componentsDir = compAns.trim();
2650
+ const utilsAns = await askQuestion2(`Where should utility functions (cn helper) be placed? (default: ${defaultUtilsFile}): `);
2651
+ if (utilsAns.trim()) utilsFile = utilsAns.trim();
2652
+ }
2653
+ const absoluteComponentsDir = path5.resolve(project.baseDir, componentsDir);
2654
+ const absoluteUtilsFile = path5.resolve(project.baseDir, utilsFile);
2655
+ ensureDir(absoluteComponentsDir);
2656
+ ensureCnUtil(absoluteUtilsFile);
2657
+ const didUpdateAlias = ensurePathAlias(project.baseDir, project.projectType, project.hasSrcDir);
2658
+ if (didUpdateAlias) {
2659
+ console.log(`\x1B[32m\u2714\x1B[0m Configured path alias \x1B[1m'@/*'\x1B[0m in project config`);
926
2660
  }
2661
+ const didInjectCss = injectThemeCss(project.baseDir, defaultCssFile, theme, radius);
2662
+ if (didInjectCss) {
2663
+ console.log(`\x1B[32m\u2714\x1B[0m Injected Tailwind CSS v4 @theme tokens into \x1B[1m${defaultCssFile}\x1B[0m`);
2664
+ }
2665
+ installPeerDependencies(project.baseDir, project.packageManager);
2666
+ const config = {
2667
+ $schema: "https://nexoreui.site/schema.json",
2668
+ style: "default",
2669
+ theme,
2670
+ radius: Number(radius),
2671
+ framework: project.projectType,
2672
+ packageManager: project.packageManager,
2673
+ font: "system",
2674
+ density: "default",
2675
+ animation: "energetic",
2676
+ defaultMode: "light",
2677
+ tailwind: {
2678
+ config: "tailwind.config.js",
2679
+ css: defaultCssFile,
2680
+ baseColor: "zinc",
2681
+ cssVariables: true
2682
+ },
2683
+ aliases: {
2684
+ components: `@/${componentsDir.replace(/^src\//, "")}`,
2685
+ utils: `@/${utilsFile.replace(/^src\//, "").replace(/\.(ts|js)$/, "")}`
2686
+ }
2687
+ };
2688
+ const configPath = path5.join(project.baseDir, "nexore.json");
2689
+ fs5.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf8");
2690
+ console.log(`\x1B[32m\u2714\x1B[0m Generated \x1B[1mnexore.json\x1B[0m (Theme: ${theme}, Radius: ${radius}rem)`);
2691
+ console.log(`\x1B[32m\u2714\x1B[0m Utilities ready at \x1B[1m${utilsFile}\x1B[0m`);
2692
+ console.log(`\x1B[32m\u2714\x1B[0m Components directory ready at \x1B[1m${componentsDir}\x1B[0m`);
927
2693
  console.log(`
928
- \x1B[32m${successCount} component(s) added successfully.\x1B[0m`);
929
- if (allDeps.size > 0) {
930
- console.log(`
931
- Install peer dependencies:`);
932
- console.log(` \x1B[36mpnpm add ${Array.from(allDeps).join(" ")}\x1B[0m
2694
+ \x1B[32m\x1B[1m\u{1F389} NexoreUI initialized successfully! You can now add components:\x1B[0m`);
2695
+ console.log(` \x1B[36mnpx nexoreui add button card modal table --all\x1B[0m
933
2696
  `);
934
- }
935
2697
  }
936
2698
 
937
- // src/commands/list.ts
938
- function listCommand() {
939
- const names = Object.keys(registry);
2699
+ // src/commands/create.ts
2700
+ var fs6 = __toESM(require("fs"));
2701
+ var path6 = __toESM(require("path"));
2702
+ var import_child_process3 = require("child_process");
2703
+ async function createCommand(projectName, options = {}) {
2704
+ const name = projectName || "my-nexore-app";
2705
+ const targetDir = path6.resolve(process.cwd(), name);
940
2706
  console.log(`
941
- \x1B[34m\x1B[1mNexoreUI \u2014 Available Components\x1B[0m
2707
+ \x1B[36m\x1B[1m\u{1F680} Creating a new NexoreUI Project:\x1B[0m \x1B[32m${name}\x1B[0m
942
2708
  `);
943
- names.forEach((name) => {
944
- const entry = registry[name];
945
- console.log(` \x1B[32m${name.padEnd(15)}\x1B[0m \u2192 ${entry.fileName}`);
2709
+ if (fs6.existsSync(targetDir) && fs6.readdirSync(targetDir).length > 0) {
2710
+ console.error(`\x1B[31mError: Target directory ${name} already exists and is not empty.\x1B[0m`);
2711
+ return;
2712
+ }
2713
+ console.log(`\x1B[33m\u26A1 Scaffolding React + Vite + Tailwind CSS template...\x1B[0m`);
2714
+ try {
2715
+ (0, import_child_process3.execSync)(`npm create vite@latest ${name} -- --template react-ts`, { stdio: "inherit" });
2716
+ } catch (err) {
2717
+ console.error(`\x1B[31mFailed to scaffold Vite project.\x1B[0m`);
2718
+ return;
2719
+ }
2720
+ process.chdir(targetDir);
2721
+ console.log(`
2722
+ \x1B[33m\u{1F4E6} Installing NexoreUI, Tailwind CSS, and core packages...\x1B[0m`);
2723
+ (0, import_child_process3.execSync)(`npm install nexoreui lucide-react clsx tailwind-merge framer-motion @tailwindcss/vite tailwindcss`, {
2724
+ stdio: "inherit"
946
2725
  });
2726
+ await initCommand({
2727
+ yes: true,
2728
+ theme: options.theme || "cyan",
2729
+ radius: options.radius || "1.0"
2730
+ });
2731
+ console.log(`
2732
+ \x1B[32m\x1B[1m\u2728 Project ${name} is ready!\x1B[0m`);
947
2733
  console.log(`
948
- \x1B[90mTotal: ${names.length} components\x1B[0m`);
949
- console.log(`Usage: \x1B[36mnpx nexoreui add ${names[0]}\x1B[0m
2734
+ To get started:
2735
+ `);
2736
+ console.log(` \x1B[36mcd ${name}\x1B[0m`);
2737
+ console.log(` \x1B[36mnpx nexoreui add button card modal table --all\x1B[0m`);
2738
+ console.log(` \x1B[36mnpm run dev\x1B[0m
950
2739
  `);
951
2740
  }
952
2741
 
@@ -958,20 +2747,59 @@ async function main() {
958
2747
  printHelp();
959
2748
  return;
960
2749
  }
961
- if (command === "list") {
2750
+ if (command === "create") {
2751
+ const projectName = args[1] && !args[1].startsWith("-") ? args[1] : void 0;
2752
+ let theme;
2753
+ let radius;
2754
+ for (let i = 1; i < args.length; i++) {
2755
+ const arg = args[i];
2756
+ if (arg === "--theme" && args[i + 1]) {
2757
+ theme = args[++i];
2758
+ } else if (arg.startsWith("--theme=")) {
2759
+ theme = arg.split("=")[1];
2760
+ } else if (arg === "--radius" && args[i + 1]) {
2761
+ radius = args[++i];
2762
+ } else if (arg.startsWith("--radius=")) {
2763
+ radius = arg.split("=")[1];
2764
+ }
2765
+ }
2766
+ await createCommand(projectName, { theme, radius });
2767
+ } else if (command === "init") {
2768
+ let yes = false;
2769
+ let theme;
2770
+ let radius;
2771
+ for (let i = 1; i < args.length; i++) {
2772
+ const arg = args[i];
2773
+ if (arg === "-y" || arg === "--yes") {
2774
+ yes = true;
2775
+ } else if (arg === "--theme" && args[i + 1]) {
2776
+ theme = args[++i];
2777
+ } else if (arg.startsWith("--theme=")) {
2778
+ theme = arg.split("=")[1];
2779
+ } else if (arg === "--radius" && args[i + 1]) {
2780
+ radius = args[++i];
2781
+ } else if (arg.startsWith("--radius=")) {
2782
+ radius = arg.split("=")[1];
2783
+ }
2784
+ }
2785
+ await initCommand({ yes, theme, radius });
2786
+ } else if (command === "list") {
962
2787
  listCommand();
963
2788
  } else if (command === "add") {
964
2789
  const components = [];
965
2790
  let yes = false;
2791
+ let all = false;
966
2792
  for (let i = 1; i < args.length; i++) {
967
2793
  const arg = args[i];
968
2794
  if (arg === "-y" || arg === "--yes") {
969
2795
  yes = true;
2796
+ } else if (arg === "--all" || arg === "-a") {
2797
+ all = true;
970
2798
  } else if (!arg.startsWith("-")) {
971
2799
  components.push(arg);
972
2800
  }
973
2801
  }
974
- await addCommand(components, { yes });
2802
+ await addCommand(components, { yes, all });
975
2803
  } else {
976
2804
  console.error(`\x1B[31mUnknown command: ${command}\x1B[0m`);
977
2805
  printHelp();
@@ -979,16 +2807,23 @@ async function main() {
979
2807
  }
980
2808
  function printHelp() {
981
2809
  console.log(`
982
- \x1B[34m\x1B[1mNexoreUI CLI\x1B[0m
2810
+ \x1B[36m\x1B[1mNexoreUI CLI\x1B[0m
2811
+ \x1B[90mModern, animated, production-ready React components with Tailwind CSS v4\x1B[0m
2812
+
983
2813
  Usage:
984
2814
  npx nexoreui [command] [options]
985
2815
 
986
2816
  Commands:
987
- \x1B[32madd [components...]\x1B[0m Add components to your project (e.g., button, modal, card, alert, badge)
988
- \x1B[32mlist\x1B[0m List all available components
2817
+ \x1B[32mcreate [name]\x1B[0m Create a new fully configured NexoreUI starter project
2818
+ \x1B[32minit\x1B[0m Initialize NexoreUI in your project (configure theme, aliases, and CSS)
2819
+ \x1B[32madd [components...]\x1B[0m Add components to your project (use --all to install all 40+ components)
2820
+ \x1B[32mlist\x1B[0m List all available components in registry
989
2821
 
990
2822
  Options:
991
- \x1B[33m-y, --yes\x1B[0m Skip prompts and use default paths
2823
+ \x1B[33m--theme <name>\x1B[0m Set color palette (cyan, indigo, violet, emerald, rose, amber, slate, neon)
2824
+ \x1B[33m--radius <val>\x1B[0m Set border radius (0, 0.3, 0.5, 0.75, 1.0)
2825
+ \x1B[33m--all, -a\x1B[0m Install all available components at once
2826
+ \x1B[33m-y, --yes\x1B[0m Skip prompts and use defaults automatically
992
2827
  \x1B[33m-h, --help\x1B[0m Show help information
993
2828
  `);
994
2829
  }