ucode-agent 1.4.0 → 1.6.0

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.
Files changed (76) hide show
  1. package/README.md +64 -4
  2. package/package.json +5 -2
  3. package/skills/build-app/SKILL.md +19 -1
  4. package/skills/ui-ux/SKILL.md +2 -2
  5. package/src/core/doctor.js +122 -0
  6. package/src/core/loop.js +303 -12
  7. package/src/core/provider.js +93 -10
  8. package/src/core/stuck.js +269 -0
  9. package/src/tools/browser.js +121 -59
  10. package/src/tools/deploy.js +283 -0
  11. package/src/tools/files.js +91 -8
  12. package/src/tools/index.js +65 -8
  13. package/src/tools/scaffold.js +190 -0
  14. package/src/tools/shell.js +89 -1
  15. package/src/ui/activity.js +203 -0
  16. package/src/ui/plain.js +22 -3
  17. package/src/ui/screen.js +65 -19
  18. package/templates/next-shadcn/AGENTS.md +9 -0
  19. package/templates/next-shadcn/README.md +9 -0
  20. package/templates/next-shadcn/TEMPLATE.md +83 -0
  21. package/templates/next-shadcn/_gitignore +41 -0
  22. package/templates/next-shadcn/_package-lock.json +11465 -0
  23. package/templates/next-shadcn/components.json +25 -0
  24. package/templates/next-shadcn/eslint.config.mjs +18 -0
  25. package/templates/next-shadcn/next-env.d.ts +5 -0
  26. package/templates/next-shadcn/next.config.ts +8 -0
  27. package/templates/next-shadcn/package.json +39 -0
  28. package/templates/next-shadcn/postcss.config.mjs +7 -0
  29. package/templates/next-shadcn/presets/citrus.json +77 -0
  30. package/templates/next-shadcn/presets/graphite.json +77 -0
  31. package/templates/next-shadcn/presets/grove.json +77 -0
  32. package/templates/next-shadcn/presets/ocean.json +78 -0
  33. package/templates/next-shadcn/presets/sunset.json +77 -0
  34. package/templates/next-shadcn/presets/violet.json +77 -0
  35. package/templates/next-shadcn/src/app/favicon.ico +0 -0
  36. package/templates/next-shadcn/src/app/globals.css +155 -0
  37. package/templates/next-shadcn/src/app/layout.tsx +42 -0
  38. package/templates/next-shadcn/src/app/page.tsx +14 -0
  39. package/templates/next-shadcn/src/components/theme-provider.tsx +7 -0
  40. package/templates/next-shadcn/src/components/theme-toggle.tsx +27 -0
  41. package/templates/next-shadcn/src/components/ui/accordion.tsx +80 -0
  42. package/templates/next-shadcn/src/components/ui/alert-dialog.tsx +199 -0
  43. package/templates/next-shadcn/src/components/ui/avatar.tsx +111 -0
  44. package/templates/next-shadcn/src/components/ui/badge.tsx +48 -0
  45. package/templates/next-shadcn/src/components/ui/button.tsx +66 -0
  46. package/templates/next-shadcn/src/components/ui/calendar.tsx +222 -0
  47. package/templates/next-shadcn/src/components/ui/card.tsx +102 -0
  48. package/templates/next-shadcn/src/components/ui/checkbox.tsx +32 -0
  49. package/templates/next-shadcn/src/components/ui/collapsible.tsx +33 -0
  50. package/templates/next-shadcn/src/components/ui/command.tsx +195 -0
  51. package/templates/next-shadcn/src/components/ui/dialog.tsx +168 -0
  52. package/templates/next-shadcn/src/components/ui/dropdown-menu.tsx +268 -0
  53. package/templates/next-shadcn/src/components/ui/hover-card.tsx +43 -0
  54. package/templates/next-shadcn/src/components/ui/input-group.tsx +156 -0
  55. package/templates/next-shadcn/src/components/ui/input.tsx +18 -0
  56. package/templates/next-shadcn/src/components/ui/label.tsx +23 -0
  57. package/templates/next-shadcn/src/components/ui/popover.tsx +88 -0
  58. package/templates/next-shadcn/src/components/ui/progress.tsx +30 -0
  59. package/templates/next-shadcn/src/components/ui/radio-group.tsx +43 -0
  60. package/templates/next-shadcn/src/components/ui/scroll-area.tsx +54 -0
  61. package/templates/next-shadcn/src/components/ui/select.tsx +191 -0
  62. package/templates/next-shadcn/src/components/ui/separator.tsx +27 -0
  63. package/templates/next-shadcn/src/components/ui/sheet.tsx +147 -0
  64. package/templates/next-shadcn/src/components/ui/skeleton.tsx +13 -0
  65. package/templates/next-shadcn/src/components/ui/slider.tsx +58 -0
  66. package/templates/next-shadcn/src/components/ui/sonner.tsx +49 -0
  67. package/templates/next-shadcn/src/components/ui/switch.tsx +32 -0
  68. package/templates/next-shadcn/src/components/ui/table.tsx +115 -0
  69. package/templates/next-shadcn/src/components/ui/tabs.tsx +89 -0
  70. package/templates/next-shadcn/src/components/ui/textarea.tsx +17 -0
  71. package/templates/next-shadcn/src/components/ui/toggle-group.tsx +89 -0
  72. package/templates/next-shadcn/src/components/ui/toggle.tsx +46 -0
  73. package/templates/next-shadcn/src/components/ui/tooltip.tsx +56 -0
  74. package/templates/next-shadcn/src/lib/utils.ts +6 -0
  75. package/templates/next-shadcn/tsconfig.json +34 -0
  76. package/ucode.js +8 -1
@@ -0,0 +1,155 @@
1
+ /*
2
+ * Design tokens. This palette is a considered starting point, not a finished
3
+ * look: re-tint --primary, --accent and the neutrals (the third number in each
4
+ * oklch() is the hue) so each app has its own direction.
5
+ */
6
+ @import "tailwindcss";
7
+ @import "tw-animate-css";
8
+ @import "shadcn/tailwind.css";
9
+
10
+ @custom-variant dark (&:is(.dark *));
11
+
12
+ @theme inline {
13
+ --color-background: var(--background);
14
+ --color-foreground: var(--foreground);
15
+ --font-sans: var(--font-sans);
16
+ --font-mono: var(--font-geist-mono);
17
+ --font-heading: var(--font-sans);
18
+ --color-sidebar-ring: var(--sidebar-ring);
19
+ --color-sidebar-border: var(--sidebar-border);
20
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
21
+ --color-sidebar-accent: var(--sidebar-accent);
22
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
23
+ --color-sidebar-primary: var(--sidebar-primary);
24
+ --color-sidebar-foreground: var(--sidebar-foreground);
25
+ --color-sidebar: var(--sidebar);
26
+ --color-chart-5: var(--chart-5);
27
+ --color-chart-4: var(--chart-4);
28
+ --color-chart-3: var(--chart-3);
29
+ --color-chart-2: var(--chart-2);
30
+ --color-chart-1: var(--chart-1);
31
+ --color-ring: var(--ring);
32
+ --color-input: var(--input);
33
+ --color-border: var(--border);
34
+ --color-destructive: var(--destructive);
35
+ --color-success: var(--success);
36
+ --color-warning: var(--warning);
37
+ --color-accent-foreground: var(--accent-foreground);
38
+ --color-accent: var(--accent);
39
+ --color-muted-foreground: var(--muted-foreground);
40
+ --color-muted: var(--muted);
41
+ --color-secondary-foreground: var(--secondary-foreground);
42
+ --color-secondary: var(--secondary);
43
+ --color-primary-foreground: var(--primary-foreground);
44
+ --color-primary: var(--primary);
45
+ --color-popover-foreground: var(--popover-foreground);
46
+ --color-popover: var(--popover);
47
+ --color-card-foreground: var(--card-foreground);
48
+ --color-card: var(--card);
49
+ --radius-sm: calc(var(--radius) * 0.6);
50
+ --radius-md: calc(var(--radius) * 0.8);
51
+ --radius-lg: var(--radius);
52
+ --radius-xl: calc(var(--radius) * 1.4);
53
+ --radius-2xl: calc(var(--radius) * 1.8);
54
+ --radius-3xl: calc(var(--radius) * 2.2);
55
+ --radius-4xl: calc(var(--radius) * 2.6);
56
+ }
57
+
58
+ :root {
59
+ --background: oklch(0.985 0.004 255);
60
+ --foreground: oklch(0.21 0.02 262);
61
+ --card: oklch(1 0 0);
62
+ --card-foreground: oklch(0.21 0.02 262);
63
+ --popover: oklch(1 0 0);
64
+ --popover-foreground: oklch(0.21 0.02 262);
65
+ --primary: oklch(0.53 0.2 264);
66
+ --primary-foreground: oklch(0.99 0 0);
67
+ --secondary: oklch(0.955 0.008 255);
68
+ --secondary-foreground: oklch(0.27 0.03 262);
69
+ --muted: oklch(0.96 0.006 255);
70
+ --muted-foreground: oklch(0.52 0.02 262);
71
+ --accent: oklch(0.95 0.025 264);
72
+ --accent-foreground: oklch(0.36 0.13 264);
73
+ --destructive: oklch(0.577 0.215 27);
74
+ --success: oklch(0.6 0.15 155);
75
+ --warning: oklch(0.74 0.15 72);
76
+ --border: oklch(0.915 0.008 255);
77
+ --input: oklch(0.9 0.01 255);
78
+ --ring: oklch(0.53 0.2 264);
79
+ --chart-1: oklch(0.53 0.2 264);
80
+ --chart-2: oklch(0.6 0.15 155);
81
+ --chart-3: oklch(0.74 0.15 72);
82
+ --chart-4: oklch(0.62 0.19 330);
83
+ --chart-5: oklch(0.52 0.02 262);
84
+ --radius: 0.75rem;
85
+ --sidebar: oklch(0.975 0.006 255);
86
+ --sidebar-foreground: oklch(0.21 0.02 262);
87
+ --sidebar-primary: oklch(0.53 0.2 264);
88
+ --sidebar-primary-foreground: oklch(0.99 0 0);
89
+ --sidebar-accent: oklch(0.95 0.025 264);
90
+ --sidebar-accent-foreground: oklch(0.36 0.13 264);
91
+ --sidebar-border: oklch(0.915 0.008 255);
92
+ --sidebar-ring: oklch(0.53 0.2 264);
93
+ }
94
+
95
+ .dark {
96
+ --background: oklch(0.165 0.012 262);
97
+ --foreground: oklch(0.95 0.008 262);
98
+ --card: oklch(0.2 0.014 262);
99
+ --card-foreground: oklch(0.95 0.008 262);
100
+ --popover: oklch(0.215 0.015 262);
101
+ --popover-foreground: oklch(0.95 0.008 262);
102
+ --primary: oklch(0.7 0.16 264);
103
+ --primary-foreground: oklch(0.17 0.02 262);
104
+ --secondary: oklch(0.25 0.015 262);
105
+ --secondary-foreground: oklch(0.93 0.01 262);
106
+ --muted: oklch(0.24 0.014 262);
107
+ --muted-foreground: oklch(0.7 0.02 262);
108
+ --accent: oklch(0.28 0.045 264);
109
+ --accent-foreground: oklch(0.9 0.05 264);
110
+ --destructive: oklch(0.7 0.19 22);
111
+ --success: oklch(0.72 0.15 155);
112
+ --warning: oklch(0.8 0.14 75);
113
+ --border: oklch(1 0 0 / 9%);
114
+ --input: oklch(1 0 0 / 13%);
115
+ --ring: oklch(0.7 0.16 264);
116
+ --chart-1: oklch(0.7 0.16 264);
117
+ --chart-2: oklch(0.72 0.15 155);
118
+ --chart-3: oklch(0.8 0.14 75);
119
+ --chart-4: oklch(0.72 0.17 330);
120
+ --chart-5: oklch(0.7 0.02 262);
121
+ --sidebar: oklch(0.185 0.013 262);
122
+ --sidebar-foreground: oklch(0.95 0.008 262);
123
+ --sidebar-primary: oklch(0.7 0.16 264);
124
+ --sidebar-primary-foreground: oklch(0.17 0.02 262);
125
+ --sidebar-accent: oklch(0.28 0.045 264);
126
+ --sidebar-accent-foreground: oklch(0.9 0.05 264);
127
+ --sidebar-border: oklch(1 0 0 / 9%);
128
+ --sidebar-ring: oklch(0.7 0.16 264);
129
+ }
130
+
131
+ @layer base {
132
+ * {
133
+ @apply border-border outline-ring/50;
134
+ }
135
+ body {
136
+ @apply bg-background text-foreground;
137
+ }
138
+ html {
139
+ @apply font-sans;
140
+ }
141
+ h1, h2, h3 {
142
+ letter-spacing: -0.02em;
143
+ text-wrap: balance;
144
+ }
145
+ p {
146
+ text-wrap: pretty;
147
+ }
148
+ }
149
+
150
+ @media (prefers-reduced-motion: reduce) {
151
+ *, *::before, *::after {
152
+ animation-duration: 1ms !important;
153
+ transition-duration: 1ms !important;
154
+ }
155
+ }
@@ -0,0 +1,42 @@
1
+ import type { Metadata } from "next";
2
+ import { Geist, Geist_Mono } from "next/font/google";
3
+ import { ThemeProvider } from "@/components/theme-provider";
4
+ import { Toaster } from "@/components/ui/sonner";
5
+ import { TooltipProvider } from "@/components/ui/tooltip";
6
+ import "./globals.css";
7
+
8
+ // Registered as --font-sans, which is the variable globals.css and every
9
+ // shadcn component read. Swap Geist for the font the app's direction calls for.
10
+ const sans = Geist({
11
+ variable: "--font-sans",
12
+ subsets: ["latin"],
13
+ });
14
+
15
+ const mono = Geist_Mono({
16
+ variable: "--font-geist-mono",
17
+ subsets: ["latin"],
18
+ });
19
+
20
+ export const metadata: Metadata = {
21
+ title: "__APP_NAME__",
22
+ description: "__APP_DESCRIPTION__",
23
+ };
24
+
25
+ export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
26
+ return (
27
+ <html
28
+ lang="en"
29
+ suppressHydrationWarning
30
+ className={`${sans.variable} ${mono.variable} h-full antialiased`}
31
+ >
32
+ <body className="min-h-full flex flex-col">
33
+ <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
34
+ <TooltipProvider>
35
+ {children}
36
+ <Toaster richColors closeButton />
37
+ </TooltipProvider>
38
+ </ThemeProvider>
39
+ </body>
40
+ </html>
41
+ );
42
+ }
@@ -0,0 +1,14 @@
1
+ import { ThemeToggle } from "@/components/theme-toggle";
2
+
3
+ // Placeholder — replace this whole page with the app.
4
+ export default function Home() {
5
+ return (
6
+ <main className="flex flex-1 items-center justify-center p-8">
7
+ <div className="space-y-3 text-center">
8
+ <h1 className="text-3xl font-semibold tracking-tight">__APP_NAME__</h1>
9
+ <p className="text-muted-foreground">Replace src/app/page.tsx with the app.</p>
10
+ <ThemeToggle />
11
+ </div>
12
+ </main>
13
+ );
14
+ }
@@ -0,0 +1,7 @@
1
+ "use client";
2
+
3
+ import { ThemeProvider as NextThemesProvider } from "next-themes";
4
+
5
+ export function ThemeProvider({ children, ...props }: React.ComponentProps<typeof NextThemesProvider>) {
6
+ return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
7
+ }
@@ -0,0 +1,27 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { useTheme } from "next-themes";
5
+ import { MoonIcon, SunIcon } from "lucide-react";
6
+ import { Button } from "@/components/ui/button";
7
+
8
+ /** Light / dark switch. Renders a same-size placeholder until mounted, so nothing shifts. */
9
+ export function ThemeToggle() {
10
+ const { resolvedTheme, setTheme } = useTheme();
11
+ const [mounted, setMounted] = useState(false);
12
+ useEffect(() => setMounted(true), []);
13
+
14
+ if (!mounted) return <span aria-hidden className="inline-block size-8" />;
15
+
16
+ const dark = resolvedTheme === "dark";
17
+ return (
18
+ <Button
19
+ variant="ghost"
20
+ size="icon"
21
+ aria-label={dark ? "Switch to light mode" : "Switch to dark mode"}
22
+ onClick={() => setTheme(dark ? "light" : "dark")}
23
+ >
24
+ {dark ? <SunIcon /> : <MoonIcon />}
25
+ </Button>
26
+ );
27
+ }
@@ -0,0 +1,80 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "cn"
5
+ import { Accordion as AccordionPrimitive } from "radix-ui"
6
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
7
+
8
+ function Accordion({
9
+ className,
10
+ ...props
11
+ }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
12
+ return (
13
+ <AccordionPrimitive.Root
14
+ data-slot="accordion"
15
+ className={cn("flex w-full flex-col", className)}
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+
21
+ function AccordionItem({
22
+ className,
23
+ ...props
24
+ }: React.ComponentProps<typeof AccordionPrimitive.Item>) {
25
+ return (
26
+ <AccordionPrimitive.Item
27
+ data-slot="accordion-item"
28
+ className={cn("not-last:border-b", className)}
29
+ {...props}
30
+ />
31
+ )
32
+ }
33
+
34
+ function AccordionTrigger({
35
+ className,
36
+ children,
37
+ ...props
38
+ }: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
39
+ return (
40
+ <AccordionPrimitive.Header className="flex">
41
+ <AccordionPrimitive.Trigger
42
+ data-slot="accordion-trigger"
43
+ className={cn(
44
+ "group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring disabled:pointer-events-none disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
45
+ className
46
+ )}
47
+ {...props}
48
+ >
49
+ {children}
50
+ <ChevronDownIcon data-slot="accordion-trigger-icon" className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" />
51
+ <ChevronUpIcon data-slot="accordion-trigger-icon" className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" />
52
+ </AccordionPrimitive.Trigger>
53
+ </AccordionPrimitive.Header>
54
+ )
55
+ }
56
+
57
+ function AccordionContent({
58
+ className,
59
+ children,
60
+ ...props
61
+ }: React.ComponentProps<typeof AccordionPrimitive.Content>) {
62
+ return (
63
+ <AccordionPrimitive.Content
64
+ data-slot="accordion-content"
65
+ className="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up"
66
+ {...props}
67
+ >
68
+ <div
69
+ className={cn(
70
+ "h-(--radix-accordion-content-height) pt-0 pb-2.5 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
71
+ className
72
+ )}
73
+ >
74
+ {children}
75
+ </div>
76
+ </AccordionPrimitive.Content>
77
+ )
78
+ }
79
+
80
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
@@ -0,0 +1,199 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "cn"
5
+ import { AlertDialog as AlertDialogPrimitive } from "radix-ui"
6
+
7
+ import { Button } from "@/components/ui/button"
8
+
9
+ function AlertDialog({
10
+ ...props
11
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
12
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
13
+ }
14
+
15
+ function AlertDialogTrigger({
16
+ ...props
17
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
18
+ return (
19
+ <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
20
+ )
21
+ }
22
+
23
+ function AlertDialogPortal({
24
+ ...props
25
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
26
+ return (
27
+ <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
28
+ )
29
+ }
30
+
31
+ function AlertDialogOverlay({
32
+ className,
33
+ ...props
34
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
35
+ return (
36
+ <AlertDialogPrimitive.Overlay
37
+ data-slot="alert-dialog-overlay"
38
+ className={cn(
39
+ "fixed inset-0 z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
40
+ className
41
+ )}
42
+ {...props}
43
+ />
44
+ )
45
+ }
46
+
47
+ function AlertDialogContent({
48
+ className,
49
+ size = "default",
50
+ ...props
51
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
52
+ size?: "default" | "sm"
53
+ }) {
54
+ return (
55
+ <AlertDialogPortal>
56
+ <AlertDialogOverlay />
57
+ <AlertDialogPrimitive.Content
58
+ data-slot="alert-dialog-content"
59
+ data-size={size}
60
+ className={cn(
61
+ "group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
62
+ className
63
+ )}
64
+ {...props}
65
+ />
66
+ </AlertDialogPortal>
67
+ )
68
+ }
69
+
70
+ function AlertDialogHeader({
71
+ className,
72
+ ...props
73
+ }: React.ComponentProps<"div">) {
74
+ return (
75
+ <div
76
+ data-slot="alert-dialog-header"
77
+ className={cn(
78
+ "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
79
+ className
80
+ )}
81
+ {...props}
82
+ />
83
+ )
84
+ }
85
+
86
+ function AlertDialogFooter({
87
+ className,
88
+ ...props
89
+ }: React.ComponentProps<"div">) {
90
+ return (
91
+ <div
92
+ data-slot="alert-dialog-footer"
93
+ className={cn(
94
+ "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
95
+ className
96
+ )}
97
+ {...props}
98
+ />
99
+ )
100
+ }
101
+
102
+ function AlertDialogMedia({
103
+ className,
104
+ ...props
105
+ }: React.ComponentProps<"div">) {
106
+ return (
107
+ <div
108
+ data-slot="alert-dialog-media"
109
+ className={cn(
110
+ "mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
111
+ className
112
+ )}
113
+ {...props}
114
+ />
115
+ )
116
+ }
117
+
118
+ function AlertDialogTitle({
119
+ className,
120
+ ...props
121
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
122
+ return (
123
+ <AlertDialogPrimitive.Title
124
+ data-slot="alert-dialog-title"
125
+ className={cn(
126
+ "font-heading text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
127
+ className
128
+ )}
129
+ {...props}
130
+ />
131
+ )
132
+ }
133
+
134
+ function AlertDialogDescription({
135
+ className,
136
+ ...props
137
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
138
+ return (
139
+ <AlertDialogPrimitive.Description
140
+ data-slot="alert-dialog-description"
141
+ className={cn(
142
+ "text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
143
+ className
144
+ )}
145
+ {...props}
146
+ />
147
+ )
148
+ }
149
+
150
+ function AlertDialogAction({
151
+ className,
152
+ variant = "default",
153
+ size = "default",
154
+ ...props
155
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
156
+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
157
+ return (
158
+ <Button variant={variant} size={size} asChild>
159
+ <AlertDialogPrimitive.Action
160
+ data-slot="alert-dialog-action"
161
+ className={cn(className)}
162
+ {...props}
163
+ />
164
+ </Button>
165
+ )
166
+ }
167
+
168
+ function AlertDialogCancel({
169
+ className,
170
+ variant = "outline",
171
+ size = "default",
172
+ ...props
173
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
174
+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
175
+ return (
176
+ <Button variant={variant} size={size} asChild>
177
+ <AlertDialogPrimitive.Cancel
178
+ data-slot="alert-dialog-cancel"
179
+ className={cn(className)}
180
+ {...props}
181
+ />
182
+ </Button>
183
+ )
184
+ }
185
+
186
+ export {
187
+ AlertDialog,
188
+ AlertDialogAction,
189
+ AlertDialogCancel,
190
+ AlertDialogContent,
191
+ AlertDialogDescription,
192
+ AlertDialogFooter,
193
+ AlertDialogHeader,
194
+ AlertDialogMedia,
195
+ AlertDialogOverlay,
196
+ AlertDialogPortal,
197
+ AlertDialogTitle,
198
+ AlertDialogTrigger,
199
+ }
@@ -0,0 +1,111 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "cn"
5
+ import { Avatar as AvatarPrimitive } from "radix-ui"
6
+
7
+ function Avatar({
8
+ className,
9
+ size = "default",
10
+ ...props
11
+ }: React.ComponentProps<typeof AvatarPrimitive.Root> & {
12
+ size?: "default" | "sm" | "lg"
13
+ }) {
14
+ return (
15
+ <AvatarPrimitive.Root
16
+ data-slot="avatar"
17
+ data-size={size}
18
+ className={cn(
19
+ "group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
20
+ className
21
+ )}
22
+ {...props}
23
+ />
24
+ )
25
+ }
26
+
27
+ function AvatarImage({
28
+ className,
29
+ ...props
30
+ }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
31
+ return (
32
+ <AvatarPrimitive.Image
33
+ data-slot="avatar-image"
34
+ className={cn(
35
+ "aspect-square size-full rounded-full object-cover",
36
+ className
37
+ )}
38
+ {...props}
39
+ />
40
+ )
41
+ }
42
+
43
+ function AvatarFallback({
44
+ className,
45
+ ...props
46
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
47
+ return (
48
+ <AvatarPrimitive.Fallback
49
+ data-slot="avatar-fallback"
50
+ className={cn(
51
+ "flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
52
+ className
53
+ )}
54
+ {...props}
55
+ />
56
+ )
57
+ }
58
+
59
+ function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
60
+ return (
61
+ <span
62
+ data-slot="avatar-badge"
63
+ className={cn(
64
+ "absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none",
65
+ "group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
66
+ "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
67
+ "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
68
+ className
69
+ )}
70
+ {...props}
71
+ />
72
+ )
73
+ }
74
+
75
+ function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
76
+ return (
77
+ <div
78
+ data-slot="avatar-group"
79
+ className={cn(
80
+ "group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
81
+ className
82
+ )}
83
+ {...props}
84
+ />
85
+ )
86
+ }
87
+
88
+ function AvatarGroupCount({
89
+ className,
90
+ ...props
91
+ }: React.ComponentProps<"div">) {
92
+ return (
93
+ <div
94
+ data-slot="avatar-group-count"
95
+ className={cn(
96
+ "relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
97
+ className
98
+ )}
99
+ {...props}
100
+ />
101
+ )
102
+ }
103
+
104
+ export {
105
+ Avatar,
106
+ AvatarImage,
107
+ AvatarFallback,
108
+ AvatarGroup,
109
+ AvatarGroupCount,
110
+ AvatarBadge,
111
+ }
@@ -0,0 +1,48 @@
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+ import { cn } from "cn"
4
+ import { Slot } from "radix-ui"
5
+
6
+ const badgeVariants = cva(
7
+ "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
12
+ secondary:
13
+ "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
14
+ destructive:
15
+ "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
16
+ outline:
17
+ "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
18
+ ghost:
19
+ "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
20
+ link: "text-primary underline-offset-4 hover:underline",
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ variant: "default",
25
+ },
26
+ }
27
+ )
28
+
29
+ function Badge({
30
+ className,
31
+ variant = "default",
32
+ asChild = false,
33
+ ...props
34
+ }: React.ComponentProps<"span"> &
35
+ VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
36
+ const Comp = asChild ? Slot.Root : "span"
37
+
38
+ return (
39
+ <Comp
40
+ data-slot="badge"
41
+ data-variant={variant}
42
+ className={cn(badgeVariants({ variant }), className)}
43
+ {...props}
44
+ />
45
+ )
46
+ }
47
+
48
+ export { Badge, badgeVariants }