@dyrected/admin 2.0.0 → 2.4.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 (67) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/package.json +4 -4
  3. package/scripts/prefix-tailwind-precision.py +98 -0
  4. package/scripts/prefix-tailwind.py +67 -0
  5. package/src/components/auth/auth-gate.tsx +4 -4
  6. package/src/components/error-boundary.tsx +4 -4
  7. package/src/components/forms/fields/block-builder.tsx +24 -24
  8. package/src/components/forms/fields/date-picker.tsx +7 -7
  9. package/src/components/forms/fields/json-editor.tsx +5 -5
  10. package/src/components/forms/fields/media-picker.tsx +39 -39
  11. package/src/components/forms/fields/multi-select.tsx +12 -12
  12. package/src/components/forms/fields/radio-field.tsx +8 -8
  13. package/src/components/forms/fields/relationship-picker.tsx +13 -13
  14. package/src/components/forms/fields/rich-text-editor.tsx +22 -22
  15. package/src/components/forms/fields/select-field.tsx +3 -3
  16. package/src/components/forms/form-engine.tsx +3 -3
  17. package/src/components/forms/form-field-renderer.tsx +37 -37
  18. package/src/components/layout/admin-shell.tsx +60 -60
  19. package/src/components/live-preview/LivePreviewPane.tsx +14 -14
  20. package/src/components/media/focal-point-picker.tsx +9 -9
  21. package/src/components/media/media-card.tsx +10 -10
  22. package/src/components/media/media-grid.tsx +3 -3
  23. package/src/components/media/media-library-dialog.tsx +105 -105
  24. package/src/components/ui/badge.tsx +5 -5
  25. package/src/components/ui/button.tsx +11 -11
  26. package/src/components/ui/calendar.tsx +36 -36
  27. package/src/components/ui/card.tsx +6 -6
  28. package/src/components/ui/checkbox.tsx +3 -3
  29. package/src/components/ui/command.tsx +12 -12
  30. package/src/components/ui/data-table.tsx +18 -18
  31. package/src/components/ui/dialog.tsx +9 -9
  32. package/src/components/ui/dropdown-menu.tsx +16 -16
  33. package/src/components/ui/form.tsx +4 -4
  34. package/src/components/ui/input.tsx +3 -3
  35. package/src/components/ui/label.tsx +1 -1
  36. package/src/components/ui/page-header.tsx +6 -6
  37. package/src/components/ui/pagination.tsx +6 -6
  38. package/src/components/ui/popover.tsx +1 -1
  39. package/src/components/ui/progress.tsx +2 -2
  40. package/src/components/ui/radio-group.tsx +4 -4
  41. package/src/components/ui/render-cell.tsx +16 -16
  42. package/src/components/ui/scroll-area.tsx +6 -6
  43. package/src/components/ui/select.tsx +14 -14
  44. package/src/components/ui/separator.tsx +2 -2
  45. package/src/components/ui/sheet.tsx +13 -13
  46. package/src/components/ui/sidebar.tsx +60 -60
  47. package/src/components/ui/skeleton.tsx +1 -1
  48. package/src/components/ui/sonner.tsx +1 -1
  49. package/src/components/ui/switch.tsx +2 -2
  50. package/src/components/ui/table.tsx +7 -7
  51. package/src/components/ui/tabs.tsx +3 -3
  52. package/src/components/ui/textarea.tsx +1 -1
  53. package/src/components/ui/toggle.tsx +6 -6
  54. package/src/components/ui/tooltip.tsx +1 -1
  55. package/src/index.css +27 -27
  56. package/src/index.tsx +4 -4
  57. package/src/lib/utils.ts +7 -3
  58. package/src/pages/auth/first-user-page.tsx +18 -18
  59. package/src/pages/auth/login-page.tsx +14 -14
  60. package/src/pages/collections/edit-page.tsx +37 -37
  61. package/src/pages/collections/list-page.tsx +23 -23
  62. package/src/pages/dashboard/dashboard.tsx +49 -49
  63. package/src/pages/globals/editor-page.tsx +13 -13
  64. package/src/pages/media/media-page.tsx +106 -106
  65. package/src/pages/setup/setup-prompt.tsx +73 -44
  66. package/tailwind.config.ts +1 -0
  67. package/vite.config.ts +0 -1
@@ -10,14 +10,14 @@ interface MediaGridProps {
10
10
  export function MediaGrid({ items, baseUrl, onDelete, slug }: MediaGridProps) {
11
11
  if (!items || items.length === 0) {
12
12
  return (
13
- <div className="flex flex-col items-center justify-center h-[300px] border-2 border-dashed border-border/60 rounded-3xl bg-muted/5">
14
- <p className="text-sm text-muted-foreground font-medium">No media assets found</p>
13
+ <div className="dy-flex dy-flex-col dy-items-center dy-justify-center dy-h-[300px] dy-border-2 dy-border-dashed dy-border-border/60 dy-rounded-3xl dy-bg-muted/5">
14
+ <p className="dy-text-sm dy-text-muted-foreground dy-font-medium">No media assets found</p>
15
15
  </div>
16
16
  )
17
17
  }
18
18
 
19
19
  return (
20
- <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-6">
20
+ <div className="dy-grid dy-grid-cols-2 sm:dy-grid-cols-3 md:dy-grid-cols-4 lg:dy-grid-cols-6 dy-gap-6">
21
21
  {items.map((item) => (
22
22
  <MediaCard
23
23
  key={item.id}
@@ -163,53 +163,53 @@ export function MediaLibraryDialog({
163
163
 
164
164
  return (
165
165
  <Dialog open={isOpen} onOpenChange={onOpenChange}>
166
- <DialogContent className="sm:max-w-[900px] p-0 overflow-hidden gap-0 bg-background border-none shadow-2xl">
167
- <Tabs value={activeTab} onValueChange={setActiveTab} className="flex flex-col h-[650px]">
168
- <div className="px-6 py-4 border-b flex items-center justify-between bg-muted/20">
169
- <div className="flex items-center gap-4">
170
- <DialogTitle className="text-xl font-serif font-bold tracking-tight">Media Library</DialogTitle>
166
+ <DialogContent className="sm:dy-max-w-[900px] dy-p-0 dy-overflow-hidden dy-gap-0 dy-bg-background dy-border-none dy-shadow-2xl">
167
+ <Tabs value={activeTab} onValueChange={setActiveTab} className="dy-flex dy-flex-col dy-h-[650px]">
168
+ <div className="dy-px-6 dy-py-4 dy-border-b dy-flex dy-items-center dy-justify-between dy-bg-muted/20">
169
+ <div className="dy-flex dy-items-center dy-gap-4">
170
+ <DialogTitle className="dy-text-xl dy-font-serif dy-font-bold dy-tracking-tight">Media Library</DialogTitle>
171
171
  {multiple && selectedValues.length > 0 && (
172
- <div className="flex items-center gap-2 px-3 py-1 bg-primary/10 rounded-full border border-primary/20 animate-in fade-in slide-in-from-left-2">
173
- <span className="text-xs font-bold text-primary">{selectedValues.length} Selected</span>
174
- <Button variant="ghost" size="icon" className="h-4 w-4 text-primary hover:bg-transparent" onClick={handleConfirm}>
175
- <Check className="h-3 w-3" />
172
+ <div className="dy-flex dy-items-center dy-gap-2 dy-px-3 dy-py-1 dy-bg-primary/10 dy-rounded-full dy-border dy-border-primary/20 dy-animate-in dy-fade-in dy-slide-in-from-left-2">
173
+ <span className="dy-text-xs dy-font-bold dy-text-primary">{selectedValues.length} Selected</span>
174
+ <Button variant="ghost" size="icon" className="dy-h-4 dy-w-4 dy-text-primary hover:dy-bg-transparent" onClick={handleConfirm}>
175
+ <Check className="dy-h-3 dy-w-3" />
176
176
  </Button>
177
177
  </div>
178
178
  )}
179
179
  </div>
180
- <TabsList className="bg-muted/50 p-1 rounded-xl">
181
- <TabsTrigger value="library" className="gap-2 rounded-lg px-4 font-bold text-xs uppercase tracking-wider transition-all data-[state=active]:bg-background data-[state=active]:shadow-sm">
182
- <Library className="h-3.5 w-3.5" /> Library
180
+ <TabsList className="dy-bg-muted/50 dy-p-1 dy-rounded-xl">
181
+ <TabsTrigger value="library" className="dy-gap-2 dy-rounded-lg dy-px-4 dy-font-bold dy-text-xs dy-uppercase dy-tracking-wider dy-transition-all data-[state=active]:dy-bg-background data-[state=active]:dy-shadow-sm">
182
+ <Library className="dy-h-3.5 dy-w-3.5" /> Library
183
183
  </TabsTrigger>
184
- <TabsTrigger value="upload" className="gap-2 rounded-lg px-4 font-bold text-xs uppercase tracking-wider transition-all data-[state=active]:bg-background data-[state=active]:shadow-sm">
185
- <Upload className="h-3.5 w-3.5" /> Upload
184
+ <TabsTrigger value="upload" className="dy-gap-2 dy-rounded-lg dy-px-4 dy-font-bold dy-text-xs dy-uppercase dy-tracking-wider dy-transition-all data-[state=active]:dy-bg-background data-[state=active]:dy-shadow-sm">
185
+ <Upload className="dy-h-3.5 dy-w-3.5" /> Upload
186
186
  </TabsTrigger>
187
- <TabsTrigger value="external" className="gap-2 rounded-lg px-4 font-bold text-xs uppercase tracking-wider transition-all data-[state=active]:bg-background data-[state=active]:shadow-sm">
188
- <Globe className="h-3.5 w-3.5" /> External URL
187
+ <TabsTrigger value="external" className="dy-gap-2 dy-rounded-lg dy-px-4 dy-font-bold dy-text-xs dy-uppercase dy-tracking-wider dy-transition-all data-[state=active]:dy-bg-background data-[state=active]:dy-shadow-sm">
188
+ <Globe className="dy-h-3.5 dy-w-3.5" /> External URL
189
189
  </TabsTrigger>
190
190
  </TabsList>
191
191
  </div>
192
192
 
193
- <div className="flex-1 overflow-hidden">
194
- <TabsContent value="library" className="h-full m-0 p-0 focus-visible:ring-0">
195
- <div className="flex h-full">
196
- <div className="flex-1 flex flex-col p-6 space-y-4 border-r">
197
- <div className="flex items-center gap-4">
198
- <div className="relative flex-1 group">
199
- <Search className="absolute left-3.5 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground group-focus-within:text-primary transition-colors" />
193
+ <div className="dy-flex-1 dy-overflow-hidden">
194
+ <TabsContent value="library" className="dy-h-full dy-m-0 dy-p-0 focus-visible:dy-ring-0">
195
+ <div className="dy-flex dy-h-full">
196
+ <div className="dy-flex-1 dy-flex dy-flex-col dy-p-6 dy-space-y-4 dy-border-r">
197
+ <div className="dy-flex dy-items-center dy-gap-4">
198
+ <div className="dy-relative dy-flex-1 dy-group">
199
+ <Search className="dy-absolute dy-left-3.5 dy-top-1/2 dy--translate-y-1/2 dy-h-4 dy-w-4 dy-text-muted-foreground dy-group-focus-within:dy-text-primary dy-transition-colors" />
200
200
  <Input
201
201
  placeholder="Search your media library..."
202
- className="pl-11 h-11 rounded-xl border-muted bg-muted/10 focus:bg-background transition-all"
202
+ className="dy-pl-11 dy-h-11 dy-rounded-xl dy-border-muted dy-bg-muted/10 focus:dy-bg-background dy-transition-all"
203
203
  value={searchQuery}
204
204
  onChange={(e) => setSearchQuery(e.target.value)}
205
205
  />
206
206
  </div>
207
207
  {multiple && (
208
- <div className="flex items-center gap-1 bg-muted/30 p-1 rounded-lg">
208
+ <div className="dy-flex dy-items-center dy-gap-1 dy-bg-muted/30 dy-p-1 dy-rounded-lg">
209
209
  <Button
210
210
  variant="ghost"
211
211
  size="sm"
212
- className="h-8 text-[10px] font-bold uppercase tracking-wider px-3 hover:bg-background rounded-md"
212
+ className="dy-h-8 dy-text-[10px] dy-font-bold dy-uppercase dy-tracking-wider dy-px-3 hover:dy-bg-background dy-rounded-md"
213
213
  onClick={() => {
214
214
  media?.forEach((item: any) => {
215
215
  if (!selectedValues.includes(item.id)) onSelect(item.id)
@@ -218,11 +218,11 @@ export function MediaLibraryDialog({
218
218
  >
219
219
  Select All
220
220
  </Button>
221
- <div className="w-px h-4 bg-border/50 mx-1" />
221
+ <div className="dy-w-px dy-h-4 dy-bg-border/50 dy-mx-1" />
222
222
  <Button
223
223
  variant="ghost"
224
224
  size="sm"
225
- className="h-8 text-[10px] font-bold uppercase tracking-wider px-3 text-destructive hover:text-destructive hover:bg-destructive/10 rounded-md"
225
+ className="dy-h-8 dy-text-[10px] dy-font-bold dy-uppercase dy-tracking-wider dy-px-3 dy-text-destructive hover:dy-text-destructive hover:dy-bg-destructive/10 dy-rounded-md"
226
226
  onClick={() => {
227
227
  selectedValues.forEach(id => onSelect(id))
228
228
  }}
@@ -232,8 +232,8 @@ export function MediaLibraryDialog({
232
232
  </div>
233
233
  )}
234
234
  </div>
235
- <ScrollArea className="flex-1 -mx-2 px-2">
236
- <div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-5 gap-4 pb-4">
235
+ <ScrollArea className="dy-flex-1 dy--mx-2 dy-px-2">
236
+ <div className="dy-grid dy-grid-cols-3 sm:dy-grid-cols-4 md:dy-grid-cols-5 dy-gap-4 dy-pb-4">
237
237
  {media?.map((item: any) => (
238
238
  <button
239
239
  key={item.id}
@@ -252,31 +252,31 @@ export function MediaLibraryDialog({
252
252
  }
253
253
  }}
254
254
  className={cn(
255
- "relative group rounded-2xl overflow-hidden border-2 aspect-square transition-all hover:scale-[1.02] active:scale-95 shadow-sm bg-muted/5",
255
+ "dy-relative dy-group dy-rounded-2xl dy-overflow-hidden dy-border-2 dy-aspect-square dy-transition-all hover:dy-scale-[1.02] active:dy-scale-95 dy-shadow-sm dy-bg-muted/5",
256
256
  selectedItem?.id === item.id
257
- ? "border-primary ring-4 ring-primary/10 shadow-lg shadow-primary/5"
258
- : "border-border/40 hover:border-border"
257
+ ? "dy-border-primary dy-ring-4 dy-ring-primary/10 dy-shadow-lg dy-shadow-primary/5"
258
+ : "dy-border-border/40 hover:dy-border-border"
259
259
  )}
260
260
  >
261
261
  <img
262
262
  src={getPreviewUrl(item)}
263
263
  alt={item.filename}
264
- className="object-cover w-full h-full"
264
+ className="dy-object-cover dy-w-full dy-h-full"
265
265
  />
266
266
  {selectedValues.includes(item.id) && (
267
- <div className="absolute top-2.5 right-2.5 h-7 w-7 bg-primary rounded-full flex items-center justify-center text-white shadow-xl animate-in zoom-in border-2 border-white">
268
- <Check className="h-4 w-4" />
267
+ <div className="dy-absolute dy-top-2.5 dy-right-2.5 dy-h-7 dy-w-7 dy-bg-primary dy-rounded-full dy-flex dy-items-center dy-justify-center dy-text-white dy-shadow-xl dy-animate-in dy-zoom-in dy-border-2 dy-border-white">
268
+ <Check className="dy-h-4 dy-w-4" />
269
269
  </div>
270
270
  )}
271
271
  {(item.mimeType?.startsWith('video/') || item.mimeType === 'video/youtube' || item.mimeType === 'video/vimeo') && (
272
- <div className="absolute inset-0 flex items-center justify-center bg-black/20 group-hover:bg-black/40 transition-colors">
273
- <div className="h-10 w-10 bg-white/20 backdrop-blur-md rounded-full flex items-center justify-center border border-white/30 shadow-2xl">
274
- <Video className="h-5 w-5 text-white" />
272
+ <div className="dy-absolute dy-inset-0 dy-flex dy-items-center dy-justify-center dy-bg-black/20 dy-group-hover:dy-bg-black/40 dy-transition-colors">
273
+ <div className="dy-h-10 dy-w-10 dy-bg-white/20 dy-backdrop-blur-md dy-rounded-full dy-flex dy-items-center dy-justify-center dy-border dy-border-white/30 dy-shadow-2xl">
274
+ <Video className="dy-h-5 dy-w-5 dy-text-white" />
275
275
  </div>
276
276
  </div>
277
277
  )}
278
- <div className="absolute inset-x-0 bottom-0 p-2.5 bg-gradient-to-t from-black/80 via-black/40 to-transparent opacity-0 group-hover:opacity-100 transition-opacity">
279
- <p className="text-[10px] text-white truncate font-bold uppercase tracking-wider">{item.filename}</p>
278
+ <div className="dy-absolute dy-inset-x-0 dy-bottom-0 dy-p-2.5 dy-bg-gradient-to-t dy-from-black/80 dy-via-black/40 dy-to-transparent dy-opacity-0 dy-group-hover:dy-opacity-100 dy-transition-opacity">
279
+ <p className="dy-text-[10px] dy-text-white dy-truncate dy-font-bold dy-uppercase dy-tracking-wider">{item.filename}</p>
280
280
  </div>
281
281
  </button>
282
282
  ))}
@@ -284,42 +284,42 @@ export function MediaLibraryDialog({
284
284
  </ScrollArea>
285
285
  </div>
286
286
 
287
- <div className="w-80 bg-muted/5 p-6 flex flex-col gap-6 overflow-y-auto border-l border-muted/20">
287
+ <div className="dy-w-80 dy-bg-muted/5 dy-p-6 dy-flex dy-flex-col dy-gap-6 dy-overflow-y-auto dy-border-l dy-border-muted/20">
288
288
  {selectedItem ? (
289
289
  <>
290
- <div className="space-y-5">
291
- <div className="aspect-square rounded-3xl overflow-hidden border bg-background shadow-2xl group relative ring-1 ring-border/50">
290
+ <div className="dy-space-y-5">
291
+ <div className="dy-aspect-square dy-rounded-3xl dy-overflow-hidden dy-border dy-bg-background dy-shadow-2xl dy-group dy-relative dy-ring-1 dy-ring-border/50">
292
292
  <img
293
293
  src={getPreviewUrl(selectedItem)}
294
- className="w-full h-full object-contain p-2"
294
+ className="dy-w-full dy-h-full dy-object-contain dy-p-2"
295
295
  alt=""
296
296
  />
297
- <div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center backdrop-blur-sm">
298
- <Button variant="secondary" size="sm" className="rounded-full shadow-lg font-bold" onClick={() => window.open(getPreviewUrl(selectedItem), '_blank')}>
297
+ <div className="dy-absolute dy-inset-0 dy-bg-black/40 dy-opacity-0 dy-group-hover:dy-opacity-100 dy-transition-opacity dy-flex dy-items-center dy-justify-center dy-backdrop-blur-sm">
298
+ <Button variant="secondary" size="sm" className="dy-rounded-full dy-shadow-lg dy-font-bold" onClick={() => window.open(getPreviewUrl(selectedItem), '_blank')}>
299
299
  View Full
300
300
  </Button>
301
301
  </div>
302
302
  </div>
303
- <div className="space-y-2">
304
- <h4 className="font-bold text-sm truncate leading-tight" title={selectedItem.filename}>
303
+ <div className="dy-space-y-2">
304
+ <h4 className="dy-font-bold dy-text-sm dy-truncate dy-leading-tight" title={selectedItem.filename}>
305
305
  {selectedItem.filename}
306
306
  </h4>
307
- <div className="flex flex-wrap items-center gap-2">
308
- <span className="text-[9px] font-black uppercase tracking-widest bg-primary/10 text-primary px-2 py-1 rounded-md border border-primary/10">
307
+ <div className="dy-flex dy-flex-wrap dy-items-center dy-gap-2">
308
+ <span className="dy-text-[9px] dy-font-black dy-uppercase dy-tracking-widest dy-bg-primary/10 dy-text-primary dy-px-2 dy-py-1 dy-rounded-md dy-border dy-border-primary/10">
309
309
  {selectedItem.mimeType?.split('/')[1] || selectedItem.mimeType}
310
310
  </span>
311
- <span className="text-[10px] font-bold text-muted-foreground/60">
311
+ <span className="dy-text-[10px] dy-font-bold dy-text-muted-foreground/60">
312
312
  {selectedItem.filesize ? `${(selectedItem.filesize / 1024).toFixed(1)} KB` : 'External Asset'}
313
313
  </span>
314
314
  </div>
315
315
  </div>
316
316
  </div>
317
317
 
318
- <div className="space-y-3 pt-6 border-t border-muted/20">
318
+ <div className="dy-space-y-3 dy-pt-6 dy-border-t dy-border-muted/20">
319
319
  {multiple ? (
320
320
  <>
321
321
  <Button
322
- className="w-full h-11 rounded-xl shadow-sm font-bold tracking-tight transition-all"
322
+ className="dy-w-full dy-h-11 dy-rounded-xl dy-shadow-sm dy-font-bold dy-tracking-tight dy-transition-all"
323
323
  variant={selectedValues.includes(selectedItem.id) ? "outline" : "default"}
324
324
  onClick={() => onSelect(selectedItem.id)}
325
325
  >
@@ -327,17 +327,17 @@ export function MediaLibraryDialog({
327
327
  </Button>
328
328
  {selectedValues.length > 0 && (
329
329
  <Button
330
- className="w-full h-11 rounded-xl shadow-xl bg-primary hover:bg-primary/90 font-bold tracking-tight transition-all group"
330
+ className="dy-w-full dy-h-11 dy-rounded-xl dy-shadow-xl dy-bg-primary hover:dy-bg-primary/90 dy-font-bold dy-tracking-tight dy-transition-all dy-group"
331
331
  onClick={handleConfirm}
332
332
  >
333
333
  <span>Confirm {selectedValues.length} {selectedValues.length === 1 ? 'Asset' : 'Assets'}</span>
334
- <Sparkles className="ml-2 h-4 w-4 opacity-50 group-hover:opacity-100 group-hover:scale-110 transition-all" />
334
+ <Sparkles className="dy-ml-2 dy-h-4 dy-w-4 dy-opacity-50 dy-group-hover:dy-opacity-100 dy-group-hover:dy-scale-110 dy-transition-all" />
335
335
  </Button>
336
336
  )}
337
337
  </>
338
338
  ) : (
339
339
  <Button
340
- className="w-full h-11 rounded-xl shadow-lg font-bold tracking-tight bg-primary hover:bg-primary/90 transition-all"
340
+ className="dy-w-full dy-h-11 dy-rounded-xl dy-shadow-lg dy-font-bold dy-tracking-tight dy-bg-primary hover:dy-bg-primary/90 dy-transition-all"
341
341
  onClick={() => {
342
342
  onSelect(selectedItem.id)
343
343
  onOpenChange(false)
@@ -349,13 +349,13 @@ export function MediaLibraryDialog({
349
349
  </div>
350
350
  </>
351
351
  ) : (
352
- <div className="flex-1 flex flex-col items-center justify-center text-center space-y-5 text-muted-foreground/30">
353
- <div className="p-6 bg-muted/10 rounded-full border border-muted/20 shadow-inner">
354
- <ImageIcon className="h-10 w-10" />
352
+ <div className="dy-flex-1 dy-flex dy-flex-col dy-items-center dy-justify-center dy-text-center dy-space-y-5 dy-text-muted-foreground/30">
353
+ <div className="dy-p-6 dy-bg-muted/10 dy-rounded-full dy-border dy-border-muted/20 dy-shadow-inner">
354
+ <ImageIcon className="dy-h-10 dy-w-10" />
355
355
  </div>
356
- <div className="space-y-1">
357
- <p className="text-xs font-bold uppercase tracking-widest">No Selection</p>
358
- <p className="text-[10px] font-medium max-w-[150px] leading-relaxed">Select an item from the library to view details and metadata</p>
356
+ <div className="dy-space-y-1">
357
+ <p className="dy-text-xs dy-font-bold dy-uppercase dy-tracking-widest">No Selection</p>
358
+ <p className="dy-text-[10px] dy-font-medium dy-max-w-[150px] dy-leading-relaxed">Select an item from the library to view details and metadata</p>
359
359
  </div>
360
360
  </div>
361
361
  )}
@@ -363,53 +363,53 @@ export function MediaLibraryDialog({
363
363
  </div>
364
364
  </TabsContent>
365
365
 
366
- <TabsContent value="upload" className="h-full m-0 p-8 focus-visible:ring-0">
367
- <div className="h-full flex flex-col items-center justify-center border-2 border-dashed border-primary/20 rounded-[2.5rem] bg-primary/5 hover:bg-primary/10 transition-all group relative overflow-hidden">
368
- <div className="absolute inset-0 bg-[radial-gradient(circle_at_center,var(--primary)_0%,transparent_100%)] opacity-[0.03]" />
366
+ <TabsContent value="upload" className="dy-h-full dy-m-0 dy-p-8 focus-visible:dy-ring-0">
367
+ <div className="dy-h-full dy-flex dy-flex-col dy-items-center dy-justify-center dy-border-2 dy-border-dashed dy-border-primary/20 dy-rounded-[2.5rem] dy-bg-primary/5 hover:dy-bg-primary/10 dy-transition-all dy-group dy-relative dy-overflow-hidden">
368
+ <div className="dy-absolute dy-inset-0 dy-bg-[radial-gradient(circle_at_center,var(--primary)_0%,transparent_100%)] dy-opacity-[0.03]" />
369
369
  <input
370
370
  type="file"
371
371
  id="media-upload-dialog"
372
- className="hidden"
372
+ className="dy-hidden"
373
373
  onChange={handleUpload}
374
374
  disabled={isUploading}
375
375
  />
376
376
  <label
377
377
  htmlFor="media-upload-dialog"
378
- className="flex flex-col items-center gap-8 cursor-pointer p-12 text-center relative z-10"
378
+ className="dy-flex dy-flex-col dy-items-center dy-gap-8 dy-cursor-pointer dy-p-12 dy-text-center dy-relative dy-z-10"
379
379
  >
380
- <div className="h-24 w-24 bg-background rounded-full flex items-center justify-center text-primary group-hover:scale-110 transition-all shadow-2xl shadow-primary/20 border border-primary/10">
381
- <Upload className="h-10 w-10" />
380
+ <div className="dy-h-24 dy-w-24 dy-bg-background dy-rounded-full dy-flex dy-items-center dy-justify-center dy-text-primary dy-group-hover:dy-scale-110 dy-transition-all dy-shadow-2xl dy-shadow-primary/20 dy-border dy-border-primary/10">
381
+ <Upload className="dy-h-10 dy-w-10" />
382
382
  </div>
383
- <div className="space-y-2">
384
- <p className="font-serif font-bold text-3xl tracking-tight">Upload new assets</p>
385
- <p className="text-muted-foreground/60 font-medium">Drag and drop files here or click to browse your computer</p>
383
+ <div className="dy-space-y-2">
384
+ <p className="dy-font-serif dy-font-bold dy-text-3xl dy-tracking-tight">Upload new assets</p>
385
+ <p className="dy-text-muted-foreground/60 dy-font-medium">Drag and drop files here or click to browse your computer</p>
386
386
  </div>
387
- <Button variant="secondary" className="rounded-full px-8 h-12 font-bold shadow-sm pointer-events-none group-hover:bg-primary group-hover:text-white transition-all">
387
+ <Button variant="secondary" className="dy-rounded-full dy-px-8 dy-h-12 dy-font-bold dy-shadow-sm dy-pointer-events-none dy-group-hover:dy-bg-primary dy-group-hover:dy-text-white dy-transition-all">
388
388
  Choose Files
389
389
  </Button>
390
390
  </label>
391
391
  </div>
392
392
  </TabsContent>
393
393
 
394
- <TabsContent value="external" className="h-full m-0 p-12 focus-visible:ring-0">
395
- <div className="max-w-2xl mx-auto space-y-10 pt-4">
396
- <div className="text-center space-y-4">
397
- <div className="h-20 w-20 bg-primary/10 text-primary rounded-3xl flex items-center justify-center mx-auto mb-6 shadow-sm rotate-3 group-hover:rotate-0 transition-transform">
398
- <Globe className="h-10 w-10" />
394
+ <TabsContent value="external" className="dy-h-full dy-m-0 dy-p-12 focus-visible:dy-ring-0">
395
+ <div className="dy-max-w-2xl dy-mx-auto dy-space-y-10 dy-pt-4">
396
+ <div className="dy-text-center dy-space-y-4">
397
+ <div className="dy-h-20 dy-w-20 dy-bg-primary/10 dy-text-primary dy-rounded-3xl dy-flex dy-items-center dy-justify-center dy-mx-auto dy-mb-6 dy-shadow-sm dy-rotate-3 dy-group-hover:dy-rotate-0 dy-transition-transform">
398
+ <Globe className="dy-h-10 dy-w-10" />
399
399
  </div>
400
- <h3 className="text-3xl font-serif font-bold tracking-tight">Add External Resource</h3>
401
- <p className="text-sm text-muted-foreground/70 leading-relaxed max-w-md mx-auto">
400
+ <h3 className="dy-text-3xl dy-font-serif dy-font-bold dy-tracking-tight">Add External Resource</h3>
401
+ <p className="dy-text-sm dy-text-muted-foreground/70 dy-leading-relaxed dy-max-w-md dy-mx-auto">
402
402
  Paste a link to any image, YouTube video, Vimeo link, or file to add it to your library without uploading.
403
403
  </p>
404
404
  </div>
405
405
 
406
- <div className="space-y-6">
407
- <div className="flex gap-3">
408
- <div className="relative flex-1">
409
- <LinkIcon className="absolute left-4 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
406
+ <div className="dy-space-y-6">
407
+ <div className="dy-flex dy-gap-3">
408
+ <div className="dy-relative dy-flex-1">
409
+ <LinkIcon className="dy-absolute dy-left-4 dy-top-1/2 dy--translate-y-1/2 dy-h-4 dy-w-4 dy-text-muted-foreground" />
410
410
  <Input
411
411
  placeholder="https://example.com/image.jpg or video link..."
412
- className="h-14 rounded-2xl shadow-xl border-muted bg-muted/5 pl-12 text-base font-medium focus:bg-background transition-all"
412
+ className="dy-h-14 dy-rounded-2xl dy-shadow-xl dy-border-muted dy-bg-muted/5 dy-pl-12 dy-text-base dy-font-medium focus:dy-bg-background dy-transition-all"
413
413
  value={externalUrl}
414
414
  onChange={(e) => setExternalUrl(e.target.value)}
415
415
  />
@@ -417,41 +417,41 @@ export function MediaLibraryDialog({
417
417
  <Button
418
418
  onClick={handleExternalUrlSubmit}
419
419
  disabled={isUploading || !externalUrl}
420
- className="h-14 rounded-2xl px-10 font-bold shadow-xl shadow-primary/20 bg-primary hover:bg-primary/90 transition-all active:scale-95"
420
+ className="dy-h-14 dy-rounded-2xl dy-px-10 dy-font-bold dy-shadow-xl dy-shadow-primary/20 dy-bg-primary hover:dy-bg-primary/90 dy-transition-all active:dy-scale-95"
421
421
  >
422
422
  {isUploading ? "Adding..." : "Add URL"}
423
423
  </Button>
424
424
  </div>
425
425
 
426
- <div className="grid grid-cols-2 gap-4">
427
- <div className="p-4 rounded-2xl bg-red-50/50 border border-red-100 flex items-start gap-3">
428
- <div className="mt-0.5 p-1.5 bg-red-100 rounded-lg text-red-600">
429
- <Video className="h-4 w-4" />
426
+ <div className="dy-grid dy-grid-cols-2 dy-gap-4">
427
+ <div className="dy-p-4 dy-rounded-2xl dy-bg-red-50/50 dy-border dy-border-red-100 dy-flex dy-items-start dy-gap-3">
428
+ <div className="dy-mt-0.5 dy-p-1.5 dy-bg-red-100 dy-rounded-lg dy-text-red-600">
429
+ <Video className="dy-h-4 dy-w-4" />
430
430
  </div>
431
431
  <div>
432
- <p className="text-xs font-bold text-red-900">Video Streaming</p>
433
- <p className="text-[10px] text-red-700/70 leading-relaxed font-medium mt-0.5">Supports YouTube & Vimeo. We recommend these for the best performance and compatibility.</p>
432
+ <p className="dy-text-xs dy-font-bold dy-text-red-900">Video Streaming</p>
433
+ <p className="dy-text-[10px] dy-text-red-700/70 dy-leading-relaxed dy-font-medium dy-mt-0.5">Supports YouTube & Vimeo. We recommend these for the best performance and compatibility.</p>
434
434
  </div>
435
435
  </div>
436
- <div className="p-4 rounded-2xl bg-blue-50/50 border border-blue-100 flex items-start gap-3">
437
- <div className="mt-0.5 p-1.5 bg-blue-100 rounded-lg text-blue-600">
438
- <ImageIcon className="h-4 w-4" />
436
+ <div className="dy-p-4 dy-rounded-2xl dy-bg-blue-50/50 dy-border dy-border-blue-100 dy-flex dy-items-start dy-gap-3">
437
+ <div className="dy-mt-0.5 dy-p-1.5 dy-bg-blue-100 dy-rounded-lg dy-text-blue-600">
438
+ <ImageIcon className="dy-h-4 dy-w-4" />
439
439
  </div>
440
440
  <div>
441
- <p className="text-xs font-bold text-blue-900">External Assets</p>
442
- <p className="text-[10px] text-blue-700/70 leading-relaxed font-medium mt-0.5">Add direct links to images or files from other CDNs. We'll automatically detect the type.</p>
441
+ <p className="dy-text-xs dy-font-bold dy-text-blue-900">External Assets</p>
442
+ <p className="dy-text-[10px] dy-text-blue-700/70 dy-leading-relaxed dy-font-medium dy-mt-0.5">Add direct links to images or files from other CDNs. We'll automatically detect the type.</p>
443
443
  </div>
444
444
  </div>
445
445
  </div>
446
446
 
447
- <div className="flex items-center gap-3 p-4 rounded-2xl bg-muted/20 border border-muted/30">
448
- <div className="p-2 bg-background rounded-xl shadow-sm text-muted-foreground">
449
- <Info className="h-4 w-4" />
447
+ <div className="dy-flex dy-items-center dy-gap-3 dy-p-4 dy-rounded-2xl dy-bg-muted/20 dy-border dy-border-muted/30">
448
+ <div className="dy-p-2 dy-bg-background dy-rounded-xl dy-shadow-sm dy-text-muted-foreground">
449
+ <Info className="dy-h-4 dy-w-4" />
450
450
  </div>
451
- <p className="text-[11px] text-muted-foreground/80 font-medium leading-relaxed">
452
- <span className="font-bold text-foreground">Pro Tip:</span> External videos are better streamed from
453
- <span className="text-red-600 font-bold ml-1">YouTube</span> or
454
- <span className="text-blue-500 font-bold ml-1">Vimeo</span> to ensure smooth playback on all devices.
451
+ <p className="dy-text-[11px] dy-text-muted-foreground/80 dy-font-medium dy-leading-relaxed">
452
+ <span className="dy-font-bold dy-text-foreground">Pro Tip:</span> External videos are better streamed from
453
+ <span className="dy-text-red-600 dy-font-bold dy-ml-1">YouTube</span> or
454
+ <span className="dy-text-blue-500 dy-font-bold dy-ml-1">Vimeo</span> to ensure smooth playback on all devices.
455
455
  </p>
456
456
  </div>
457
457
  </div>
@@ -4,17 +4,17 @@ import { cva, type VariantProps } from "class-variance-authority"
4
4
  import { cn } from "../../lib/utils"
5
5
 
6
6
  const badgeVariants = cva(
7
- "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
7
+ "dy-inline-flex dy-items-center dy-rounded-full dy-border dy-px-2.5 dy-py-0.5 dy-text-xs dy-font-semibold dy-transition-colors focus:dy-outline-none focus:dy-ring-2 focus:dy-ring-ring focus:dy-ring-offset-2",
8
8
  {
9
9
  variants: {
10
10
  variant: {
11
11
  default:
12
- "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
12
+ "dy-border-transparent dy-bg-primary dy-text-primary-foreground hover:dy-bg-primary/80",
13
13
  secondary:
14
- "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
14
+ "dy-border-transparent dy-bg-secondary dy-text-secondary-foreground hover:dy-bg-secondary/80",
15
15
  destructive:
16
- "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17
- outline: "text-foreground",
16
+ "dy-border-transparent dy-bg-destructive dy-text-destructive-foreground hover:dy-bg-destructive/80",
17
+ outline: "dy-text-foreground",
18
18
  },
19
19
  },
20
20
  defaultVariants: {
@@ -5,25 +5,25 @@ import { cva, type VariantProps } from "class-variance-authority"
5
5
  import { cn } from "../../lib/utils"
6
6
 
7
7
  const buttonVariants = cva(
8
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
8
+ "dy-inline-flex dy-items-center dy-justify-center dy-gap-2 dy-whitespace-nowrap dy-rounded-md dy-text-sm dy-font-medium dy-ring-offset-background dy-transition-colors focus-visible:dy-outline-none focus-visible:dy-ring-2 focus-visible:dy-ring-ring focus-visible:dy-ring-offset-2 disabled:dy-pointer-events-none disabled:dy-opacity-50 [&_svg]:dy-pointer-events-none [&_svg]:dy-size-4 [&_svg]:dy-shrink-0",
9
9
  {
10
10
  variants: {
11
11
  variant: {
12
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
12
+ default: "dy-bg-primary dy-text-primary-foreground hover:dy-bg-primary/90",
13
13
  destructive:
14
- "bg-destructive text-destructive-foreground hover:bg-destructive/90",
14
+ "dy-bg-destructive dy-text-destructive-foreground hover:dy-bg-destructive/90",
15
15
  outline:
16
- "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
16
+ "dy-border dy-border-input dy-bg-background hover:dy-bg-accent hover:dy-text-accent-foreground",
17
17
  secondary:
18
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19
- ghost: "hover:bg-accent hover:text-accent-foreground",
20
- link: "text-primary underline-offset-4 hover:underline",
18
+ "dy-bg-secondary dy-text-secondary-foreground hover:dy-bg-secondary/80",
19
+ ghost: "hover:dy-bg-accent hover:dy-text-accent-foreground",
20
+ link: "dy-text-primary dy-underline-offset-4 hover:dy-underline",
21
21
  },
22
22
  size: {
23
- default: "h-10 px-4 py-2",
24
- sm: "h-9 rounded-md px-3",
25
- lg: "h-11 rounded-md px-8",
26
- icon: "h-10 w-10",
23
+ default: "dy-h-10 dy-px-4 dy-py-2",
24
+ sm: "dy-h-9 dy-rounded-md dy-px-3",
25
+ lg: "dy-h-11 dy-rounded-md dy-px-8",
26
+ icon: "dy-h-10 dy-w-10",
27
27
  },
28
28
  },
29
29
  defaultVariants: {