@actuate-media/cms-admin 0.49.0 → 0.49.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/AdminRoot.d.ts.map +1 -1
  3. package/dist/AdminRoot.js +10 -11
  4. package/dist/AdminRoot.js.map +1 -1
  5. package/dist/__tests__/components/modal.render.test.d.ts +2 -0
  6. package/dist/__tests__/components/modal.render.test.d.ts.map +1 -0
  7. package/dist/__tests__/components/modal.render.test.js +37 -0
  8. package/dist/__tests__/components/modal.render.test.js.map +1 -0
  9. package/dist/actuate-admin.css +1 -1
  10. package/dist/components/FolderTree.js +19 -19
  11. package/dist/components/FolderTree.js.map +1 -1
  12. package/dist/components/MediaPickerModal.d.ts +1 -1
  13. package/dist/components/MediaPickerModal.d.ts.map +1 -1
  14. package/dist/components/MediaPickerModal.js +12 -11
  15. package/dist/components/MediaPickerModal.js.map +1 -1
  16. package/dist/components/SchedulePublishDialog.d.ts +1 -1
  17. package/dist/components/SchedulePublishDialog.d.ts.map +1 -1
  18. package/dist/components/SchedulePublishDialog.js +6 -4
  19. package/dist/components/SchedulePublishDialog.js.map +1 -1
  20. package/dist/components/SharePreviewLinkDialog.d.ts +1 -1
  21. package/dist/components/SharePreviewLinkDialog.d.ts.map +1 -1
  22. package/dist/components/SharePreviewLinkDialog.js +10 -9
  23. package/dist/components/SharePreviewLinkDialog.js.map +1 -1
  24. package/dist/components/VersionHistory.d.ts +1 -1
  25. package/dist/components/VersionHistory.d.ts.map +1 -1
  26. package/dist/components/VersionHistory.js +14 -14
  27. package/dist/components/VersionHistory.js.map +1 -1
  28. package/dist/components/ui/Modal.d.ts +17 -1
  29. package/dist/components/ui/Modal.d.ts.map +1 -1
  30. package/dist/components/ui/Modal.js +16 -15
  31. package/dist/components/ui/Modal.js.map +1 -1
  32. package/dist/fields/BlockBuilderField.d.ts.map +1 -1
  33. package/dist/fields/BlockBuilderField.js +6 -4
  34. package/dist/fields/BlockBuilderField.js.map +1 -1
  35. package/dist/views/MediaBrowser.d.ts.map +1 -1
  36. package/dist/views/MediaBrowser.js +16 -16
  37. package/dist/views/MediaBrowser.js.map +1 -1
  38. package/dist/views/Pages/PagesListView.d.ts.map +1 -1
  39. package/dist/views/Pages/PagesListView.js +3 -3
  40. package/dist/views/Pages/PagesListView.js.map +1 -1
  41. package/dist/views/Posts/PostsListView.d.ts.map +1 -1
  42. package/dist/views/Posts/PostsListView.js +3 -3
  43. package/dist/views/Posts/PostsListView.js.map +1 -1
  44. package/dist/views/Redirects.d.ts.map +1 -1
  45. package/dist/views/Redirects.js +7 -7
  46. package/dist/views/Redirects.js.map +1 -1
  47. package/dist/views/ScriptTagEditor.js +4 -4
  48. package/dist/views/ScriptTagEditor.js.map +1 -1
  49. package/dist/views/ScriptTags.d.ts.map +1 -1
  50. package/dist/views/ScriptTags.js +5 -5
  51. package/dist/views/ScriptTags.js.map +1 -1
  52. package/dist/views/page-builder/AIGenerateDialog.d.ts +1 -1
  53. package/dist/views/page-builder/AIGenerateDialog.d.ts.map +1 -1
  54. package/dist/views/page-builder/AIGenerateDialog.js +23 -23
  55. package/dist/views/page-builder/AIGenerateDialog.js.map +1 -1
  56. package/package.json +1 -1
  57. package/src/AdminRoot.tsx +35 -30
  58. package/src/__tests__/components/modal.render.test.tsx +57 -0
  59. package/src/components/FolderTree.tsx +32 -32
  60. package/src/components/MediaPickerModal.tsx +143 -119
  61. package/src/components/SchedulePublishDialog.tsx +118 -93
  62. package/src/components/SharePreviewLinkDialog.tsx +115 -93
  63. package/src/components/VersionHistory.tsx +98 -82
  64. package/src/components/ui/Modal.tsx +48 -39
  65. package/src/fields/BlockBuilderField.tsx +26 -19
  66. package/src/views/MediaBrowser.tsx +128 -120
  67. package/src/views/Pages/PagesListView.tsx +123 -115
  68. package/src/views/Posts/PostsListView.tsx +114 -110
  69. package/src/views/Redirects.tsx +48 -44
  70. package/src/views/ScriptTagEditor.tsx +40 -40
  71. package/src/views/ScriptTags.tsx +34 -30
  72. package/src/views/page-builder/AIGenerateDialog.tsx +347 -335
@@ -361,7 +361,7 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
361
361
  <button
362
362
  type="button"
363
363
  onClick={() => setSortConfig(toggleSort(sortConfig, sortKey))}
364
- className="flex items-center gap-1 text-xs font-medium text-gray-700 transition-colors hover:text-gray-900"
364
+ className="text-foreground hover:text-foreground flex items-center gap-1 text-xs font-medium transition-colors"
365
365
  >
366
366
  {label}
367
367
  {active ? (
@@ -371,7 +371,7 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
371
371
  <ArrowDown className="h-3 w-3" />
372
372
  )
373
373
  ) : (
374
- <ArrowUpDown className="h-3 w-3 text-gray-400" />
374
+ <ArrowUpDown className="text-muted-foreground h-3 w-3" />
375
375
  )}
376
376
  </button>
377
377
  )
@@ -380,7 +380,7 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
380
380
  if (loading) {
381
381
  return (
382
382
  <div className="flex h-64 items-center justify-center p-3 pr-6 sm:p-4 sm:pr-8">
383
- <Loader2 className="h-6 w-6 animate-spin text-blue-600" />
383
+ <Loader2 className="text-primary h-6 w-6 animate-spin" />
384
384
  </div>
385
385
  )
386
386
  }
@@ -388,12 +388,12 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
388
388
  return (
389
389
  <div className="flex h-full flex-col p-3 pr-6 sm:p-4 sm:pr-8">
390
390
  {error && (
391
- <div className="mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3">
392
- <AlertTriangle className="h-5 w-5 shrink-0 text-red-600" />
393
- <span className="flex-1 text-sm text-red-800">{error}</span>
391
+ <div className="border-destructive/30 bg-destructive/10 mb-4 flex items-center gap-3 rounded-lg border p-3">
392
+ <AlertTriangle className="text-destructive h-5 w-5 shrink-0" />
393
+ <span className="text-destructive flex-1 text-sm">{error}</span>
394
394
  <button
395
395
  onClick={refetch}
396
- className="rounded-lg border border-red-300 px-3 py-1 text-sm text-red-700 transition-colors hover:bg-red-100"
396
+ className="border-destructive/40 text-destructive hover:bg-destructive/15 rounded-lg border px-3 py-1 text-sm transition-colors"
397
397
  >
398
398
  Retry
399
399
  </button>
@@ -405,14 +405,16 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
405
405
  <button
406
406
  type="button"
407
407
  onClick={() => setSidebarOpen((prev) => !prev)}
408
- className="rounded-lg p-1.5 transition-colors hover:bg-gray-100"
408
+ className="hover:bg-muted rounded-lg p-1.5 transition-colors"
409
409
  title={sidebarOpen ? 'Hide folders' : 'Show folders'}
410
410
  >
411
- <FolderInput className="h-5 w-5 text-gray-600" />
411
+ <FolderInput className="text-muted-foreground h-5 w-5" />
412
412
  </button>
413
413
  <div>
414
- <h1 className="mb-1 text-xl font-semibold text-gray-900 sm:text-2xl">Media Library</h1>
415
- <p className="text-sm text-gray-600">{filteredAndSorted.length} files</p>
414
+ <h1 className="text-foreground mb-1 text-xl font-semibold sm:text-2xl">
415
+ Media Library
416
+ </h1>
417
+ <p className="text-muted-foreground text-sm">{filteredAndSorted.length} files</p>
416
418
  </div>
417
419
  </div>
418
420
  <div>
@@ -427,7 +429,7 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
427
429
  <button
428
430
  onClick={() => fileInputRef.current?.click()}
429
431
  disabled={uploading}
430
- className="flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-700 disabled:opacity-50"
432
+ className="bg-primary text-primary-foreground hover:bg-primary/90 flex items-center gap-2 rounded-lg px-4 py-2 text-sm transition-colors disabled:opacity-50"
431
433
  >
432
434
  {uploading ? (
433
435
  <Loader2 className="h-4 w-4 animate-spin" />
@@ -441,7 +443,7 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
441
443
 
442
444
  <div className="flex min-h-0 flex-1 gap-4 overflow-hidden">
443
445
  {sidebarOpen && (
444
- <div className="flex w-56 shrink-0 flex-col overflow-hidden rounded-lg border border-gray-200 bg-white">
446
+ <div className="border-border bg-card flex w-56 shrink-0 flex-col overflow-hidden rounded-lg border">
445
447
  <FolderTree
446
448
  scope="media"
447
449
  selected={folderSel}
@@ -457,23 +459,23 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
457
459
  )}
458
460
 
459
461
  <div className="flex min-w-0 flex-1 flex-col">
460
- <div className="mb-4 rounded-lg border border-gray-200 bg-white">
462
+ <div className="border-border bg-card mb-4 rounded-lg border">
461
463
  <div className="flex items-center justify-between p-3">
462
464
  <div className="flex flex-1 items-center gap-3">
463
465
  <div className="relative max-w-md flex-1">
464
- <Search className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" />
466
+ <Search className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
465
467
  <input
466
468
  type="text"
467
469
  placeholder="Search media..."
468
470
  value={searchQuery}
469
471
  onChange={(e) => setSearchQuery(e.target.value)}
470
- className="w-full rounded-lg border border-gray-300 py-2 pr-3 pl-9 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
472
+ className="border-input focus:ring-ring w-full rounded-lg border py-2 pr-3 pl-9 text-sm focus:ring-2 focus:outline-none"
471
473
  />
472
474
  </div>
473
475
  <select
474
476
  value={filterType}
475
477
  onChange={(e) => setFilterType(e.target.value)}
476
- className="rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
478
+ className="border-input focus:ring-ring rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
477
479
  >
478
480
  <option value="all">All Types</option>
479
481
  <option value="image">Images</option>
@@ -481,16 +483,16 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
481
483
  <option value="document">Documents</option>
482
484
  </select>
483
485
  </div>
484
- <div className="flex items-center gap-1 rounded-lg bg-gray-100 p-1">
486
+ <div className="bg-muted flex items-center gap-1 rounded-lg p-1">
485
487
  <button
486
488
  onClick={() => setViewMode('grid')}
487
- className={`rounded p-1.5 transition-colors ${viewMode === 'grid' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-600 hover:text-gray-900'}`}
489
+ className={`rounded p-1.5 transition-colors ${viewMode === 'grid' ? 'bg-card text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'}`}
488
490
  >
489
491
  <Grid3x3 className="h-4 w-4" />
490
492
  </button>
491
493
  <button
492
494
  onClick={() => setViewMode('list')}
493
- className={`rounded p-1.5 transition-colors ${viewMode === 'list' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-600 hover:text-gray-900'}`}
495
+ className={`rounded p-1.5 transition-colors ${viewMode === 'list' ? 'bg-card text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'}`}
494
496
  >
495
497
  <List className="h-4 w-4" />
496
498
  </button>
@@ -499,9 +501,9 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
499
501
  </div>
500
502
 
501
503
  {selectedMedia.length > 0 && (
502
- <div className="mb-4 rounded-lg border border-blue-200 bg-blue-50 p-3">
504
+ <div className="border-primary/30 bg-primary/10 mb-4 rounded-lg border p-3">
503
505
  <div className="flex items-center justify-between">
504
- <span className="text-sm text-blue-900">
506
+ <span className="text-primary text-sm">
505
507
  {selectedMedia.length} file{selectedMedia.length !== 1 ? 's' : ''} selected
506
508
  </span>
507
509
  <div className="flex items-center gap-2">
@@ -510,13 +512,13 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
510
512
  for (const id of selectedMedia) await deleteMedia(id)
511
513
  setSelectedMedia([])
512
514
  }}
513
- className="rounded-lg bg-red-600 px-3 py-1.5 text-sm text-white transition-colors hover:bg-red-700"
515
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90 rounded-lg px-3 py-1.5 text-sm transition-colors"
514
516
  >
515
517
  Delete Selected
516
518
  </button>
517
519
  <button
518
520
  onClick={() => setSelectedMedia([])}
519
- className="rounded-lg border border-gray-300 bg-white px-3 py-1.5 text-sm transition-colors hover:bg-gray-50"
521
+ className="border-input bg-card hover:bg-muted rounded-lg border px-3 py-1.5 text-sm transition-colors"
520
522
  >
521
523
  Cancel
522
524
  </button>
@@ -527,22 +529,24 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
527
529
 
528
530
  {filteredAndSorted.length === 0 && !loading ? (
529
531
  <div className="flex flex-col items-center justify-center py-16 text-center">
530
- <div className="mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-gray-100">
531
- <ImageIcon className="h-6 w-6 text-gray-400" />
532
+ <div className="bg-muted mb-4 flex h-12 w-12 items-center justify-center rounded-full">
533
+ <ImageIcon className="text-muted-foreground h-6 w-6" />
532
534
  </div>
533
- <h3 className="mb-1 text-sm font-medium text-gray-900">
535
+ <h3 className="text-foreground mb-1 text-sm font-medium">
534
536
  {folderSel.type === 'smart' && folderSel.smart === 'uncategorized'
535
537
  ? 'No uncategorized media'
536
538
  : folderSel.type === 'folder'
537
539
  ? 'No media in this folder'
538
540
  : 'No media yet'}
539
541
  </h3>
540
- <p className="text-sm text-gray-500">Upload your first file to get started.</p>
542
+ <p className="text-muted-foreground text-sm">
543
+ Upload your first file to get started.
544
+ </p>
541
545
  </div>
542
546
  ) : (
543
547
  <div className="flex min-h-0 flex-1 gap-4 overflow-hidden">
544
548
  <div
545
- className={`overflow-hidden rounded-lg border border-gray-200 bg-white transition-all duration-200 ${panelOpen ? 'min-w-0 flex-1' : 'w-full'}`}
549
+ className={`border-border bg-card overflow-hidden rounded-lg border transition-all duration-200 ${panelOpen ? 'min-w-0 flex-1' : 'w-full'}`}
546
550
  >
547
551
  {viewMode === 'grid' ? (
548
552
  <div
@@ -567,25 +571,25 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
567
571
  key={item.id}
568
572
  className={`group relative flex cursor-pointer flex-col overflow-hidden rounded-lg border-2 transition-all ${
569
573
  isActive
570
- ? 'border-blue-500 ring-2 ring-blue-200'
574
+ ? 'border-primary ring-primary/30 ring-2'
571
575
  : selectedMedia.includes(item.id)
572
- ? 'border-blue-400 ring-1 ring-blue-100'
573
- : 'border-gray-200 hover:border-gray-300'
576
+ ? 'border-primary/60 ring-primary/20 ring-1'
577
+ : 'border-border hover:border-input'
574
578
  }`}
575
579
  onClick={() => openDetail(item)}
576
580
  draggable
577
581
  onDragStart={(e) => handleDragStart(e, item.id)}
578
582
  >
579
583
  <div className="relative aspect-square w-full overflow-hidden">
580
- <div className="flex h-full w-full items-center justify-center bg-gray-100">
584
+ <div className="bg-muted flex h-full w-full items-center justify-center">
581
585
  <MediaPreview item={item} />
582
586
  </div>
583
587
  {hasIssues && (
584
588
  <div
585
- className="absolute top-1.5 left-1.5 flex h-5 w-5 items-center justify-center rounded-full bg-yellow-500"
589
+ className="bg-warning absolute top-1.5 left-1.5 flex h-5 w-5 items-center justify-center rounded-full"
586
590
  title="Needs attention"
587
591
  >
588
- <AlertTriangle className="h-3 w-3 text-white" />
592
+ <AlertTriangle className="text-warning-foreground h-3 w-3" />
589
593
  </div>
590
594
  )}
591
595
  <div
@@ -595,13 +599,13 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
595
599
  <div
596
600
  className={`flex h-5 w-5 items-center justify-center rounded border-2 transition-colors ${
597
601
  selectedMedia.includes(item.id)
598
- ? 'border-blue-600 bg-blue-600'
599
- : 'border-gray-400 bg-white/80'
602
+ ? 'border-primary bg-primary'
603
+ : 'border-input bg-card/80'
600
604
  }`}
601
605
  >
602
606
  {selectedMedia.includes(item.id) && (
603
607
  <svg
604
- className="h-3 w-3 text-white"
608
+ className="text-primary-foreground h-3 w-3"
605
609
  fill="none"
606
610
  viewBox="0 0 12 12"
607
611
  >
@@ -617,18 +621,18 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
617
621
  </div>
618
622
  </div>
619
623
  </div>
620
- <div className="flex items-center gap-2 border-t border-gray-200 bg-white px-2 py-1.5">
624
+ <div className="border-border bg-card flex items-center gap-2 border-t px-2 py-1.5">
621
625
  <span
622
626
  className={`mt-0.5 h-2 w-2 shrink-0 self-start rounded-full ${
623
- hasIssues ? 'bg-yellow-500' : 'bg-green-500'
627
+ hasIssues ? 'bg-warning' : 'bg-success'
624
628
  }`}
625
629
  title={hasIssues ? 'Needs attention' : 'SEO complete'}
626
630
  />
627
631
  <div className="min-w-0 flex-1">
628
- <p className="truncate text-xs font-medium text-gray-900">
632
+ <p className="text-foreground truncate text-xs font-medium">
629
633
  {item.name}
630
634
  </p>
631
- <p className="truncate text-xs text-gray-500">
635
+ <p className="text-muted-foreground truncate text-xs">
632
636
  {[typeLabel, item.size].filter(Boolean).join(' · ')}
633
637
  </p>
634
638
  </div>
@@ -640,7 +644,7 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
640
644
  ) : (
641
645
  <div className="h-full overflow-y-auto">
642
646
  <table className="w-full">
643
- <thead className="sticky top-0 border-b border-gray-200 bg-gray-50">
647
+ <thead className="border-border bg-muted sticky top-0 border-b">
644
648
  <tr>
645
649
  <th className="w-8 px-3 py-2 text-left">
646
650
  <input
@@ -650,7 +654,7 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
650
654
  filteredAndSorted.length > 0
651
655
  }
652
656
  onChange={handleSelectAll}
653
- className="rounded border-gray-300"
657
+ className="border-input rounded"
654
658
  />
655
659
  </th>
656
660
  <th className="w-6 px-1 py-2"></th>
@@ -666,19 +670,19 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
666
670
  <th className="px-3 py-2 text-left">
667
671
  <SortHeader label="Uploaded" sortKey="date" />
668
672
  </th>
669
- <th className="px-3 py-2 text-left text-xs font-medium text-gray-700">
673
+ <th className="text-foreground px-3 py-2 text-left text-xs font-medium">
670
674
  Status
671
675
  </th>
672
676
  </tr>
673
677
  </thead>
674
- <tbody className="divide-y divide-gray-200">
678
+ <tbody className="divide-border divide-y">
675
679
  {filteredAndSorted.map((item) => {
676
680
  const isActive = activeItem?.id === item.id
677
681
  const hasIssues = !item.altTag || !item.title || item.usedOn?.length === 0
678
682
  return (
679
683
  <tr
680
684
  key={item.id}
681
- className={`cursor-pointer transition-colors ${isActive ? 'bg-blue-50' : 'hover:bg-gray-50'}`}
685
+ className={`cursor-pointer transition-colors ${isActive ? 'bg-primary/10' : 'hover:bg-muted'}`}
682
686
  onClick={() => openDetail(item)}
683
687
  draggable
684
688
  onDragStart={(e) => handleDragStart(e, item.id)}
@@ -693,34 +697,38 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
693
697
  item.id,
694
698
  )
695
699
  }
696
- className="rounded border-gray-300"
700
+ className="border-input rounded"
697
701
  />
698
702
  </td>
699
703
  <td className="cursor-grab px-1 py-2">
700
- <GripVertical className="h-4 w-4 text-gray-300" />
704
+ <GripVertical className="text-muted-foreground h-4 w-4" />
701
705
  </td>
702
706
  <td className="px-3 py-2">
703
707
  <div className="flex items-center gap-3">
704
- <div className="flex h-10 w-10 items-center justify-center overflow-hidden rounded bg-gray-100">
708
+ <div className="bg-muted flex h-10 w-10 items-center justify-center overflow-hidden rounded">
705
709
  <MediaPreview item={item} />
706
710
  </div>
707
- <span className="text-sm font-medium text-gray-900">
711
+ <span className="text-foreground text-sm font-medium">
708
712
  {item.name}
709
713
  </span>
710
714
  </div>
711
715
  </td>
712
- <td className="px-3 py-2 text-sm text-gray-600">
716
+ <td className="text-muted-foreground px-3 py-2 text-sm">
713
717
  {item.format ?? item.type}
714
718
  </td>
715
- <td className="px-3 py-2 text-sm text-gray-600">{item.size}</td>
716
- <td className="px-3 py-2 text-sm text-gray-600">{item.date}</td>
719
+ <td className="text-muted-foreground px-3 py-2 text-sm">
720
+ {item.size}
721
+ </td>
722
+ <td className="text-muted-foreground px-3 py-2 text-sm">
723
+ {item.date}
724
+ </td>
717
725
  <td className="px-3 py-2">
718
726
  {hasIssues ? (
719
- <span className="inline-flex items-center gap-1 rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-800">
727
+ <span className="bg-warning/15 text-warning inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium">
720
728
  <AlertTriangle className="h-3 w-3" /> Needs attention
721
729
  </span>
722
730
  ) : (
723
- <span className="inline-flex items-center rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800">
731
+ <span className="bg-success/15 text-success inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium">
724
732
  Complete
725
733
  </span>
726
734
  )}
@@ -735,42 +743,42 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
735
743
  </div>
736
744
 
737
745
  {panelOpen && activeItem && (
738
- <div className="flex w-80 shrink-0 flex-col overflow-y-auto rounded-lg border border-gray-200 bg-white lg:w-96">
739
- <div className="sticky top-0 z-10 flex items-center justify-between border-b border-gray-200 bg-white p-4">
740
- <h3 className="truncate text-sm font-semibold text-gray-900">
746
+ <div className="border-border bg-card flex w-80 shrink-0 flex-col overflow-y-auto rounded-lg border lg:w-96">
747
+ <div className="border-border bg-card sticky top-0 z-10 flex items-center justify-between border-b p-4">
748
+ <h3 className="text-foreground truncate text-sm font-semibold">
741
749
  {activeItem.name}
742
750
  </h3>
743
751
  <button
744
752
  onClick={closeDetail}
745
- className="rounded p-1 transition-colors hover:bg-gray-100"
753
+ className="hover:bg-muted rounded p-1 transition-colors"
746
754
  aria-label="Close panel"
747
755
  >
748
- <X className="h-4 w-4 text-gray-500" />
756
+ <X className="text-muted-foreground h-4 w-4" />
749
757
  </button>
750
758
  </div>
751
759
 
752
760
  <div className="flex-1 overflow-y-auto">
753
- <div className="border-b border-gray-200 p-4">
754
- <div className="flex aspect-video items-center justify-center overflow-hidden rounded-lg bg-gray-100">
761
+ <div className="border-border border-b p-4">
762
+ <div className="bg-muted flex aspect-video items-center justify-center overflow-hidden rounded-lg">
755
763
  {isImageMedia(activeItem) ? (
756
764
  <MediaPreview item={activeItem} />
757
765
  ) : (
758
- <ImageIcon className="h-12 w-12 text-gray-300" />
766
+ <ImageIcon className="text-muted-foreground h-12 w-12" />
759
767
  )}
760
768
  </div>
761
769
  </div>
762
770
 
763
771
  {issues.length > 0 && (
764
- <div className="mx-4 mt-4 rounded-lg border border-yellow-200 bg-yellow-50 p-3">
772
+ <div className="border-warning/30 bg-warning/10 mx-4 mt-4 rounded-lg border p-3">
765
773
  <div className="flex items-start gap-2">
766
- <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-yellow-600" />
774
+ <AlertTriangle className="text-warning mt-0.5 h-4 w-4 shrink-0" />
767
775
  <div>
768
- <p className="mb-1 text-xs font-semibold text-yellow-900">
776
+ <p className="text-warning mb-1 text-xs font-semibold">
769
777
  {issues.length} issue{issues.length !== 1 ? 's' : ''} found
770
778
  </p>
771
779
  <ul className="space-y-0.5">
772
780
  {issues.map((issue, i) => (
773
- <li key={i} className="text-xs text-yellow-800">
781
+ <li key={i} className="text-warning text-xs">
774
782
  • {issue}
775
783
  </li>
776
784
  ))}
@@ -784,7 +792,7 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
784
792
  if (!activeItem.title) await handleAiGenerate('title')
785
793
  if (activeItem.sizeBytes > 2000000) await handleAiGenerate('optimize')
786
794
  }}
787
- className="mt-2 flex w-full items-center justify-center gap-1.5 rounded-lg bg-yellow-600 px-3 py-1.5 text-xs text-white transition-colors hover:bg-yellow-700"
795
+ className="bg-warning text-warning-foreground hover:bg-warning/90 mt-2 flex w-full items-center justify-center gap-1.5 rounded-lg px-3 py-1.5 text-xs transition-colors"
788
796
  >
789
797
  <Sparkles className="h-3.5 w-3.5" />
790
798
  AI Fix All Issues
@@ -792,68 +800,68 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
792
800
  </div>
793
801
  )}
794
802
 
795
- <div className="space-y-3 border-b border-gray-200 p-4">
796
- <h4 className="text-xs font-semibold tracking-wide text-gray-500 uppercase">
803
+ <div className="border-border space-y-3 border-b p-4">
804
+ <h4 className="text-muted-foreground text-xs font-semibold tracking-wide uppercase">
797
805
  File Information
798
806
  </h4>
799
807
  <div className="grid grid-cols-2 gap-3">
800
808
  <div>
801
- <div className="mb-0.5 text-xs text-gray-500">Format</div>
802
- <div className="text-sm text-gray-900">
809
+ <div className="text-muted-foreground mb-0.5 text-xs">Format</div>
810
+ <div className="text-foreground text-sm">
803
811
  {activeItem.format ?? 'Unknown'}
804
812
  </div>
805
813
  </div>
806
814
  <div>
807
- <div className="mb-0.5 text-xs text-gray-500">File Size</div>
808
- <div className="flex items-center gap-1 text-sm text-gray-900">
815
+ <div className="text-muted-foreground mb-0.5 text-xs">File Size</div>
816
+ <div className="text-foreground flex items-center gap-1 text-sm">
809
817
  {activeItem.size}
810
818
  {activeItem.sizeBytes > 2000000 && (
811
- <span className="text-xs text-yellow-600">(large)</span>
819
+ <span className="text-warning text-xs">(large)</span>
812
820
  )}
813
821
  </div>
814
822
  </div>
815
823
  <div>
816
- <div className="mb-0.5 text-xs text-gray-500">Dimensions</div>
817
- <div className="text-sm text-gray-900">
824
+ <div className="text-muted-foreground mb-0.5 text-xs">Dimensions</div>
825
+ <div className="text-foreground text-sm">
818
826
  {activeItem.dimensions ?? '—'}
819
827
  </div>
820
828
  </div>
821
829
  <div>
822
- <div className="mb-0.5 text-xs text-gray-500">Uploaded</div>
823
- <div className="text-sm text-gray-900">{activeItem.date}</div>
830
+ <div className="text-muted-foreground mb-0.5 text-xs">Uploaded</div>
831
+ <div className="text-foreground text-sm">{activeItem.date}</div>
824
832
  </div>
825
833
  </div>
826
834
 
827
835
  <div>
828
- <div className="mb-1 text-xs text-gray-500">URL</div>
836
+ <div className="text-muted-foreground mb-1 text-xs">URL</div>
829
837
  <div className="flex items-center gap-1">
830
- <code className="flex-1 truncate rounded border border-gray-200 bg-gray-50 px-2 py-1.5 text-xs text-gray-700">
838
+ <code className="border-border bg-muted text-foreground flex-1 truncate rounded border px-2 py-1.5 text-xs">
831
839
  {activeItem.url}
832
840
  </code>
833
841
  <button
834
842
  onClick={handleCopyUrl}
835
- className="shrink-0 rounded p-1.5 transition-colors hover:bg-gray-100"
843
+ className="hover:bg-muted shrink-0 rounded p-1.5 transition-colors"
836
844
  title="Copy URL"
837
845
  >
838
- <Copy className="h-3.5 w-3.5 text-gray-500" />
846
+ <Copy className="text-muted-foreground h-3.5 w-3.5" />
839
847
  </button>
840
848
  </div>
841
849
  </div>
842
850
  </div>
843
851
 
844
- <div className="space-y-4 border-b border-gray-200 p-4">
845
- <h4 className="text-xs font-semibold tracking-wide text-gray-500 uppercase">
852
+ <div className="border-border space-y-4 border-b p-4">
853
+ <h4 className="text-muted-foreground text-xs font-semibold tracking-wide uppercase">
846
854
  SEO & Accessibility
847
855
  </h4>
848
856
 
849
857
  <div>
850
858
  <div className="mb-1 flex items-center justify-between">
851
- <label className="text-sm font-medium text-gray-700">Alt Tag</label>
859
+ <label className="text-foreground text-sm font-medium">Alt Tag</label>
852
860
  <button
853
861
  type="button"
854
862
  onClick={() => handleAiGenerate('alt')}
855
863
  disabled={aiGenerating === 'alt'}
856
- className="flex items-center gap-1 text-xs text-indigo-600 transition-colors hover:text-indigo-700 disabled:opacity-50"
864
+ className="text-primary hover:text-primary/80 flex items-center gap-1 text-xs transition-colors disabled:opacity-50"
857
865
  >
858
866
  <Bot className="h-3.5 w-3.5" />
859
867
  {aiGenerating === 'alt' ? 'Generating...' : 'AI Generate'}
@@ -864,10 +872,10 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
864
872
  onChange={(e) => setEditAlt(e.target.value)}
865
873
  placeholder="Describe this image for accessibility..."
866
874
  rows={2}
867
- className="w-full resize-none rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
875
+ className="border-input focus:ring-ring w-full resize-none rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
868
876
  />
869
877
  {!editAlt && (
870
- <p className="mt-1 text-xs text-red-500">
878
+ <p className="text-destructive mt-1 text-xs">
871
879
  Required for accessibility and SEO
872
880
  </p>
873
881
  )}
@@ -875,12 +883,12 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
875
883
 
876
884
  <div>
877
885
  <div className="mb-1 flex items-center justify-between">
878
- <label className="text-sm font-medium text-gray-700">Title</label>
886
+ <label className="text-foreground text-sm font-medium">Title</label>
879
887
  <button
880
888
  type="button"
881
889
  onClick={() => handleAiGenerate('title')}
882
890
  disabled={aiGenerating === 'title'}
883
- className="flex items-center gap-1 text-xs text-indigo-600 transition-colors hover:text-indigo-700 disabled:opacity-50"
891
+ className="text-primary hover:text-primary/80 flex items-center gap-1 text-xs transition-colors disabled:opacity-50"
884
892
  >
885
893
  <Bot className="h-3.5 w-3.5" />
886
894
  {aiGenerating === 'title' ? 'Generating...' : 'AI Generate'}
@@ -891,25 +899,25 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
891
899
  value={editTitle}
892
900
  onChange={(e) => setEditTitle(e.target.value)}
893
901
  placeholder="Image title..."
894
- className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
902
+ className="border-input focus:ring-ring w-full rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
895
903
  />
896
904
  </div>
897
905
 
898
906
  <div>
899
- <label className="mb-1 block text-sm font-medium text-gray-700">
907
+ <label className="text-foreground mb-1 block text-sm font-medium">
900
908
  File Name
901
909
  </label>
902
910
  <input
903
911
  type="text"
904
912
  value={editFilename}
905
913
  onChange={(e) => setEditFilename(e.target.value)}
906
- className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
914
+ className="border-input focus:ring-ring w-full rounded-lg border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
907
915
  />
908
916
  </div>
909
917
  </div>
910
918
 
911
- <div className="border-b border-gray-200 p-4">
912
- <h4 className="mb-3 text-xs font-semibold tracking-wide text-gray-500 uppercase">
919
+ <div className="border-border border-b p-4">
920
+ <h4 className="text-muted-foreground mb-3 text-xs font-semibold tracking-wide uppercase">
913
921
  Used On {activeItem.usedOn && `(${activeItem.usedOn.length})`}
914
922
  </h4>
915
923
  {activeItem.usedOn && activeItem.usedOn.length > 0 ? (
@@ -919,23 +927,23 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
919
927
  key={i}
920
928
  type="button"
921
929
  onClick={() => onNavigate?.(usage.path)}
922
- className="flex w-full items-center gap-2 rounded-lg border border-gray-200 p-2 text-left transition-colors hover:bg-gray-50"
930
+ className="border-border hover:bg-muted flex w-full items-center gap-2 rounded-lg border p-2 text-left transition-colors"
923
931
  >
924
- <Link2 className="h-4 w-4 shrink-0 text-gray-400" />
925
- <span className="flex-1 truncate text-sm text-gray-900">
932
+ <Link2 className="text-muted-foreground h-4 w-4 shrink-0" />
933
+ <span className="text-foreground flex-1 truncate text-sm">
926
934
  {usage.page}
927
935
  </span>
928
- <ExternalLink className="h-3.5 w-3.5 shrink-0 text-gray-400" />
936
+ <ExternalLink className="text-muted-foreground h-3.5 w-3.5 shrink-0" />
929
937
  </button>
930
938
  ))}
931
939
  </div>
932
940
  ) : (
933
- <div className="rounded-lg border border-orange-200 bg-orange-50 p-3">
941
+ <div className="border-warning/30 bg-warning/10 rounded-lg border p-3">
934
942
  <div className="flex items-start gap-2">
935
- <AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-orange-600" />
943
+ <AlertTriangle className="text-warning mt-0.5 h-4 w-4 shrink-0" />
936
944
  <div>
937
- <p className="text-xs font-medium text-orange-900">Orphaned media</p>
938
- <p className="mt-0.5 text-xs text-orange-700">
945
+ <p className="text-warning text-xs font-medium">Orphaned media</p>
946
+ <p className="text-warning mt-0.5 text-xs">
939
947
  This file isn&apos;t used on any page. Consider deleting it to save
940
948
  storage.
941
949
  </p>
@@ -946,37 +954,37 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
946
954
  </div>
947
955
 
948
956
  <div className="space-y-3 p-4">
949
- <h4 className="text-xs font-semibold tracking-wide text-gray-500 uppercase">
957
+ <h4 className="text-muted-foreground text-xs font-semibold tracking-wide uppercase">
950
958
  AI Optimization
951
959
  </h4>
952
960
  <button
953
961
  type="button"
954
962
  onClick={() => handleAiGenerate('optimize')}
955
963
  disabled={aiGenerating === 'optimize'}
956
- className="flex w-full items-center gap-2 rounded-lg border border-indigo-200 bg-indigo-50 p-3 text-left transition-colors hover:bg-indigo-100 disabled:opacity-50"
964
+ className="border-primary/30 bg-primary/10 hover:bg-primary/15 flex w-full items-center gap-2 rounded-lg border p-3 text-left transition-colors disabled:opacity-50"
957
965
  >
958
966
  <Sparkles
959
- className={`h-5 w-5 shrink-0 text-indigo-600 ${aiGenerating === 'optimize' ? 'animate-spin' : ''}`}
967
+ className={`text-primary h-5 w-5 shrink-0 ${aiGenerating === 'optimize' ? 'animate-spin' : ''}`}
960
968
  />
961
969
  <div className="flex-1">
962
- <div className="text-sm font-medium text-indigo-900">
970
+ <div className="text-primary text-sm font-medium">
963
971
  {aiGenerating === 'optimize' ? 'Optimizing...' : 'Optimize Image'}
964
972
  </div>
965
- <div className="mt-0.5 text-xs text-indigo-700">
973
+ <div className="text-primary mt-0.5 text-xs">
966
974
  Compress and convert to modern format (WebP/AVIF)
967
975
  </div>
968
976
  </div>
969
977
  </button>
970
978
  <button
971
979
  type="button"
972
- className="flex w-full items-center gap-2 rounded-lg border border-gray-200 p-3 text-left transition-colors hover:bg-gray-50"
980
+ className="border-border hover:bg-muted flex w-full items-center gap-2 rounded-lg border p-3 text-left transition-colors"
973
981
  >
974
- <Bot className="h-5 w-5 shrink-0 text-gray-500" />
982
+ <Bot className="text-muted-foreground h-5 w-5 shrink-0" />
975
983
  <div className="flex-1">
976
- <div className="text-sm font-medium text-gray-900">
984
+ <div className="text-foreground text-sm font-medium">
977
985
  AI Content Analysis
978
986
  </div>
979
- <div className="mt-0.5 text-xs text-gray-600">
987
+ <div className="text-muted-foreground mt-0.5 text-xs">
980
988
  Detect objects, faces, text, and suggest categories
981
989
  </div>
982
990
  </div>
@@ -984,30 +992,30 @@ export function MediaBrowser({ onNavigate }: MediaBrowserProps) {
984
992
  </div>
985
993
  </div>
986
994
 
987
- <div className="sticky bottom-0 flex items-center gap-2 border-t border-gray-200 bg-white p-4">
995
+ <div className="border-border bg-card sticky bottom-0 flex items-center gap-2 border-t p-4">
988
996
  <button
989
997
  type="button"
990
998
  onClick={handleSaveDetails}
991
999
  disabled={saving}
992
- className="flex flex-1 items-center justify-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-700 disabled:opacity-50"
1000
+ className="bg-primary text-primary-foreground hover:bg-primary/90 flex flex-1 items-center justify-center gap-2 rounded-lg px-4 py-2 text-sm transition-colors disabled:opacity-50"
993
1001
  >
994
1002
  {saving && <Loader2 className="h-4 w-4 animate-spin" />}
995
1003
  {saving ? 'Saving...' : 'Save Changes'}
996
1004
  </button>
997
1005
  <button
998
1006
  type="button"
999
- className="rounded-lg p-2 transition-colors hover:bg-gray-100"
1007
+ className="hover:bg-muted rounded-lg p-2 transition-colors"
1000
1008
  title="Download"
1001
1009
  >
1002
- <Download className="h-4 w-4 text-gray-600" />
1010
+ <Download className="text-muted-foreground h-4 w-4" />
1003
1011
  </button>
1004
1012
  <button
1005
1013
  type="button"
1006
1014
  onClick={() => deleteMedia(activeItem.id)}
1007
- className="rounded-lg p-2 transition-colors hover:bg-gray-100"
1015
+ className="hover:bg-muted rounded-lg p-2 transition-colors"
1008
1016
  title="Delete"
1009
1017
  >
1010
- <Trash2 className="h-4 w-4 text-red-600" />
1018
+ <Trash2 className="text-destructive h-4 w-4" />
1011
1019
  </button>
1012
1020
  </div>
1013
1021
  </div>