@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
@@ -72,40 +72,40 @@ export function GlobalEditorPage() {
72
72
  if (isGlobalLoading) return <div>Loading global settings...</div>
73
73
 
74
74
  return (
75
- <div className="space-y-8 max-w-5xl mx-auto">
76
- <div className="flex items-center justify-between gap-4 border-b border-border/50 pb-6">
77
- <div className="flex items-center gap-4">
78
- <div className="p-2 bg-primary/10 text-primary rounded-lg shrink-0">
79
- <Globe className="h-5 w-5" />
75
+ <div className="dy-space-y-8 dy-max-w-5xl dy-mx-auto">
76
+ <div className="dy-flex dy-items-center dy-justify-between dy-gap-4 dy-border-b dy-border-border/50 dy-pb-6">
77
+ <div className="dy-flex dy-items-center dy-gap-4">
78
+ <div className="dy-p-2 dy-bg-primary/10 dy-text-primary dy-rounded-lg dy-shrink-0">
79
+ <Globe className="dy-h-5 dy-w-5" />
80
80
  </div>
81
81
  <div>
82
- <h1 className="text-lg font-serif font-bold tracking-tight text-foreground truncate">
82
+ <h1 className="dy-text-lg dy-font-serif dy-font-bold dy-tracking-tight dy-text-foreground dy-truncate">
83
83
  {schema.label || schema.slug}
84
84
  </h1>
85
- <p className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground/40 leading-none mt-1">
85
+ <p className="dy-text-[10px] dy-font-bold dy-uppercase dy-tracking-widest dy-text-muted-foreground/40 dy-leading-none dy-mt-1">
86
86
  Global Configuration
87
87
  </p>
88
88
  </div>
89
89
  </div>
90
90
 
91
- <div className="flex items-center gap-2">
91
+ <div className="dy-flex dy-items-center dy-gap-2">
92
92
  <Button
93
93
  size="icon"
94
- className="h-9 w-9 rounded-lg shadow-sm"
94
+ className="dy-h-9 dy-w-9 dy-rounded-lg dy-shadow-sm"
95
95
  onClick={() => document.getElementById('dyrected-form-submit')?.click()}
96
96
  disabled={saveMutation.isPending}
97
97
  title="Save Changes (⌘S)"
98
98
  >
99
99
  {saveMutation.isPending ? (
100
- <div className="h-4 w-4 animate-spin border-2 border-current border-t-transparent rounded-full" />
100
+ <div className="dy-h-4 dy-w-4 dy-animate-spin dy-border-2 dy-border-current dy-border-t-transparent dy-rounded-full" />
101
101
  ) : (
102
- <Save className="h-4 w-4" />
102
+ <Save className="dy-h-4 dy-w-4" />
103
103
  )}
104
104
  </Button>
105
105
  </div>
106
106
  </div>
107
107
 
108
- <div className="animate-in space-y-8 pb-20">
108
+ <div className="dy-animate-in dy-space-y-8 dy-pb-20">
109
109
  <FormEngine
110
110
  collection={slug!}
111
111
  fields={schema.fields}
@@ -115,7 +115,7 @@ export function GlobalEditorPage() {
115
115
  onChange={(dirty) => setIsDirty(dirty)}
116
116
  submitLabel="Save Changes"
117
117
  />
118
- <button id="dyrected-form-submit" type="submit" className="hidden" />
118
+ <button id="dyrected-form-submit" type="submit" className="dy-hidden" />
119
119
  </div>
120
120
  </div>
121
121
  )
@@ -93,41 +93,41 @@ export function MediaPage({ collectionSlug, schema }: { collectionSlug?: string,
93
93
  })
94
94
 
95
95
  return (
96
- <div {...getRootProps()} className="min-h-full space-y-8 animate-in relative">
96
+ <div {...getRootProps()} className="dy-min-h-full dy-space-y-8 dy-animate-in dy-relative">
97
97
  <input {...getInputProps()} />
98
98
 
99
99
  {isDragActive && (
100
- <div className="absolute inset-0 z-50 bg-primary/10 backdrop-blur-[2px] border-4 border-dashed border-primary rounded-2xl flex items-center justify-center pointer-events-none">
101
- <div className="bg-white p-8 rounded-2xl shadow-2xl flex flex-col items-center gap-4">
102
- <div className="h-16 w-16 rounded-full bg-primary/10 flex items-center justify-center">
103
- <Upload className="h-8 w-8 text-primary animate-bounce" />
100
+ <div className="dy-absolute dy-inset-0 dy-z-50 dy-bg-primary/10 dy-backdrop-blur-[2px] dy-border-4 dy-border-dashed dy-border-primary dy-rounded-2xl dy-flex dy-items-center dy-justify-center dy-pointer-events-none">
101
+ <div className="dy-bg-white dy-p-8 dy-rounded-2xl dy-shadow-2xl dy-flex dy-flex-col dy-items-center dy-gap-4">
102
+ <div className="dy-h-16 dy-w-16 dy-rounded-full dy-bg-primary/10 dy-flex dy-items-center dy-justify-center">
103
+ <Upload className="dy-h-8 dy-w-8 dy-text-primary dy-animate-bounce" />
104
104
  </div>
105
- <p className="text-xl font-bold">Drop to upload assets</p>
105
+ <p className="dy-text-xl dy-font-bold">Drop to upload assets</p>
106
106
  </div>
107
107
  </div>
108
108
  )}
109
- <div className="flex items-end justify-between border-b border-border/50 pb-6">
109
+ <div className="dy-flex dy-items-end dy-justify-between dy-border-b dy-border-border/50 dy-pb-6">
110
110
  <div>
111
- <div className="flex items-center gap-2 mb-1">
112
- <ImageIcon className="h-5 w-5 text-primary" />
113
- <h1 className="text-3xl font-bold tracking-tight text-foreground">
111
+ <div className="dy-flex dy-items-center dy-gap-2 dy-mb-1">
112
+ <ImageIcon className="dy-h-5 dy-w-5 dy-text-primary" />
113
+ <h1 className="dy-text-3xl dy-font-bold dy-tracking-tight dy-text-foreground">
114
114
  {schema?.labels?.plural ?? schema?.label ?? "Media Library"}
115
115
  </h1>
116
116
  </div>
117
- <p className="text-sm text-muted-foreground">
117
+ <p className="dy-text-sm dy-text-muted-foreground">
118
118
  Manage your images, documents, and other assets for this site.
119
119
  </p>
120
120
  </div>
121
121
  <Dialog open={isUploadOpen} onOpenChange={setIsUploadOpen}>
122
122
  <DialogTrigger asChild>
123
- <Button className="h-10 px-4 rounded-lg bg-primary hover:bg-primary/90 shadow-md transition-all active:scale-95">
124
- <Upload className="mr-2 h-4 w-4" />
123
+ <Button className="dy-h-10 dy-px-4 dy-rounded-lg dy-bg-primary hover:dy-bg-primary/90 dy-shadow-md dy-transition-all active:dy-scale-95">
124
+ <Upload className="dy-mr-2 dy-h-4 dy-w-4" />
125
125
  Upload Assets
126
126
  </Button>
127
127
  </DialogTrigger>
128
- <DialogContent className="sm:max-w-[600px] rounded-2xl overflow-hidden border-none shadow-2xl">
129
- <DialogHeader className="pb-4 border-b border-border/40">
130
- <DialogTitle className="text-xl font-bold">Upload Media Assets</DialogTitle>
128
+ <DialogContent className="sm:dy-max-w-[600px] dy-rounded-2xl dy-overflow-hidden dy-border-none dy-shadow-2xl">
129
+ <DialogHeader className="dy-pb-4 dy-border-b dy-border-border/40">
130
+ <DialogTitle className="dy-text-xl dy-font-bold">Upload Media Assets</DialogTitle>
131
131
  </DialogHeader>
132
132
  <FileUploader
133
133
  collectionSlug={collectionSlug}
@@ -140,35 +140,35 @@ export function MediaPage({ collectionSlug, schema }: { collectionSlug?: string,
140
140
  </Dialog>
141
141
  </div>
142
142
 
143
- <div className="flex items-center gap-4">
144
- <div className="relative flex-1 max-w-sm">
145
- <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground/60" />
143
+ <div className="dy-flex dy-items-center dy-gap-4">
144
+ <div className="dy-relative dy-flex-1 dy-max-w-sm">
145
+ <Search className="dy-absolute dy-left-3 dy-top-1/2 dy--translate-y-1/2 dy-h-4 dy-w-4 dy-text-muted-foreground/60" />
146
146
  <Input
147
147
  placeholder="Search assets by filename..."
148
- className="pl-10 h-11 bg-white border-border/60 rounded-xl shadow-sm focus-visible:ring-primary/20"
148
+ className="dy-pl-10 dy-h-11 dy-bg-white dy-border-border/60 dy-rounded-xl dy-shadow-sm focus-visible:dy-ring-primary/20"
149
149
  value={search}
150
150
  onChange={(e) => setSearch(e.target.value)}
151
151
  />
152
152
  </div>
153
153
  </div>
154
154
 
155
- <ScrollArea className="h-[calc(100vh-320px)] pr-4">
155
+ <ScrollArea className="dy-h-[calc(100vh-320px)] dy-pr-4">
156
156
  {isLoading ? (
157
- <div className="flex h-60 items-center justify-center">
158
- <div className="animate-spin rounded-full border-4 border-primary/20 border-t-primary h-10 w-10"></div>
157
+ <div className="dy-flex dy-h-60 dy-items-center dy-justify-center">
158
+ <div className="dy-animate-spin dy-rounded-full dy-border-4 dy-border-primary/20 dy-border-t-primary dy-h-10 dy-w-10"></div>
159
159
  </div>
160
160
  ) : mediaResponse?.length === 0 ? (
161
- <div className="flex h-80 flex-col items-center justify-center rounded-2xl border-2 border-dashed border-border/60 bg-muted/5 text-center animate-in">
162
- <div className="h-16 w-16 rounded-2xl bg-muted/40 flex items-center justify-center mb-4">
163
- <FileIcon className="h-8 w-8 text-muted-foreground/50" />
161
+ <div className="dy-flex dy-h-80 dy-flex-col dy-items-center dy-justify-center dy-rounded-2xl dy-border-2 dy-border-dashed dy-border-border/60 dy-bg-muted/5 dy-text-center dy-animate-in">
162
+ <div className="dy-h-16 dy-w-16 dy-rounded-2xl dy-bg-muted/40 dy-flex dy-items-center dy-justify-center dy-mb-4">
163
+ <FileIcon className="dy-h-8 dy-w-8 dy-text-muted-foreground/50" />
164
164
  </div>
165
- <h3 className="text-lg font-bold text-foreground">No assets found</h3>
166
- <p className="text-sm text-muted-foreground max-w-xs mx-auto">
165
+ <h3 className="dy-text-lg dy-font-bold dy-text-foreground">No assets found</h3>
166
+ <p className="dy-text-sm dy-text-muted-foreground dy-max-w-xs dy-mx-auto">
167
167
  Your media library is empty. Upload some files to start building your content.
168
168
  </p>
169
169
  </div>
170
170
  ) : (
171
- <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6 gap-6 pb-8">
171
+ <div className="dy-grid dy-grid-cols-2 md:dy-grid-cols-3 lg:dy-grid-cols-4 xl:dy-grid-cols-5 2xl:dy-grid-cols-6 dy-gap-6 dy-pb-8">
172
172
  {mediaResponse?.map((item) => (
173
173
  <MediaCard
174
174
  key={item.id}
@@ -206,17 +206,17 @@ function MediaCard({ item, baseUrl, onDelete, onClick, isSelected }: {
206
206
  return (
207
207
  <Card
208
208
  className={cn(
209
- "overflow-hidden group relative border-border/40 bg-white shadow-sm hover:shadow-xl transition-all duration-300 rounded-xl cursor-pointer",
210
- isSelected && "ring-2 ring-primary ring-offset-2 shadow-lg scale-[0.98]"
209
+ "dy-overflow-hidden dy-group dy-relative dy-border-border/40 dy-bg-white dy-shadow-sm hover:dy-shadow-xl dy-transition-all dy-duration-300 dy-rounded-xl dy-cursor-pointer",
210
+ isSelected && "dy-ring-2 dy-ring-primary dy-ring-offset-2 dy-shadow-lg dy-scale-[0.98]"
211
211
  )}
212
212
  onClick={onClick}
213
213
  >
214
- <CardHeader className="p-0 border-b border-border/10">
215
- <AspectRatio ratio={1 / 1} className="bg-muted/30 overflow-hidden relative">
214
+ <CardHeader className="dy-p-0 dy-border-b dy-border-border/10">
215
+ <AspectRatio ratio={1 / 1} className="dy-bg-muted/30 dy-overflow-hidden dy-relative">
216
216
  {isImage ? (
217
217
  <>
218
218
  {item.blurhash && (
219
- <div className="absolute inset-0 z-0">
219
+ <div className="dy-absolute dy-inset-0 dy-z-0">
220
220
  <Blurhash
221
221
  hash={item.blurhash}
222
222
  width="100%"
@@ -230,35 +230,35 @@ function MediaCard({ item, baseUrl, onDelete, onClick, isSelected }: {
230
230
  <img
231
231
  src={url}
232
232
  alt={item.filename}
233
- className="object-cover w-full h-full transition-transform duration-500 group-hover:scale-110 relative z-10"
233
+ className="dy-object-cover dy-w-full dy-h-full dy-transition-transform dy-duration-500 dy-group-hover:dy-scale-110 dy-relative dy-z-10"
234
234
  loading="lazy"
235
235
  />
236
236
  </>
237
237
  ) : (
238
- <div className="flex items-center justify-center h-full bg-primary/5">
239
- <FileIcon className="h-10 w-10 text-primary/40" />
238
+ <div className="dy-flex dy-items-center dy-justify-center dy-h-full dy-bg-primary/5">
239
+ <FileIcon className="dy-h-10 dy-w-10 dy-text-primary/40" />
240
240
  </div>
241
241
  )}
242
242
  </AspectRatio>
243
243
  </CardHeader>
244
- <CardContent className="p-3 bg-white">
245
- <p className="text-[11px] font-bold truncate text-foreground/90 mb-0.5" title={item.filename}>
244
+ <CardContent className="dy-p-3 dy-bg-white">
245
+ <p className="dy-text-[11px] dy-font-bold dy-truncate dy-text-foreground/90 dy-mb-0.5" title={item.filename}>
246
246
  {item.filename}
247
247
  </p>
248
- <div className="flex items-center justify-between">
249
- <p className="text-[9px] text-muted-foreground font-bold uppercase tracking-wider">
248
+ <div className="dy-flex dy-items-center dy-justify-between">
249
+ <p className="dy-text-[9px] dy-text-muted-foreground dy-font-bold dy-uppercase dy-tracking-wider">
250
250
  {item.mimeType?.split("/")[1] || "file"}
251
251
  </p>
252
- <p className="text-[9px] text-muted-foreground font-medium">
252
+ <p className="dy-text-[9px] dy-text-muted-foreground dy-font-medium">
253
253
  {((item.filesize || item.size || 0) / 1024).toFixed(1)} KB
254
254
  </p>
255
255
  </div>
256
256
  </CardContent>
257
- <div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity">
257
+ <div className="dy-absolute dy-top-2 dy-right-2 dy-flex dy-gap-2 dy-opacity-0 dy-group-hover:dy-opacity-100 dy-transition-opacity">
258
258
  <Button
259
259
  size="icon"
260
260
  variant="destructive"
261
- className="h-7 w-7 rounded-lg shadow-lg"
261
+ className="dy-h-7 dy-w-7 dy-rounded-lg dy-shadow-lg"
262
262
  onClick={(e) => {
263
263
  e.stopPropagation()
264
264
  if (confirm("Are you sure you want to delete this file?")) {
@@ -266,7 +266,7 @@ function MediaCard({ item, baseUrl, onDelete, onClick, isSelected }: {
266
266
  }
267
267
  }}
268
268
  >
269
- <Trash2 className="h-3.5 w-3.5" />
269
+ <Trash2 className="dy-h-3.5 dy-w-3.5" />
270
270
  </Button>
271
271
  </div>
272
272
  </Card>
@@ -313,22 +313,22 @@ function MediaSidebar({ item, onClose, baseUrl, onUpdate }: {
313
313
 
314
314
  return (
315
315
  <Sheet open={!!item} onOpenChange={onClose}>
316
- <SheetContent className="sm:max-w-md p-0 flex flex-col h-full border-l border-border/40 bg-white shadow-2xl">
317
- <SheetHeader className="p-6 border-b border-border/40 bg-white">
318
- <SheetTitle className="flex items-center gap-2">
319
- <Info className="h-5 w-5 text-primary" />
316
+ <SheetContent className="sm:dy-max-w-md dy-p-0 dy-flex dy-flex-col dy-h-full dy-border-l dy-border-border/40 dy-bg-white dy-shadow-2xl">
317
+ <SheetHeader className="dy-p-6 dy-border-b dy-border-border/40 dy-bg-white">
318
+ <SheetTitle className="dy-flex dy-items-center dy-gap-2">
319
+ <Info className="dy-h-5 dy-w-5 dy-text-primary" />
320
320
  File Details
321
321
  </SheetTitle>
322
322
  </SheetHeader>
323
323
 
324
- <ScrollArea className="flex-1 bg-white">
325
- <div className="p-6 space-y-8">
326
- <div className="rounded-xl overflow-hidden border border-border/40 bg-muted/10 relative shadow-inner">
324
+ <ScrollArea className="dy-flex-1 dy-bg-white">
325
+ <div className="dy-p-6 dy-space-y-8">
326
+ <div className="dy-rounded-xl dy-overflow-hidden dy-border dy-border-border/40 dy-bg-muted/10 dy-relative dy-shadow-inner">
327
327
  <AspectRatio ratio={16 / 9}>
328
328
  {isImage ? (
329
329
  <>
330
330
  {item.blurhash && (
331
- <div className="absolute inset-0 z-0">
331
+ <div className="dy-absolute dy-inset-0 dy-z-0">
332
332
  <Blurhash
333
333
  hash={item.blurhash}
334
334
  width="100%"
@@ -339,49 +339,49 @@ function MediaSidebar({ item, onClose, baseUrl, onUpdate }: {
339
339
  />
340
340
  </div>
341
341
  )}
342
- <img src={url} alt={item.filename} className="object-contain w-full h-full bg-checkered relative z-10" />
342
+ <img src={url} alt={item.filename} className="dy-object-contain dy-w-full dy-h-full dy-bg-checkered dy-relative dy-z-10" />
343
343
  </>
344
344
  ) : (
345
- <div className="flex items-center justify-center h-full">
346
- <FileIcon className="h-16 w-16 text-muted-foreground/30" />
345
+ <div className="dy-flex dy-items-center dy-justify-center dy-h-full">
346
+ <FileIcon className="dy-h-16 dy-w-16 dy-text-muted-foreground/30" />
347
347
  </div>
348
348
  )}
349
349
  </AspectRatio>
350
350
  </div>
351
351
 
352
- <div className="space-y-6">
353
- <div className="space-y-4">
354
- <div className="space-y-2">
355
- <label className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground/80">Filename</label>
352
+ <div className="dy-space-y-6">
353
+ <div className="dy-space-y-4">
354
+ <div className="dy-space-y-2">
355
+ <label className="dy-text-[10px] dy-font-bold dy-uppercase dy-tracking-widest dy-text-muted-foreground/80">Filename</label>
356
356
  <Input
357
357
  value={formData.filename}
358
358
  onChange={(e) => setFormData({ ...formData, filename: e.target.value })}
359
- className="h-10 rounded-lg bg-white border-border/60 focus:ring-1 focus:ring-primary/20"
359
+ className="dy-h-10 dy-rounded-lg dy-bg-white dy-border-border/60 focus:dy-ring-1 focus:dy-ring-primary/20"
360
360
  />
361
361
  </div>
362
- <div className="space-y-2">
363
- <label className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground/80">Alt Text</label>
362
+ <div className="dy-space-y-2">
363
+ <label className="dy-text-[10px] dy-font-bold dy-uppercase dy-tracking-widest dy-text-muted-foreground/80">Alt Text</label>
364
364
  <Input
365
365
  value={formData.alt}
366
366
  onChange={(e) => setFormData({ ...formData, alt: e.target.value })}
367
367
  placeholder="Describe the image for accessibility..."
368
- className="h-10 rounded-lg bg-white border-border/60 focus:ring-1 focus:ring-primary/20"
368
+ className="dy-h-10 dy-rounded-lg dy-bg-white dy-border-border/60 focus:dy-ring-1 focus:dy-ring-primary/20"
369
369
  />
370
370
  </div>
371
- <div className="space-y-2">
372
- <label className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground/80">Caption</label>
371
+ <div className="dy-space-y-2">
372
+ <label className="dy-text-[10px] dy-font-bold dy-uppercase dy-tracking-widest dy-text-muted-foreground/80">Caption</label>
373
373
  <textarea
374
374
  value={formData.caption}
375
375
  onChange={(e) => setFormData({ ...formData, caption: e.target.value })}
376
376
  placeholder="Add a caption..."
377
- className="flex min-h-[80px] w-full rounded-lg border border-border/60 bg-white px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/20 disabled:cursor-not-allowed disabled:opacity-50"
377
+ className="dy-flex dy-min-h-[80px] dy-w-full dy-rounded-lg dy-border dy-border-border/60 dy-bg-white dy-px-3 dy-py-2 dy-text-sm dy-ring-offset-background placeholder:dy-text-muted-foreground focus-visible:dy-outline-none focus-visible:dy-ring-1 focus-visible:dy-ring-primary/20 disabled:dy-cursor-not-allowed disabled:dy-opacity-50"
378
378
  />
379
379
  </div>
380
380
  </div>
381
381
 
382
- <Separator className="bg-border/40" />
382
+ <Separator className="dy-bg-border/40" />
383
383
 
384
- <div className="grid grid-cols-2 gap-6">
384
+ <div className="dy-grid dy-grid-cols-2 dy-gap-6">
385
385
  <DetailItem label="File ID" value={item.id} copyable />
386
386
  <DetailItem label="Size" value={`${((item.filesize || item.size || 0) / 1024).toFixed(1)} KB`} />
387
387
  <DetailItem label="Type" value={item.mimeType || "Unknown"} />
@@ -393,10 +393,10 @@ function MediaSidebar({ item, onClose, baseUrl, onUpdate }: {
393
393
  </div>
394
394
 
395
395
  {/* {isImage && (
396
- <div className="space-y-4">
397
- <Separator className="bg-border/40" />
398
- <div className="space-y-4">
399
- <label className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground/80">Focal Point</label>
396
+ <div className="dy-space-y-4">
397
+ <Separator className="dy-bg-border/40" />
398
+ <div className="dy-space-y-4">
399
+ <label className="dy-text-[10px] dy-font-bold dy-uppercase dy-tracking-widest dy-text-muted-foreground/80">Focal Point</label>
400
400
  <FocalPointPicker
401
401
  url={url}
402
402
  value={item.focalPoint}
@@ -410,19 +410,19 @@ function MediaSidebar({ item, onClose, baseUrl, onUpdate }: {
410
410
  </div>
411
411
  </ScrollArea>
412
412
 
413
- <div className="p-6 border-t border-border/40 bg-muted/5 space-y-3">
413
+ <div className="dy-p-6 dy-border-t dy-border-border/40 dy-bg-muted/5 dy-space-y-3">
414
414
  {hasChanges && (
415
415
  <Button
416
- className="w-full h-12 rounded-xl font-bold bg-primary text-white shadow-lg shadow-primary/20 animate-in fade-in slide-in-from-bottom-2"
416
+ className="dy-w-full dy-h-12 dy-rounded-xl dy-font-bold dy-bg-primary dy-text-white dy-shadow-lg dy-shadow-primary/20 dy-animate-in dy-fade-in dy-slide-in-from-bottom-2"
417
417
  onClick={handleSave}
418
418
  disabled={isSaving}
419
419
  >
420
420
  {isSaving ? "Saving..." : "Save Changes"}
421
421
  </Button>
422
422
  )}
423
- <Button className="w-full h-11 rounded-xl font-bold gap-2 bg-white" variant="outline" asChild>
423
+ <Button className="dy-w-full dy-h-11 dy-rounded-xl dy-font-bold dy-gap-2 dy-bg-white" variant="outline" asChild>
424
424
  <a href={url} target="_blank" rel="noreferrer">
425
- <ExternalLink className="h-4 w-4" />
425
+ <ExternalLink className="dy-h-4 dy-w-4" />
426
426
  Open Original
427
427
  </a>
428
428
  </Button>
@@ -438,18 +438,18 @@ function DetailItem({ label, value, copyable }: {
438
438
  copyable?: boolean
439
439
  }) {
440
440
  return (
441
- <div className="space-y-1.5">
442
- <label className="text-[10px] font-bold uppercase tracking-widest text-muted-foreground/80">{label}</label>
443
- <div className="flex items-center gap-2 group">
444
- <p className="text-sm font-medium text-foreground truncate flex-1">{value}</p>
441
+ <div className="dy-space-y-1.5">
442
+ <label className="dy-text-[10px] dy-font-bold dy-uppercase dy-tracking-widest dy-text-muted-foreground/80">{label}</label>
443
+ <div className="dy-flex dy-items-center dy-gap-2 dy-group">
444
+ <p className="dy-text-sm dy-font-medium dy-text-foreground dy-truncate dy-flex-1">{value}</p>
445
445
  {copyable && (
446
446
  <Button
447
447
  size="icon"
448
448
  variant="ghost"
449
- className="h-7 w-7 text-muted-foreground hover:text-primary transition-colors"
449
+ className="dy-h-7 dy-w-7 dy-text-muted-foreground hover:dy-text-primary dy-transition-colors"
450
450
  onClick={() => navigator.clipboard.writeText(value)}
451
451
  >
452
- <Copy className="h-3.5 w-3.5" />
452
+ <Copy className="dy-h-3.5 dy-w-3.5" />
453
453
  </Button>
454
454
  )}
455
455
  </div>
@@ -492,7 +492,7 @@ function FileUploader({ collectionSlug, onComplete }: { collectionSlug?: string,
492
492
  }
493
493
 
494
494
  return (
495
- <div className="space-y-6 py-6 px-4">
495
+ <div className="dy-space-y-6 dy-py-6 dy-px-4">
496
496
  <div
497
497
  {...getRootProps()}
498
498
  className={`border-2 border-dashed rounded-2xl p-12 text-center cursor-pointer transition-all duration-300 ${isDragActive
@@ -501,32 +501,32 @@ function FileUploader({ collectionSlug, onComplete }: { collectionSlug?: string,
501
501
  }`}
502
502
  >
503
503
  <input {...getInputProps()} />
504
- <div className="h-16 w-16 rounded-2xl bg-primary/10 flex items-center justify-center mx-auto mb-4">
505
- <Upload className="h-8 w-8 text-primary" />
504
+ <div className="dy-h-16 dy-w-16 dy-rounded-2xl dy-bg-primary/10 dy-flex dy-items-center dy-justify-center dy-mx-auto dy-mb-4">
505
+ <Upload className="dy-h-8 dy-w-8 dy-text-primary" />
506
506
  </div>
507
- <p className="text-xl font-bold text-foreground">Drag & drop assets</p>
508
- <p className="text-sm text-muted-foreground mt-1">or click to browse your files</p>
507
+ <p className="dy-text-xl dy-font-bold dy-text-foreground">Drag & drop assets</p>
508
+ <p className="dy-text-sm dy-text-muted-foreground dy-mt-1">or click to browse your files</p>
509
509
  </div>
510
510
 
511
511
  {files.length > 0 && (
512
- <div className="space-y-4 animate-in fade-in slide-in-from-bottom-4">
513
- <div className="flex items-center justify-between">
514
- <p className="text-sm font-bold text-foreground">{files.length} assets selected</p>
515
- <Button variant="ghost" size="sm" onClick={() => setFiles([])} disabled={uploading} className="text-xs h-8">
512
+ <div className="dy-space-y-4 dy-animate-in dy-fade-in dy-slide-in-from-bottom-4">
513
+ <div className="dy-flex dy-items-center dy-justify-between">
514
+ <p className="dy-text-sm dy-font-bold dy-text-foreground">{files.length} assets selected</p>
515
+ <Button variant="ghost" size="sm" onClick={() => setFiles([])} disabled={uploading} className="dy-text-xs dy-h-8">
516
516
  Clear All
517
517
  </Button>
518
518
  </div>
519
519
 
520
- <div className="max-h-[240px] overflow-auto space-y-2 pr-2 custom-scrollbar">
520
+ <div className="dy-max-h-[240px] dy-overflow-auto dy-space-y-2 dy-pr-2 dy-custom-scrollbar">
521
521
  {files.map((file, idx) => (
522
- <div key={idx} className="flex items-center justify-between p-3 bg-muted/30 border border-border/40 rounded-xl text-sm group transition-colors hover:bg-muted/50">
523
- <div className="flex items-center gap-3 truncate">
524
- <div className="h-8 w-8 rounded-lg bg-white border border-border/60 flex items-center justify-center flex-shrink-0">
525
- <FileIcon className="h-4 w-4 text-muted-foreground" />
522
+ <div key={idx} className="dy-flex dy-items-center dy-justify-between dy-p-3 dy-bg-muted/30 dy-border dy-border-border/40 dy-rounded-xl dy-text-sm dy-group dy-transition-colors hover:dy-bg-muted/50">
523
+ <div className="dy-flex dy-items-center dy-gap-3 dy-truncate">
524
+ <div className="dy-h-8 dy-w-8 dy-rounded-lg dy-bg-white dy-border dy-border-border/60 dy-flex dy-items-center dy-justify-center dy-flex-shrink-0">
525
+ <FileIcon className="dy-h-4 dy-w-4 dy-text-muted-foreground" />
526
526
  </div>
527
- <span className="truncate font-medium text-foreground/80">{file.name}</span>
527
+ <span className="dy-truncate dy-font-medium dy-text-foreground/80">{file.name}</span>
528
528
  </div>
529
- <span className="text-muted-foreground text-[10px] font-bold bg-white px-2 py-1 rounded border border-border/40 ml-4">
529
+ <span className="dy-text-muted-foreground dy-text-[10px] dy-font-bold dy-bg-white dy-px-2 dy-py-1 dy-rounded dy-border dy-border-border/40 dy-ml-4">
530
530
  {(file.size / 1024).toFixed(1)} KB
531
531
  </span>
532
532
  </div>
@@ -534,24 +534,24 @@ function FileUploader({ collectionSlug, onComplete }: { collectionSlug?: string,
534
534
  </div>
535
535
 
536
536
  {uploading && (
537
- <div className="space-y-2 pt-2">
538
- <div className="flex justify-between text-[11px] font-bold uppercase tracking-wider text-muted-foreground">
537
+ <div className="dy-space-y-2 dy-pt-2">
538
+ <div className="dy-flex dy-justify-between dy-text-[11px] dy-font-bold dy-uppercase dy-tracking-wider dy-text-muted-foreground">
539
539
  <span>Uploading...</span>
540
540
  <span>{Math.round(progress)}%</span>
541
541
  </div>
542
- <Progress value={progress} className="h-2 rounded-full" />
542
+ <Progress value={progress} className="dy-h-2 dy-rounded-full" />
543
543
  </div>
544
544
  )}
545
545
 
546
- <div className="flex justify-end pt-4 border-t border-border/40">
546
+ <div className="dy-flex dy-justify-end dy-pt-4 dy-border-t dy-border-border/40">
547
547
  <Button
548
548
  onClick={handleUpload}
549
549
  disabled={uploading || files.length === 0}
550
- className="w-full h-12 rounded-xl bg-primary hover:bg-primary/90 text-white font-bold shadow-lg shadow-primary/20 transition-all active:scale-[0.98]"
550
+ className="dy-w-full dy-h-12 dy-rounded-xl dy-bg-primary hover:dy-bg-primary/90 dy-text-white dy-font-bold dy-shadow-lg dy-shadow-primary/20 dy-transition-all active:dy-scale-[0.98]"
551
551
  >
552
552
  {uploading ? (
553
- <span className="flex items-center gap-2">
554
- <div className="h-4 w-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
553
+ <span className="dy-flex dy-items-center dy-gap-2">
554
+ <div className="dy-h-4 dy-w-4 dy-border-2 dy-border-white/30 dy-border-t-white dy-rounded-full dy-animate-spin" />
555
555
  Uploading Assets...
556
556
  </span>
557
557
  ) : `Upload ${files.length} Assets`}