@actuate-media/cms-admin 0.49.1 → 0.49.4

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