larkup 0.1.14

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 (141) hide show
  1. package/.env +2 -0
  2. package/app/actions/vercel.ts +417 -0
  3. package/app/api/chat/route.ts +192 -0
  4. package/app/api/chat/status/route.ts +55 -0
  5. package/app/api/chat/suggestions/route.ts +115 -0
  6. package/app/api/config/route.ts +73 -0
  7. package/app/api/config/test/route.ts +67 -0
  8. package/app/api/config/test-embedding/route.ts +40 -0
  9. package/app/api/config/test-llm/route.ts +47 -0
  10. package/app/api/config/test-provider/route.ts +36 -0
  11. package/app/api/demo/route.ts +176 -0
  12. package/app/api/deploy/ssh/route.ts +340 -0
  13. package/app/api/documents/route.ts +90 -0
  14. package/app/api/firecrawl/local/route.ts +46 -0
  15. package/app/api/index/route.ts +113 -0
  16. package/app/api/jobs/[id]/route.ts +44 -0
  17. package/app/api/jobs/route.ts +116 -0
  18. package/app/api/parse-file/route.ts +39 -0
  19. package/app/api/search/google/route.ts +162 -0
  20. package/app/api/search/route.ts +29 -0
  21. package/app/api/server/download/route.ts +112 -0
  22. package/app/api/server/generate/route.ts +46 -0
  23. package/app/api/server/local/route.ts +42 -0
  24. package/app/api/servers/route.ts +137 -0
  25. package/app/api/vector-stores/install/route.ts +65 -0
  26. package/app/api/vector-stores/status/route.ts +26 -0
  27. package/app/chat/page.tsx +15 -0
  28. package/app/configure/page.tsx +53 -0
  29. package/app/data/page.tsx +15 -0
  30. package/app/demo/page.tsx +15 -0
  31. package/app/favicon.ico +0 -0
  32. package/app/globals.css +190 -0
  33. package/app/index-data/page.tsx +15 -0
  34. package/app/layout.tsx +51 -0
  35. package/app/page.tsx +34 -0
  36. package/app/server/page.tsx +22 -0
  37. package/app/simple/chat/page.tsx +17 -0
  38. package/app/simple/docs/page.tsx +21 -0
  39. package/app/simple/layout.tsx +9 -0
  40. package/app/simple/settings/page.tsx +627 -0
  41. package/app/template.tsx +16 -0
  42. package/app/themes.css +733 -0
  43. package/components/alerts/generic-alert.tsx +74 -0
  44. package/components/app-sidebar.tsx +187 -0
  45. package/components/app-topnav.tsx +195 -0
  46. package/components/chat/chat-settings-modal.tsx +209 -0
  47. package/components/chat/chat-workspace.tsx +348 -0
  48. package/components/chat/knowledge-base-result.tsx +97 -0
  49. package/components/chat/message-item.tsx +103 -0
  50. package/components/client-layout-wrapper.tsx +182 -0
  51. package/components/configure/configure-form.tsx +1683 -0
  52. package/components/configure/store-fields.tsx +152 -0
  53. package/components/data/corpus-panel.tsx +499 -0
  54. package/components/data/data-workspace.tsx +272 -0
  55. package/components/data/firecrawl-notice.tsx +236 -0
  56. package/components/data/jobs-panel.tsx +485 -0
  57. package/components/data/paste-panel.tsx +79 -0
  58. package/components/data/scrape-panel.tsx +964 -0
  59. package/components/data/upload-panel.tsx +714 -0
  60. package/components/demo/demo-workspace.tsx +419 -0
  61. package/components/global-settings.tsx +286 -0
  62. package/components/index/index-workspace.tsx +482 -0
  63. package/components/onboarding/simple-setup.tsx +702 -0
  64. package/components/onboarding/tech-setup.tsx +121 -0
  65. package/components/onboarding/welcome-screen.tsx +183 -0
  66. package/components/page-header.tsx +44 -0
  67. package/components/server/code-viewer.tsx +87 -0
  68. package/components/server/deploy-button.tsx +1390 -0
  69. package/components/server/server-workspace.tsx +392 -0
  70. package/components/simple/sdk-connect-dialog.tsx +201 -0
  71. package/components/simple/simple-index-button.tsx +83 -0
  72. package/components/simple/simple-sidebar.tsx +161 -0
  73. package/components/theme-customizer-provider.tsx +171 -0
  74. package/components/theme-switcher.tsx +445 -0
  75. package/components/ui/alert-dialog.tsx +187 -0
  76. package/components/ui/alert.tsx +76 -0
  77. package/components/ui/badge.tsx +52 -0
  78. package/components/ui/button.tsx +59 -0
  79. package/components/ui/card.tsx +103 -0
  80. package/components/ui/checkbox.tsx +28 -0
  81. package/components/ui/collapsible.tsx +21 -0
  82. package/components/ui/dialog.tsx +159 -0
  83. package/components/ui/dropdown-menu.tsx +281 -0
  84. package/components/ui/input.tsx +22 -0
  85. package/components/ui/label.tsx +20 -0
  86. package/components/ui/popover.tsx +90 -0
  87. package/components/ui/progress.tsx +83 -0
  88. package/components/ui/provider-icon.tsx +94 -0
  89. package/components/ui/scroll-area.tsx +55 -0
  90. package/components/ui/select.tsx +248 -0
  91. package/components/ui/separator.tsx +25 -0
  92. package/components/ui/sheet.tsx +138 -0
  93. package/components/ui/skeleton.tsx +13 -0
  94. package/components/ui/sonner.tsx +63 -0
  95. package/components/ui/switch.tsx +32 -0
  96. package/components/ui/table.tsx +116 -0
  97. package/components/ui/tabs.tsx +84 -0
  98. package/components/ui/textarea.tsx +18 -0
  99. package/components/ui/tooltip.tsx +66 -0
  100. package/components/workspace/delete-server-dialog.tsx +71 -0
  101. package/components/workspace/server-form-dialog.tsx +103 -0
  102. package/components/workspace/server-switcher.tsx +163 -0
  103. package/components/workspace/workspace-provider.tsx +177 -0
  104. package/components/workspace/workspace-top-bar.tsx +23 -0
  105. package/components.json +21 -0
  106. package/lib/error-formatter.ts +15 -0
  107. package/lib/utils.ts +6 -0
  108. package/next-env.d.ts +6 -0
  109. package/next.config.mjs +18 -0
  110. package/package.json +65 -0
  111. package/postcss.config.mjs +8 -0
  112. package/public/docker.png +0 -0
  113. package/public/favicon.ico +0 -0
  114. package/public/github.svg +3 -0
  115. package/public/hetzner.svg +1 -0
  116. package/public/icons/aws.svg +38 -0
  117. package/public/icons/azure.svg +1 -0
  118. package/public/icons/chroma.png +0 -0
  119. package/public/icons/cohere.svg +30 -0
  120. package/public/icons/digital-ocean.webp +0 -0
  121. package/public/icons/firecrawl2.png +0 -0
  122. package/public/icons/gcp.svg +1 -0
  123. package/public/icons/gemini.svg +1 -0
  124. package/public/icons/jina.svg +1 -0
  125. package/public/icons/lancedb2.png +0 -0
  126. package/public/icons/mistral.svg +469 -0
  127. package/public/icons/nomic.png +0 -0
  128. package/public/icons/openai.svg +1 -0
  129. package/public/icons/pgvector2.png +0 -0
  130. package/public/icons/pinecone.png +0 -0
  131. package/public/icons/qdrant.svg +35 -0
  132. package/public/icons/supabase.png +0 -0
  133. package/public/icons/vercel copy.svg +1 -0
  134. package/public/icons/vercel.svg +1 -0
  135. package/public/icons/voyage-light.png +0 -0
  136. package/public/icons/weaviate.webp +0 -0
  137. package/public/logo-light.png +0 -0
  138. package/public/logo.png +0 -0
  139. package/public/logo9.png +0 -0
  140. package/public/vercel.svg +1 -0
  141. package/tsconfig.json +33 -0
@@ -0,0 +1,445 @@
1
+ "use client";
2
+
3
+ import { useState, useEffect, Suspense } from "react";
4
+ import { useSearchParams } from "next/navigation";
5
+ import { GlobalSettings } from "./global-settings";
6
+
7
+ import {
8
+ useThemeCustomizer,
9
+ ThemeVariant,
10
+ BackgroundVariant,
11
+ PanelBgVariant,
12
+ LayoutVariant,
13
+ RadiusVariant,
14
+ PageStyleVariant,
15
+ } from "./theme-customizer-provider";
16
+ import { useWorkspace } from "@/components/workspace/workspace-provider";
17
+ import {
18
+ SlidersHorizontal,
19
+ LayoutTemplate,
20
+ Palette,
21
+ Monitor,
22
+ SquareDashed,
23
+ PanelTop,
24
+ User,
25
+ Check,
26
+ Settings,
27
+ } from "lucide-react";
28
+ import { Button } from "@/components/ui/button";
29
+ import {
30
+ Popover,
31
+ PopoverContent,
32
+ PopoverTrigger,
33
+ } from "@/components/ui/popover";
34
+ import {
35
+ Tooltip,
36
+ TooltipContent,
37
+ TooltipProvider,
38
+ TooltipTrigger,
39
+ } from "@/components/ui/tooltip";
40
+ import { Input } from "./ui/input";
41
+
42
+ const THEMES: { id: ThemeVariant; name: string; color: string }[] = [
43
+ { id: "default", name: "Caffee", color: "#f2efe4" },
44
+ { id: "theme-gaia", name: "Emerald Green", color: "#2b935f" },
45
+ { id: "theme-docker", name: "Ocean Blue", color: "#1d63ed" },
46
+ { id: "theme-pinecone", name: "Deep Indigo", color: "#6e56cf" },
47
+ { id: "theme-vercel", name: "Minimal Black", color: "#000000" },
48
+ { id: "theme-elevenlabs", name: "Soft Cream", color: "#f8f7f5" },
49
+ { id: "theme-espresso", name: "Espresso", color: "#6B3F2A" },
50
+ { id: "theme-sienna", name: "Warm Sienna", color: "#A0522D" },
51
+ { id: "theme-caramel", name: "Caramel", color: "#C47C3E" },
52
+ ];
53
+
54
+ const BACKGROUNDS: { id: BackgroundVariant; name: string; color: string }[] = [
55
+ { id: "bg-default", name: "White", color: "#FFFFFF" },
56
+ { id: "bg-pure", name: "Pure White", color: "#FFFFFF" },
57
+ { id: "bg-silver", name: "Silver", color: "#F8F8F8" },
58
+ { id: "bg-soft", name: "Soft", color: "#FBFAF8" },
59
+ { id: "bg-stone", name: "Stone", color: "#F5F5F2" },
60
+ { id: "bg-sage", name: "Sage", color: "#EDEDE8" },
61
+ { id: "bg-warm", name: "Warm Cream", color: "#F7F1EA" },
62
+ ];
63
+
64
+ const PANEL_BACKGROUNDS: {
65
+ id: PanelBgVariant;
66
+ name: string;
67
+ color: string;
68
+ }[] = [
69
+ { id: "panel-default", name: "Theme Default", color: "#FAFAFA" },
70
+ { id: "panel-white", name: "White", color: "#FFFFFF" },
71
+ { id: "panel-fafafa", name: "Near White", color: "#F4F4F4" },
72
+ { id: "panel-silver", name: "Silver", color: "#F8F8F8" },
73
+ { id: "panel-soft", name: "Soft", color: "#FBFAF8" },
74
+ { id: "panel-stone", name: "Stone", color: "#F5F5F2" },
75
+ { id: "panel-warm", name: "Warm Cream", color: "#F7F1EA" },
76
+ ];
77
+
78
+ const LAYOUTS: { id: LayoutVariant; name: string }[] = [
79
+ { id: "sidebar", name: "Sidebar (Left)" },
80
+ { id: "topnav", name: "Top Navigation" },
81
+ ];
82
+
83
+ const RADIUSES: { id: RadiusVariant; name: string; value: string }[] = [
84
+ { id: "radius-0", name: "Sharp (0)", value: "0" },
85
+ { id: "radius-sm", name: "Small", value: "0.3rem" },
86
+ { id: "radius-default", name: "Default", value: "0.625rem" },
87
+ { id: "radius-lg", name: "Large", value: "1rem" },
88
+ ];
89
+
90
+ const PAGE_STYLES: { id: PageStyleVariant; name: string }[] = [
91
+ { id: "card", name: "Card (Rounded/Shadow)" },
92
+ { id: "fused", name: "Fused (Flat)" },
93
+ ];
94
+
95
+ export function ThemeSwitcher({ floating = true }: { floating?: boolean }) {
96
+ return (
97
+ <Suspense fallback={<div className={floating ? "fixed bottom-6 right-6 z-50 h-12 w-12" : "size-9"} />}>
98
+ <ThemeSwitcherContent floating={floating} />
99
+ </Suspense>
100
+ );
101
+ }
102
+
103
+ function ThemeSwitcherContent({ floating = true }: { floating?: boolean }) {
104
+ const {
105
+ theme,
106
+ setTheme,
107
+ background,
108
+ setBackground,
109
+ panelBg,
110
+ setPanelBg,
111
+ navBg,
112
+ setNavBg,
113
+ layout,
114
+ setLayout,
115
+ radius,
116
+ setRadius,
117
+ pageStyle,
118
+ setPageStyle,
119
+ isMounted,
120
+ } = useThemeCustomizer();
121
+
122
+ const { username, setUsername } = useWorkspace();
123
+
124
+ const [localName, setLocalName] = useState(username || "");
125
+
126
+ // Sync local input when context username changes (e.g. on mount)
127
+ const syncedRef = useState({ synced: false })[0];
128
+ if (isMounted && !syncedRef.synced && username && localName !== username) {
129
+ setLocalName(username);
130
+ syncedRef.synced = true;
131
+ }
132
+
133
+ const searchParams = useSearchParams();
134
+ const [view, setView] = useState<"main" | "settings">("main");
135
+ const [open, setOpen] = useState(false);
136
+
137
+ useEffect(() => {
138
+ if (searchParams?.get("settings")) {
139
+ setOpen(true);
140
+ setView("settings");
141
+ }
142
+ }, [searchParams]);
143
+
144
+ const handleSaveUsername = () => {
145
+ setUsername(localName.trim());
146
+ };
147
+
148
+ if (!isMounted) return null;
149
+
150
+ const wrapperClass = floating
151
+ ? "fixed bottom-6 right-6 z-50"
152
+ : "flex items-center";
153
+ const buttonClass = floating
154
+ ? "h-12 w-12 rounded-full shadow-xl"
155
+ : "size-9 rounded-lg border border-border bg-card text-primary shadow-sm hover:bg-accent hover:text-accent-foreground";
156
+
157
+ return (
158
+ <div className={wrapperClass}>
159
+ <Popover open={open} onOpenChange={setOpen}>
160
+ <PopoverTrigger
161
+ render={
162
+ <Button
163
+ variant={floating ? "default" : "ghost"}
164
+ size="icon"
165
+ className={buttonClass}
166
+ >
167
+ <SlidersHorizontal className={floating ? "h-5 w-5" : "h-4 w-4"} />
168
+ </Button>
169
+ }
170
+ />
171
+ <PopoverContent
172
+ align="end"
173
+ className="w-80 p-0 shadow-2xl flex flex-col max-h-[85vh] overflow-hidden"
174
+ >
175
+ {view === "settings" ? (
176
+ <div className="flex flex-col min-h-0 flex-1 h-full animate-in slide-in-from-right-4 duration-300">
177
+ <GlobalSettings onBack={() => setView("main")} />
178
+ </div>
179
+ ) : (
180
+ <div className="flex flex-col min-h-0 flex-1 h-full animate-in slide-in-from-left-4 duration-300">
181
+ <div className="border-b px-4 py-3 font-semibold flex items-center justify-between shrink-0">
182
+ <div className="flex items-center gap-2">
183
+ <Palette className="h-4 w-4" /> Preferences
184
+ </div>
185
+ <Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => setView("settings")}>
186
+ <Settings className="h-4 w-4 text-muted-foreground" />
187
+ </Button>
188
+ </div>
189
+ <div className="flex-1 min-h-0 p-4 space-y-6 overflow-y-auto">
190
+ {/* Username */}
191
+ <div className="space-y-3">
192
+ <label className="text-sm font-medium flex items-center gap-2">
193
+ <User className="h-4 w-4 text-muted-foreground" /> Username
194
+ </label>
195
+ <div className="flex gap-2">
196
+ <Input
197
+ value={localName}
198
+ onChange={(e) => setLocalName(e.target.value)}
199
+ onKeyDown={(e) => e.key === "Enter" && handleSaveUsername()}
200
+ placeholder="Enter your name"
201
+ className="flex-1 rounded-md border border-input bg-background px-3 h-7 text-sm outline-none "
202
+ />
203
+ <Button
204
+ size="sm"
205
+ variant="default"
206
+ className="shrink-0 h-7 px-3.5"
207
+ onClick={handleSaveUsername}
208
+ >
209
+ save
210
+ {/* <Check className="h-3.5 w-3.5 " /> */}
211
+ </Button>
212
+ </div>
213
+ </div>
214
+
215
+ {/* Color Theme */}
216
+ <div className="space-y-3">
217
+ <label className="text-sm font-medium flex items-center gap-2">
218
+ <Palette className="h-4 w-4 text-muted-foreground" /> Color
219
+ Palette
220
+ </label>
221
+ <div className="grid grid-cols-3 gap-2">
222
+ {THEMES.map((t) => (
223
+ <button
224
+ key={t.id}
225
+ onClick={() => setTheme(t.id)}
226
+ className={`flex flex-col items-center justify-center gap-2 rounded-md border-2 p-2 text-xs transition-all hover:bg-muted ${
227
+ theme === t.id
228
+ ? "border-primary bg-primary/5"
229
+ : "border-transparent"
230
+ }`}
231
+ >
232
+ <div
233
+ className="h-6 w-6 rounded-full border shadow-sm"
234
+ style={{ background: t.color }}
235
+ />
236
+ <span className="truncate w-full text-center">
237
+ {t.name}
238
+ </span>
239
+ </button>
240
+ ))}
241
+ </div>
242
+ </div>
243
+
244
+ {/* Page Background */}
245
+ <TooltipProvider delay={200}>
246
+ <div className="space-y-3">
247
+ <label className="text-sm font-medium flex items-center gap-2">
248
+ <SquareDashed className="h-4 w-4 text-muted-foreground" />{" "}
249
+ Page Background
250
+ </label>
251
+ <p className="text-[11px] text-muted-foreground -mt-1">
252
+ Full page background behind everything
253
+ </p>
254
+ <div className="flex flex-wrap gap-2">
255
+ {BACKGROUNDS.map((bg) => (
256
+ <Tooltip key={bg.id}>
257
+ <TooltipTrigger
258
+ render={
259
+ <button
260
+ onClick={() => setBackground(bg.id)}
261
+ className={`flex items-center justify-center rounded-full border-2 p-0.5 transition-all hover:scale-110 ${
262
+ background === bg.id
263
+ ? "border-primary"
264
+ : "border-transparent"
265
+ }`}
266
+ />
267
+ }
268
+ >
269
+ <div
270
+ className={`h-6 w-6 rounded-full border shrink-0 ${bg.id === "bg-default" ? "border-dashed" : "shadow-sm"}`}
271
+ style={{ background: bg.color }}
272
+ />
273
+ </TooltipTrigger>
274
+ <TooltipContent side="top" sideOffset={6}>
275
+ <span className="text-xs font-medium">{bg.name}</span>
276
+ <span className="text-[10px] text-muted-foreground ml-1.5">
277
+ {bg.color}
278
+ </span>
279
+ </TooltipContent>
280
+ </Tooltip>
281
+ ))}
282
+ </div>
283
+ </div>
284
+ </TooltipProvider>
285
+
286
+ {/* Panel / Content Background */}
287
+ <TooltipProvider delay={200}>
288
+ <div className="space-y-3">
289
+ <label className="text-sm font-medium flex items-center gap-2">
290
+ <PanelTop className="h-4 w-4 text-muted-foreground" /> Content
291
+ Panel Background
292
+ </label>
293
+ <p className="text-[11px] text-muted-foreground -mt-1">
294
+ Inner content area (card / main panel)
295
+ </p>
296
+ <div className="flex flex-wrap gap-2">
297
+ {PANEL_BACKGROUNDS.map((pb) => (
298
+ <Tooltip key={pb.id}>
299
+ <TooltipTrigger
300
+ render={
301
+ <button
302
+ onClick={() => setPanelBg(pb.id)}
303
+ className={`flex items-center justify-center rounded-full border-2 p-0.5 transition-all hover:scale-110 ${
304
+ panelBg === pb.id
305
+ ? "border-primary"
306
+ : "border-transparent"
307
+ }`}
308
+ />
309
+ }
310
+ >
311
+ <div
312
+ className={`h-6 w-6 rounded-full border shrink-0 ${pb.id === "panel-default" ? "border-dashed" : "shadow-sm"}`}
313
+ style={{ background: pb.color }}
314
+ />
315
+ </TooltipTrigger>
316
+ <TooltipContent side="top" sideOffset={6}>
317
+ <span className="text-xs font-medium">{pb.name}</span>
318
+ <span className="text-[10px] text-muted-foreground ml-1.5">
319
+ {pb.color}
320
+ </span>
321
+ </TooltipContent>
322
+ </Tooltip>
323
+ ))}
324
+ </div>
325
+ </div>
326
+ </TooltipProvider>
327
+
328
+ {/* Nav/Sidebar Background */}
329
+ <TooltipProvider delay={200}>
330
+ <div className="space-y-3">
331
+ <label className="text-sm font-medium flex items-center gap-2">
332
+ <LayoutTemplate className="h-4 w-4 text-muted-foreground" /> Sidebar / Topnav Background
333
+ </label>
334
+ <p className="text-[11px] text-muted-foreground -mt-1">
335
+ Background color for the side or top navigation
336
+ </p>
337
+ <div className="flex flex-wrap gap-2">
338
+ {PANEL_BACKGROUNDS.map((pb) => (
339
+ <Tooltip key={`nav-${pb.id}`}>
340
+ <TooltipTrigger
341
+ render={
342
+ <button
343
+ onClick={() => setNavBg(pb.id)}
344
+ className={`flex items-center justify-center rounded-full border-2 p-0.5 transition-all hover:scale-110 ${
345
+ navBg === pb.id
346
+ ? "border-primary"
347
+ : "border-transparent"
348
+ }`}
349
+ />
350
+ }
351
+ >
352
+ <div
353
+ className={`h-6 w-6 rounded-full border shrink-0 ${pb.id === "panel-default" ? "border-dashed" : "shadow-sm"}`}
354
+ style={{ background: pb.color }}
355
+ />
356
+ </TooltipTrigger>
357
+ <TooltipContent side="top" sideOffset={6}>
358
+ <span className="text-xs font-medium">{pb.name}</span>
359
+ <span className="text-[10px] text-muted-foreground ml-1.5">
360
+ {pb.color}
361
+ </span>
362
+ </TooltipContent>
363
+ </Tooltip>
364
+ ))}
365
+ </div>
366
+ </div>
367
+ </TooltipProvider>
368
+
369
+ {/* Layout */}
370
+ <div className="space-y-3">
371
+ <label className="text-sm font-medium flex items-center gap-2">
372
+ <LayoutTemplate className="h-4 w-4 text-muted-foreground" />{" "}
373
+ Layout Style
374
+ </label>
375
+ <div className="grid grid-cols-2 gap-2">
376
+ {LAYOUTS.map((l) => (
377
+ <button
378
+ key={l.id}
379
+ onClick={() => setLayout(l.id)}
380
+ className={`rounded-md border-2 p-2 text-sm transition-all hover:bg-muted ${
381
+ layout === l.id
382
+ ? "border-primary bg-primary/5"
383
+ : "border-border"
384
+ }`}
385
+ >
386
+ {l.name}
387
+ </button>
388
+ ))}
389
+ </div>
390
+ </div>
391
+
392
+ {/* Page Style */}
393
+ <div className="space-y-3">
394
+ <label className="text-sm font-medium flex items-center gap-2">
395
+ <SquareDashed className="h-4 w-4 text-muted-foreground" /> Page
396
+ Style
397
+ </label>
398
+ <div className="grid grid-cols-2 gap-2">
399
+ {PAGE_STYLES.map((ps) => (
400
+ <button
401
+ key={ps.id}
402
+ onClick={() => setPageStyle(ps.id)}
403
+ className={`rounded-md border-2 p-2 text-xs transition-all hover:bg-muted ${
404
+ pageStyle === ps.id
405
+ ? "border-primary bg-primary/5"
406
+ : "border-border"
407
+ }`}
408
+ >
409
+ {ps.name}
410
+ </button>
411
+ ))}
412
+ </div>
413
+ </div>
414
+
415
+ {/* Border Radius */}
416
+ <div className="space-y-3">
417
+ <label className="text-sm font-medium flex items-center gap-2">
418
+ <Monitor className="h-4 w-4 text-muted-foreground" /> Border
419
+ Radius
420
+ </label>
421
+ <div className="flex flex-wrap gap-2">
422
+ {RADIUSES.map((r) => (
423
+ <button
424
+ key={r.id}
425
+ onClick={() => setRadius(r.id)}
426
+ className={`rounded-md border-2 px-3 py-1 text-xs transition-all hover:bg-muted ${
427
+ radius === r.id
428
+ ? "border-primary bg-primary/5"
429
+ : "border-border"
430
+ }`}
431
+ style={{ borderRadius: r.value }}
432
+ >
433
+ {r.name}
434
+ </button>
435
+ ))}
436
+ </div>
437
+ </div>
438
+ </div>
439
+ </div>
440
+ )}
441
+ </PopoverContent>
442
+ </Popover>
443
+ </div>
444
+ );
445
+ }
@@ -0,0 +1,187 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"
5
+
6
+ import { cn } from "@/lib/utils"
7
+ import { Button } from "@/components/ui/button"
8
+
9
+ function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
10
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
11
+ }
12
+
13
+ function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
14
+ return (
15
+ <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
16
+ )
17
+ }
18
+
19
+ function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
20
+ return (
21
+ <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
22
+ )
23
+ }
24
+
25
+ function AlertDialogOverlay({
26
+ className,
27
+ ...props
28
+ }: AlertDialogPrimitive.Backdrop.Props) {
29
+ return (
30
+ <AlertDialogPrimitive.Backdrop
31
+ data-slot="alert-dialog-overlay"
32
+ className={cn(
33
+ "fixed inset-0 isolate 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",
34
+ className
35
+ )}
36
+ {...props}
37
+ />
38
+ )
39
+ }
40
+
41
+ function AlertDialogContent({
42
+ className,
43
+ size = "default",
44
+ ...props
45
+ }: AlertDialogPrimitive.Popup.Props & {
46
+ size?: "default" | "sm"
47
+ }) {
48
+ return (
49
+ <AlertDialogPortal>
50
+ <AlertDialogOverlay />
51
+ <AlertDialogPrimitive.Popup
52
+ data-slot="alert-dialog-content"
53
+ data-size={size}
54
+ className={cn(
55
+ "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",
56
+ className
57
+ )}
58
+ {...props}
59
+ />
60
+ </AlertDialogPortal>
61
+ )
62
+ }
63
+
64
+ function AlertDialogHeader({
65
+ className,
66
+ ...props
67
+ }: React.ComponentProps<"div">) {
68
+ return (
69
+ <div
70
+ data-slot="alert-dialog-header"
71
+ className={cn(
72
+ "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]",
73
+ className
74
+ )}
75
+ {...props}
76
+ />
77
+ )
78
+ }
79
+
80
+ function AlertDialogFooter({
81
+ className,
82
+ ...props
83
+ }: React.ComponentProps<"div">) {
84
+ return (
85
+ <div
86
+ data-slot="alert-dialog-footer"
87
+ className={cn(
88
+ "-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",
89
+ className
90
+ )}
91
+ {...props}
92
+ />
93
+ )
94
+ }
95
+
96
+ function AlertDialogMedia({
97
+ className,
98
+ ...props
99
+ }: React.ComponentProps<"div">) {
100
+ return (
101
+ <div
102
+ data-slot="alert-dialog-media"
103
+ className={cn(
104
+ "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",
105
+ className
106
+ )}
107
+ {...props}
108
+ />
109
+ )
110
+ }
111
+
112
+ function AlertDialogTitle({
113
+ className,
114
+ ...props
115
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
116
+ return (
117
+ <AlertDialogPrimitive.Title
118
+ data-slot="alert-dialog-title"
119
+ className={cn(
120
+ "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",
121
+ className
122
+ )}
123
+ {...props}
124
+ />
125
+ )
126
+ }
127
+
128
+ function AlertDialogDescription({
129
+ className,
130
+ ...props
131
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
132
+ return (
133
+ <AlertDialogPrimitive.Description
134
+ data-slot="alert-dialog-description"
135
+ className={cn(
136
+ "text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
137
+ className
138
+ )}
139
+ {...props}
140
+ />
141
+ )
142
+ }
143
+
144
+ function AlertDialogAction({
145
+ className,
146
+ ...props
147
+ }: React.ComponentProps<typeof Button>) {
148
+ return (
149
+ <Button
150
+ data-slot="alert-dialog-action"
151
+ className={cn(className)}
152
+ {...props}
153
+ />
154
+ )
155
+ }
156
+
157
+ function AlertDialogCancel({
158
+ className,
159
+ variant = "outline",
160
+ size = "default",
161
+ ...props
162
+ }: AlertDialogPrimitive.Close.Props &
163
+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
164
+ return (
165
+ <AlertDialogPrimitive.Close
166
+ data-slot="alert-dialog-cancel"
167
+ className={cn(className)}
168
+ render={<Button variant={variant} size={size} />}
169
+ {...props}
170
+ />
171
+ )
172
+ }
173
+
174
+ export {
175
+ AlertDialog,
176
+ AlertDialogAction,
177
+ AlertDialogCancel,
178
+ AlertDialogContent,
179
+ AlertDialogDescription,
180
+ AlertDialogFooter,
181
+ AlertDialogHeader,
182
+ AlertDialogMedia,
183
+ AlertDialogOverlay,
184
+ AlertDialogPortal,
185
+ AlertDialogTitle,
186
+ AlertDialogTrigger,
187
+ }