@jhits/plugin-images 0.0.14 → 0.0.15

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 (94) hide show
  1. package/dist/components/Image.d.ts +1 -6
  2. package/dist/components/Image.d.ts.map +1 -1
  3. package/dist/components/Image.js +86 -202
  4. package/dist/components/ImageEditor.d.ts.map +1 -1
  5. package/dist/components/ImageEditor.js +21 -125
  6. package/dist/components/ImagePicker.d.ts.map +1 -1
  7. package/dist/components/ImagePicker.js +6 -59
  8. package/dist/utils/fallback.d.ts +9 -4
  9. package/dist/utils/fallback.d.ts.map +1 -1
  10. package/dist/utils/fallback.js +40 -12
  11. package/dist/utils/transforms.d.ts.map +1 -1
  12. package/dist/utils/transforms.js +7 -10
  13. package/dist/views/ImageManager/components/CleanupLibraryModal.d.ts +12 -0
  14. package/dist/views/ImageManager/components/CleanupLibraryModal.d.ts.map +1 -0
  15. package/dist/views/ImageManager/components/CleanupLibraryModal.js +7 -0
  16. package/dist/views/ImageManager/components/DeleteImageModal.d.ts +15 -0
  17. package/dist/views/ImageManager/components/DeleteImageModal.d.ts.map +1 -0
  18. package/dist/views/ImageManager/components/DeleteImageModal.js +8 -0
  19. package/dist/views/ImageManager/components/ImageGrid.d.ts +12 -0
  20. package/dist/views/ImageManager/components/ImageGrid.d.ts.map +1 -0
  21. package/dist/views/ImageManager/components/ImageGrid.js +15 -0
  22. package/dist/views/ImageManager/components/ImageManagerHeader.d.ts +11 -0
  23. package/dist/views/ImageManager/components/ImageManagerHeader.d.ts.map +1 -0
  24. package/dist/views/ImageManager/components/ImageManagerHeader.js +6 -0
  25. package/dist/views/ImageManager/components/ImageManagerStats.d.ts +8 -0
  26. package/dist/views/ImageManager/components/ImageManagerStats.d.ts.map +1 -0
  27. package/dist/views/ImageManager/components/ImageManagerStats.js +6 -0
  28. package/dist/views/ImageManager/components/ImageManagerToolbar.d.ts +9 -0
  29. package/dist/views/ImageManager/components/ImageManagerToolbar.d.ts.map +1 -0
  30. package/dist/views/ImageManager/components/ImageManagerToolbar.js +10 -0
  31. package/dist/views/ImageManager/components/ImageTable.d.ts +13 -0
  32. package/dist/views/ImageManager/components/ImageTable.d.ts.map +1 -0
  33. package/dist/views/ImageManager/components/ImageTable.js +13 -0
  34. package/dist/views/ImageManager/types.d.ts +26 -0
  35. package/dist/views/ImageManager/types.d.ts.map +1 -0
  36. package/dist/views/ImageManager/types.js +1 -0
  37. package/dist/views/ImageManager.d.ts +1 -1
  38. package/dist/views/ImageManager.d.ts.map +1 -1
  39. package/dist/views/ImageManager.js +28 -52
  40. package/package.json +10 -9
  41. package/src/components/Image.tsx +107 -262
  42. package/src/components/ImageEditor.tsx +31 -193
  43. package/src/components/ImagePicker.tsx +22 -107
  44. package/src/utils/fallback.ts +46 -13
  45. package/src/utils/transforms.ts +9 -12
  46. package/src/views/ImageManager/components/CleanupLibraryModal.tsx +96 -0
  47. package/src/views/ImageManager/components/DeleteImageModal.tsx +144 -0
  48. package/src/views/ImageManager/components/ImageGrid.tsx +119 -0
  49. package/src/views/ImageManager/components/ImageManagerHeader.tsx +72 -0
  50. package/src/views/ImageManager/components/ImageManagerStats.tsx +60 -0
  51. package/src/views/ImageManager/components/ImageManagerToolbar.tsx +60 -0
  52. package/src/views/ImageManager/components/ImageTable.tsx +120 -0
  53. package/src/views/ImageManager/types.ts +27 -0
  54. package/src/views/ImageManager.tsx +103 -571
  55. package/src/components/BackgroundImage.d.ts +0 -11
  56. package/src/components/BackgroundImage.d.ts.map +0 -1
  57. package/src/components/GlobalImageEditor/config.d.ts +0 -9
  58. package/src/components/GlobalImageEditor/config.d.ts.map +0 -1
  59. package/src/components/GlobalImageEditor/eventHandlers.d.ts +0 -20
  60. package/src/components/GlobalImageEditor/eventHandlers.d.ts.map +0 -1
  61. package/src/components/GlobalImageEditor/imageDetection.d.ts +0 -16
  62. package/src/components/GlobalImageEditor/imageDetection.d.ts.map +0 -1
  63. package/src/components/GlobalImageEditor/imageSetup.d.ts +0 -9
  64. package/src/components/GlobalImageEditor/imageSetup.d.ts.map +0 -1
  65. package/src/components/GlobalImageEditor/saveLogic.d.ts +0 -26
  66. package/src/components/GlobalImageEditor/saveLogic.d.ts.map +0 -1
  67. package/src/components/GlobalImageEditor/stylingDetection.d.ts +0 -9
  68. package/src/components/GlobalImageEditor/stylingDetection.d.ts.map +0 -1
  69. package/src/components/GlobalImageEditor/transformParsing.d.ts +0 -16
  70. package/src/components/GlobalImageEditor/transformParsing.d.ts.map +0 -1
  71. package/src/components/GlobalImageEditor/types.d.ts +0 -36
  72. package/src/components/GlobalImageEditor/types.d.ts.map +0 -1
  73. package/src/components/GlobalImageEditor.d.ts +0 -8
  74. package/src/components/GlobalImageEditor.d.ts.map +0 -1
  75. package/src/components/Image.d.ts +0 -22
  76. package/src/components/Image.d.ts.map +0 -1
  77. package/src/components/ImageBrowserModal.d.ts +0 -13
  78. package/src/components/ImageBrowserModal.d.ts.map +0 -1
  79. package/src/components/ImageEditor.d.ts +0 -27
  80. package/src/components/ImageEditor.d.ts.map +0 -1
  81. package/src/components/ImagePicker.d.ts +0 -3
  82. package/src/components/ImagePicker.d.ts.map +0 -1
  83. package/src/components/ImagesPluginInit.d.ts +0 -24
  84. package/src/components/ImagesPluginInit.d.ts.map +0 -1
  85. package/src/hooks/useImagePicker.d.ts +0 -20
  86. package/src/hooks/useImagePicker.d.ts.map +0 -1
  87. package/src/types/index.d.ts +0 -80
  88. package/src/types/index.d.ts.map +0 -1
  89. package/src/utils/fallback.d.ts +0 -27
  90. package/src/utils/fallback.d.ts.map +0 -1
  91. package/src/utils/transforms.d.ts +0 -26
  92. package/src/utils/transforms.d.ts.map +0 -1
  93. package/src/views/ImageManager.d.ts +0 -10
  94. package/src/views/ImageManager.d.ts.map +0 -1
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Image Manager View
3
- * Full-featured image management with CRUD operations and usage tracking
3
+ * Refactored modular interface for media management
4
4
  */
5
5
  export interface ImageManagerViewProps {
6
6
  siteId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ImageManager.d.ts","sourceRoot":"","sources":["../../src/views/ImageManager.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAuDH,MAAM,WAAW,qBAAqB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB;AAqBD,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,qBAAqB,2CAssBzE"}
1
+ {"version":3,"file":"ImageManager.d.ts","sourceRoot":"","sources":["../../src/views/ImageManager.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAsBH,MAAM,WAAW,qBAAqB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB;AAmBD,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,qBAAqB,2CAqRzE"}
@@ -1,12 +1,19 @@
1
1
  /**
2
2
  * Image Manager View
3
- * Full-featured image management with CRUD operations and usage tracking
3
+ * Refactored modular interface for media management
4
4
  */
5
5
  'use client';
6
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
6
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  import { useState, useEffect, useMemo, useCallback } from 'react';
8
- import { Search, Trash2, Upload, Image as ImageIcon, Grid3x3, List, Loader2, AlertTriangle, CheckCircle, ExternalLink, X, RefreshCw, Eye, HardDrive, CheckCircle2, Circle, Trash } from 'lucide-react';
9
- import Image from 'next/image';
8
+ import { ImageIcon, Loader2 } from 'lucide-react';
9
+ // Components
10
+ import { ImageManagerHeader } from './ImageManager/components/ImageManagerHeader';
11
+ import { ImageManagerStats } from './ImageManager/components/ImageManagerStats';
12
+ import { ImageManagerToolbar } from './ImageManager/components/ImageManagerToolbar';
13
+ import { ImageGrid } from './ImageManager/components/ImageGrid';
14
+ import { ImageTable } from './ImageManager/components/ImageTable';
15
+ import { DeleteImageModal } from './ImageManager/components/DeleteImageModal';
16
+ import { CleanupLibraryModal } from './ImageManager/components/CleanupLibraryModal';
10
17
  function formatFileSize(bytes) {
11
18
  if (bytes === 0)
12
19
  return '0 B';
@@ -17,12 +24,10 @@ function formatFileSize(bytes) {
17
24
  }
18
25
  function formatDate(dateString) {
19
26
  const date = new Date(dateString);
20
- return date.toLocaleDateString('en-US', {
27
+ return date.toLocaleDateString(undefined, {
21
28
  year: 'numeric',
22
29
  month: 'short',
23
- day: 'numeric',
24
- hour: '2-digit',
25
- minute: '2-digit'
30
+ day: 'numeric'
26
31
  });
27
32
  }
28
33
  export function ImageManagerView({ siteId, locale }) {
@@ -96,6 +101,7 @@ export function ImageManagerView({ siteId, locale }) {
96
101
  setImages(prev => [data.image, ...prev]);
97
102
  }
98
103
  }
104
+ handleRefresh();
99
105
  }
100
106
  catch (error) {
101
107
  console.error('Upload failed:', error);
@@ -118,6 +124,7 @@ export function ImageManagerView({ siteId, locale }) {
118
124
  setImages(prev => prev.filter(img => img.id !== selectedImage.id));
119
125
  setShowDeleteModal(false);
120
126
  setSelectedImage(null);
127
+ handleRefresh();
121
128
  }
122
129
  else {
123
130
  const error = await response.json();
@@ -156,18 +163,19 @@ export function ImageManagerView({ siteId, locale }) {
156
163
  }
157
164
  if (deletedCount > 0) {
158
165
  setImages(prev => prev.filter(img => isImageInUse(img.filename)));
166
+ handleRefresh();
159
167
  }
160
168
  setShowCleanupModal(false);
161
169
  if (failedCount > 0) {
162
170
  alert(`Deleted ${deletedCount} images. ${failedCount} failed.`);
163
171
  }
164
172
  else {
165
- alert(`Successfully deleted ${deletedCount} unused image${deletedCount !== 1 ? 's' : ''}.`);
173
+ alert(`Successfully deleted ${deletedCount} unused assets.`);
166
174
  }
167
175
  }
168
176
  catch (error) {
169
177
  console.error('Cleanup failed:', error);
170
- alert('Failed to cleanup images');
178
+ alert('Failed to cleanup assets');
171
179
  }
172
180
  finally {
173
181
  setCleaning(false);
@@ -192,46 +200,14 @@ export function ImageManagerView({ siteId, locale }) {
192
200
  const isImageInUse = (filename) => {
193
201
  return isImageMapped(filename) || getImageUsage(filename).length > 0;
194
202
  };
195
- return (_jsxs("div", { className: "h-full w-full rounded-[2.5rem] bg-white dark:bg-neutral-900 p-8 overflow-y-auto", children: [_jsxs("div", { className: "flex flex-col md:flex-row md:items-center justify-between gap-6 mb-8", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-black text-neutral-950 dark:text-white uppercase tracking-tighter mb-2", children: "Images" }), _jsx("p", { className: "text-sm text-neutral-500 dark:text-neutral-400", children: "Upload and manage your media library" })] }), _jsxs("div", { className: "flex items-center gap-3", children: [stats && stats.availableImages > 0 && (_jsxs("button", { onClick: () => setShowCleanupModal(true), className: "inline-flex items-center gap-2 px-4 py-3 bg-red-500/10 text-red-600 dark:text-red-400 rounded-full text-[10px] font-bold uppercase tracking-widest hover:bg-red-500/20 transition-all", title: `Remove ${stats.availableImages} unused images`, children: [_jsx(Trash, { size: 16 }), "Clean Up"] })), _jsxs("label", { className: "inline-flex items-center gap-2 px-6 py-3 bg-primary text-white rounded-full text-[10px] font-black uppercase tracking-widest hover:bg-primary/90 transition-all shadow-lg shadow-primary/20 cursor-pointer", children: [uploading ? (_jsx(Loader2, { size: 16, className: "animate-spin" })) : (_jsx(Upload, { size: 16 })), uploading ? 'Uploading...' : 'Upload', _jsx("input", { type: "file", accept: "image/*", multiple: true, onChange: handleUpload, className: "hidden", disabled: uploading })] }), _jsx("button", { onClick: handleRefresh, className: "p-3 bg-neutral-100 dark:bg-neutral-800 text-neutral-600 dark:text-neutral-400 rounded-full hover:bg-neutral-200 dark:hover:bg-neutral-700 transition-colors", title: "Refresh", children: _jsx(RefreshCw, { size: 16 }) })] })] }), stats && (_jsxs("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4 mb-8", children: [_jsxs("div", { className: "bg-neutral-100 dark:bg-neutral-800 rounded-2xl p-5 border border-neutral-200 dark:border-neutral-700", children: [_jsxs("div", { className: "flex items-center gap-3 mb-2", children: [_jsx("div", { className: "p-2 bg-primary/10 rounded-xl", children: _jsx(ImageIcon, { className: "text-primary", size: 20 }) }), _jsx("span", { className: "text-xs font-bold text-neutral-500 dark:text-neutral-400 uppercase tracking-wider", children: "Total Images" })] }), _jsx("p", { className: "text-2xl font-black text-neutral-900 dark:text-white", children: stats.totalImages })] }), _jsxs("div", { className: "bg-neutral-100 dark:bg-neutral-800 rounded-2xl p-5 border border-neutral-200 dark:border-neutral-700", children: [_jsxs("div", { className: "flex items-center gap-3 mb-2", children: [_jsx("div", { className: "p-2 bg-amber-500/10 rounded-xl", children: _jsx(HardDrive, { className: "text-amber-500", size: 20 }) }), _jsx("span", { className: "text-xs font-bold text-neutral-500 dark:text-neutral-400 uppercase tracking-wider", children: "Storage Used" })] }), _jsx("p", { className: "text-2xl font-black text-neutral-900 dark:text-white", children: formatFileSize(stats.totalSize) })] }), _jsxs("div", { className: "bg-neutral-100 dark:bg-neutral-800 rounded-2xl p-5 border border-neutral-200 dark:border-neutral-700", children: [_jsxs("div", { className: "flex items-center gap-3 mb-2", children: [_jsx("div", { className: "p-2 bg-red-500/10 rounded-xl", children: _jsx(CheckCircle2, { className: "text-red-500", size: 20 }) }), _jsx("span", { className: "text-xs font-bold text-neutral-500 dark:text-neutral-400 uppercase tracking-wider", children: "In Use" })] }), _jsx("p", { className: "text-2xl font-black text-neutral-900 dark:text-white", children: stats.usedImages })] }), _jsxs("div", { className: "bg-neutral-100 dark:bg-neutral-800 rounded-2xl p-5 border border-neutral-200 dark:border-neutral-700", children: [_jsxs("div", { className: "flex items-center gap-3 mb-2", children: [_jsx("div", { className: "p-2 bg-green-500/10 rounded-xl", children: _jsx(Circle, { className: "text-green-500", size: 20 }) }), _jsx("span", { className: "text-xs font-bold text-neutral-500 dark:text-neutral-400 uppercase tracking-wider", children: "Available" })] }), _jsx("p", { className: "text-2xl font-black text-neutral-900 dark:text-white", children: stats.availableImages })] })] })), _jsxs("div", { className: "flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 mb-6", children: [_jsxs("div", { className: "relative flex-1 max-w-md", children: [_jsx(Search, { className: "absolute left-4 top-1/2 -translate-y-1/2 text-neutral-400", size: 18 }), _jsx("input", { type: "text", placeholder: "Search images...", value: search, onChange: (e) => setSearch(e.target.value), className: "w-full pl-12 pr-4 py-3 bg-neutral-100 dark:bg-neutral-800/50 border border-neutral-300 dark:border-neutral-700 rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-primary/50" })] }), _jsxs("div", { className: "flex items-center gap-2 bg-neutral-100 dark:bg-neutral-800/50 rounded-full p-1 border border-neutral-300 dark:border-neutral-700", children: [_jsx("button", { onClick: () => setViewMode('grid'), className: `p-2 rounded-full transition-all ${viewMode === 'grid'
196
- ? 'bg-white dark:bg-neutral-900 text-primary shadow-sm'
197
- : 'text-neutral-500 dark:text-neutral-400 hover:text-neutral-950 dark:hover:text-white'}`, title: "Grid View", children: _jsx(Grid3x3, { size: 18 }) }), _jsx("button", { onClick: () => setViewMode('list'), className: `p-2 rounded-full transition-all ${viewMode === 'list'
198
- ? 'bg-white dark:bg-neutral-900 text-primary shadow-sm'
199
- : 'text-neutral-500 dark:text-neutral-400 hover:text-neutral-950 dark:hover:text-white'}`, title: "List View", children: _jsx(List, { size: 18 }) })] })] }), loading ? (_jsx("div", { className: "flex items-center justify-center py-20", children: _jsx(Loader2, { size: 32, className: "animate-spin text-primary" }) })) : filteredImages.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-center py-20 text-center", children: [_jsx("div", { className: "w-24 h-24 bg-neutral-100 dark:bg-neutral-800 rounded-full flex items-center justify-center mb-6", children: _jsx(ImageIcon, { size: 40, className: "text-neutral-400" }) }), _jsx("h3", { className: "text-xl font-bold text-neutral-600 dark:text-neutral-400 mb-2", children: search ? 'No images found' : 'No images yet' }), _jsx("p", { className: "text-neutral-500 dark:text-neutral-500 mb-6", children: search ? 'Try a different search term' : 'Upload your first image to get started' }), !search && (_jsxs("label", { className: "inline-flex items-center gap-2 px-6 py-3 bg-primary text-white rounded-full text-[10px] font-black uppercase tracking-widest hover:bg-primary/90 transition-all shadow-lg shadow-primary/20 cursor-pointer", children: [_jsx(Upload, { size: 16 }), "Upload Image", _jsx("input", { type: "file", accept: "image/*", onChange: handleUpload, className: "hidden" })] }))] })) : viewMode === 'grid' ? (_jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4", children: filteredImages.map((image) => {
200
- const usage = getImageUsage(image.filename);
201
- const inUse = isImageInUse(image.filename);
202
- return (_jsxs("div", { className: `group relative bg-neutral-100 dark:bg-neutral-800 rounded-2xl overflow-hidden border transition-all hover:shadow-lg ${inUse
203
- ? 'border-amber-300 dark:border-amber-700'
204
- : 'border-neutral-200 dark:border-neutral-700 hover:border-primary/50'}`, children: [_jsxs("div", { className: "aspect-square relative bg-neutral-200 dark:bg-neutral-700", children: [_jsx(Image, { src: image.url, alt: image.filename, fill: true, className: "object-cover", unoptimized: true }), _jsxs("div", { className: "absolute inset-0 bg-black/0 group-hover:bg-black/40 transition-all flex items-center justify-center gap-2 opacity-0 group-hover:opacity-100", children: [_jsx("button", { onClick: () => window.open(image.url, '_blank'), className: "p-2 bg-white dark:bg-neutral-900 rounded-full text-primary hover:bg-primary/10 transition-colors", title: "View full size", children: _jsx(Eye, { size: 16 }) }), _jsx("button", { onClick: () => {
205
- setSelectedImage(image);
206
- setShowDeleteModal(true);
207
- }, className: `p-2 bg-white dark:bg-neutral-900 rounded-full transition-colors ${inUse
208
- ? 'text-neutral-400 cursor-not-allowed'
209
- : 'text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20'}`, title: inUse ? 'Cannot delete - image is in use' : 'Delete', disabled: inUse, children: _jsx(Trash2, { size: 16 }) })] }), inUse && (_jsxs("div", { className: "absolute top-2 right-2 px-2 py-1 bg-red-500 text-white text-[10px] font-bold uppercase rounded-full flex items-center gap-1 shadow-lg", children: [_jsx(CheckCircle, { size: 10 }), "In Use"] }))] }), _jsxs("div", { className: "p-3", children: [_jsx("p", { className: "text-xs font-medium text-neutral-700 dark:text-neutral-300 truncate", title: image.filename, children: image.filename }), _jsxs("div", { className: "flex items-center justify-between mt-1", children: [_jsx("p", { className: "text-[10px] text-neutral-500", children: formatFileSize(image.size) }), inUse ? (_jsx("p", { className: "text-[10px] text-red-500 dark:text-red-400 font-bold", children: usage.length > 0 ? `${usage.length} ref${usage.length > 1 ? 's' : ''}` : 'mapped' })) : (_jsx("p", { className: "text-[10px] text-green-500 dark:text-green-400 font-bold", children: "available" }))] })] })] }, image.id));
210
- }) })) : (_jsx("div", { className: "bg-neutral-100 dark:bg-neutral-800 rounded-2xl overflow-hidden border border-neutral-200 dark:border-neutral-700", children: _jsxs("table", { className: "w-full", children: [_jsx("thead", { className: "bg-neutral-200 dark:bg-neutral-700/50", children: _jsxs("tr", { children: [_jsx("th", { className: "text-left px-4 py-3 text-xs font-bold text-neutral-600 dark:text-neutral-400 uppercase", children: "Preview" }), _jsx("th", { className: "text-left px-4 py-3 text-xs font-bold text-neutral-600 dark:text-neutral-400 uppercase", children: "Filename" }), _jsx("th", { className: "text-left px-4 py-3 text-xs font-bold text-neutral-600 dark:text-neutral-400 uppercase", children: "Size" }), _jsx("th", { className: "text-left px-4 py-3 text-xs font-bold text-neutral-600 dark:text-neutral-400 uppercase", children: "Uploaded" }), _jsx("th", { className: "text-left px-4 py-3 text-xs font-bold text-neutral-600 dark:text-neutral-400 uppercase", children: "Status" }), _jsx("th", { className: "text-right px-4 py-3 text-xs font-bold text-neutral-600 dark:text-neutral-400 uppercase", children: "Actions" })] }) }), _jsx("tbody", { className: "divide-y divide-neutral-200 dark:divide-neutral-700", children: filteredImages.map((image) => {
211
- const usage = getImageUsage(image.filename);
212
- const inUse = isImageInUse(image.filename);
213
- return (_jsxs("tr", { className: `transition-colors ${inUse
214
- ? 'bg-amber-50/30 dark:bg-amber-900/5 hover:bg-amber-50/50 dark:hover:bg-amber-900/10'
215
- : 'hover:bg-neutral-200/50 dark:hover:bg-neutral-700/30'}`, children: [_jsx("td", { className: "px-4 py-3", children: _jsx("div", { className: `w-12 h-12 relative bg-neutral-200 dark:bg-neutral-700 rounded-lg overflow-hidden ring-2 ${inUse ? 'ring-red-400 dark:ring-red-600' : 'ring-transparent'}`, children: _jsx(Image, { src: image.url, alt: image.filename, fill: true, className: "object-cover", unoptimized: true }) }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("p", { className: "text-sm font-medium text-neutral-700 dark:text-neutral-300 truncate max-w-[200px]", title: image.filename, children: image.filename }) }), _jsx("td", { className: "px-4 py-3 text-sm text-neutral-600 dark:text-neutral-400", children: formatFileSize(image.size) }), _jsx("td", { className: "px-4 py-3 text-sm text-neutral-600 dark:text-neutral-400", children: formatDate(image.uploadedAt) }), _jsx("td", { className: "px-4 py-3", children: inUse ? (_jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-1 bg-red-500/10 text-red-600 dark:text-red-400 text-xs font-bold rounded-full", children: [_jsx(CheckCircle, { size: 12 }), "In Use", usage.length > 0 ? ` (${usage.length})` : ''] })) : (_jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-1 bg-green-500/10 text-green-600 dark:text-green-400 text-xs font-medium rounded-full", children: [_jsx(Circle, { size: 12 }), "Available"] })) }), _jsx("td", { className: "px-4 py-3 text-right", children: _jsxs("div", { className: "flex items-center justify-end gap-2", children: [_jsx("button", { onClick: () => window.open(image.url, '_blank'), className: "p-2 text-neutral-500 hover:text-primary hover:bg-neutral-200 dark:hover:bg-neutral-700 rounded-lg transition-colors", title: "View", children: _jsx(Eye, { size: 16 }) }), _jsx("button", { onClick: () => {
216
- setSelectedImage(image);
217
- setShowDeleteModal(true);
218
- }, className: `p-2 rounded-lg transition-colors ${inUse
219
- ? 'text-neutral-400 cursor-not-allowed'
220
- : 'text-neutral-500 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20'}`, title: inUse ? 'Cannot delete - image is in use' : 'Delete', disabled: inUse, children: _jsx(Trash2, { size: 16 }) })] }) })] }, image.id));
221
- }) })] }) })), showDeleteModal && selectedImage && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm", children: _jsxs("div", { className: "bg-white dark:bg-neutral-900 rounded-2xl p-6 max-w-md w-full mx-4 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsx("h3", { className: "text-xl font-bold text-neutral-900 dark:text-white", children: "Delete Image" }), _jsx("button", { onClick: () => {
222
- setShowDeleteModal(false);
223
- setSelectedImage(null);
224
- }, className: "p-2 text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-300 rounded-lg", children: _jsx(X, { size: 20 }) })] }), (() => {
225
- const usage = getImageUsage(selectedImage.filename);
226
- const inUse = isImageInUse(selectedImage.filename);
227
- const isMapped = isImageMapped(selectedImage.filename);
228
- return (_jsxs(_Fragment, { children: [inUse ? (_jsxs("div", { className: "mb-6", children: [_jsxs("div", { className: "flex items-center gap-2 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl mb-4", children: [_jsx(AlertTriangle, { className: "text-red-500 shrink-0", size: 24 }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-red-800 dark:text-red-200", children: isMapped && usage.length === 0
229
- ? 'This image is mapped to content'
230
- : 'This image is currently in use' }), _jsx("p", { className: "text-xs text-red-600 dark:text-red-400 mt-1", children: isMapped && usage.length === 0
231
- ? 'The image is linked via semantic ID mapping.'
232
- : 'Please remove the image from all content before deleting.' })] })] }), (usage.length > 0) && (_jsxs("div", { className: "space-y-2 max-h-[200px] overflow-y-auto", children: [_jsx("p", { className: "text-sm font-medium text-neutral-700 dark:text-neutral-300", children: "Used in:" }), usage.map((item, idx) => (_jsxs("div", { className: "flex items-center justify-between p-3 bg-neutral-100 dark:bg-neutral-800 rounded-lg", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-neutral-700 dark:text-neutral-300", children: item.title }), _jsxs("p", { className: "text-xs text-neutral-500", children: [item.plugin, " - ", item.type] })] }), item.url && (_jsx("a", { href: item.url, target: "_blank", rel: "noopener noreferrer", className: "p-2 text-primary hover:bg-primary/10 rounded-lg transition-colors", children: _jsx(ExternalLink, { size: 16 }) }))] }, idx)))] })), isMapped && usage.length === 0 && (_jsx("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 mt-2", children: "This image is being used through semantic ID mappings. Delete the mapping first." }))] })) : (_jsxs("div", { className: "mb-6", children: [_jsxs("div", { className: "flex items-center gap-4 mb-4", children: [_jsx("div", { className: "w-20 h-20 relative bg-neutral-200 dark:bg-neutral-700 rounded-lg overflow-hidden shrink-0", children: _jsx(Image, { src: selectedImage.url, alt: selectedImage.filename, fill: true, className: "object-cover", unoptimized: true }) }), _jsxs("div", { children: [_jsx("p", { className: "font-medium text-neutral-900 dark:text-white", children: selectedImage.filename }), _jsx("p", { className: "text-sm text-neutral-500", children: formatFileSize(selectedImage.size) }), _jsx("p", { className: "text-sm text-neutral-500", children: formatDate(selectedImage.uploadedAt) })] })] }), _jsx("p", { className: "text-sm text-neutral-600 dark:text-neutral-400", children: "Are you sure you want to delete this image? This action cannot be undone." })] })), _jsxs("div", { className: "flex justify-end gap-3", children: [_jsx("button", { onClick: () => {
233
- setShowDeleteModal(false);
234
- setSelectedImage(null);
235
- }, className: "px-4 py-2 text-neutral-600 dark:text-neutral-400 hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-lg transition-colors", children: "Cancel" }), !inUse && (_jsxs("button", { onClick: handleDelete, disabled: deleting, className: "px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors disabled:opacity-50 flex items-center gap-2", children: [deleting && _jsx(Loader2, { size: 16, className: "animate-spin" }), "Delete"] }))] })] }));
236
- })()] }) })), showCleanupModal && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm", children: _jsxs("div", { className: "bg-white dark:bg-neutral-900 rounded-2xl p-6 max-w-md w-full mx-4 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsx("h3", { className: "text-xl font-bold text-neutral-900 dark:text-white", children: "Clean Up Unused Images" }), _jsx("button", { onClick: () => setShowCleanupModal(false), className: "p-2 text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-300 rounded-lg", children: _jsx(X, { size: 20 }) })] }), _jsxs("div", { className: "mb-6", children: [_jsxs("div", { className: "flex items-center gap-2 p-4 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-xl mb-4", children: [_jsx(AlertTriangle, { className: "text-amber-500 shrink-0", size: 24 }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-amber-800 dark:text-amber-200", children: "Warning: This action cannot be undone" }), _jsxs("p", { className: "text-xs text-amber-600 dark:text-amber-400 mt-1", children: [getUnusedImagesCount(), " unused image", getUnusedImagesCount() !== 1 ? 's' : '', " will be permanently deleted."] })] })] }), _jsx("p", { className: "text-sm text-neutral-600 dark:text-neutral-400 mb-4", children: "This will delete all images that are not currently in use by any content (blogs, newsletters, or user avatars)." }), _jsxs("div", { className: "p-3 bg-neutral-100 dark:bg-neutral-800 rounded-lg", children: [_jsxs("div", { className: "flex items-center justify-between text-sm", children: [_jsx("span", { className: "text-neutral-600 dark:text-neutral-400", children: "Total images:" }), _jsx("span", { className: "font-bold text-neutral-900 dark:text-white", children: stats?.totalImages || 0 })] }), _jsxs("div", { className: "flex items-center justify-between text-sm mt-2", children: [_jsx("span", { className: "text-neutral-600 dark:text-neutral-400", children: "In use:" }), _jsx("span", { className: "font-bold text-red-500", children: stats?.usedImages || 0 })] }), _jsxs("div", { className: "flex items-center justify-between text-sm mt-2", children: [_jsx("span", { className: "text-neutral-600 dark:text-neutral-400", children: "Will be deleted:" }), _jsx("span", { className: "font-bold text-red-500", children: getUnusedImagesCount() })] })] })] }), _jsxs("div", { className: "flex justify-end gap-3", children: [_jsx("button", { onClick: () => setShowCleanupModal(false), className: "px-4 py-2 text-neutral-600 dark:text-neutral-400 hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded-lg transition-colors", children: "Cancel" }), _jsxs("button", { onClick: handleCleanup, disabled: cleaning, className: "px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors disabled:opacity-50 flex items-center gap-2", children: [cleaning && _jsx(Loader2, { size: 16, className: "animate-spin" }), cleaning ? 'Deleting...' : `Delete ${getUnusedImagesCount()} Images`] })] })] }) }))] }));
203
+ return (_jsxs("div", { className: "w-full flex flex-col space-y-8 px-6 lg:px-10 py-6 lg:py-10 pb-10 bg-transparent", children: [_jsx(ImageManagerHeader, { uploading: uploading, unusedCount: getUnusedImagesCount(), onUpload: handleUpload, onRefresh: handleRefresh, onShowCleanup: () => setShowCleanupModal(true) }), stats && _jsx(ImageManagerStats, { stats: stats, formatFileSize: formatFileSize }), _jsx(ImageManagerToolbar, { search: search, setSearch: setSearch, viewMode: viewMode, setViewMode: setViewMode }), _jsx("div", { className: "flex-1 px-4 min-h-[400px]", children: loading ? (_jsx("div", { className: "h-full flex items-center justify-center py-20", children: _jsxs("div", { className: "flex flex-col items-center gap-4", children: [_jsx(Loader2, { size: 40, className: "animate-spin text-primary opacity-40" }), _jsx("p", { className: "text-[10px] font-bold text-primary uppercase tracking-widest animate-pulse", children: "Syncing Library" })] }) })) : filteredImages.length === 0 ? (_jsxs("div", { className: "h-full flex flex-col items-center justify-center py-32 text-center", children: [_jsxs("div", { className: "size-24 bg-dashboard-card/40 rounded-3xl border border-dashed border-dashboard-border/50 flex items-center justify-center mb-6 relative", children: [_jsx(ImageIcon, { size: 40, className: "text-dashboard-text-secondary opacity-20" }), _jsx("div", { className: "absolute inset-0 bg-primary/5 rounded-3xl animate-pulse" })] }), _jsx("h3", { className: "text-xl font-bold text-dashboard-text tracking-tight opacity-40 mb-1.5", children: search ? 'Reference Not Found' : 'Repository Empty' }), _jsx("p", { className: "text-xs text-dashboard-text-secondary font-medium opacity-60", children: search ? 'Try refining your search parameters' : 'Upload your first image to begin building your library' })] })) : viewMode === 'grid' ? (_jsx(ImageGrid, { images: filteredImages, isImageInUse: isImageInUse, getImageUsage: getImageUsage, formatFileSize: formatFileSize, onViewFull: (url) => window.open(url, '_blank'), onDelete: (img) => {
204
+ setSelectedImage(img);
205
+ setShowDeleteModal(true);
206
+ } })) : (_jsx(ImageTable, { images: filteredImages, isImageInUse: isImageInUse, getImageUsage: getImageUsage, formatFileSize: formatFileSize, formatDate: formatDate, onViewFull: (url) => window.open(url, '_blank'), onDelete: (img) => {
207
+ setSelectedImage(img);
208
+ setShowDeleteModal(true);
209
+ } })) }), _jsx(DeleteImageModal, { isOpen: showDeleteModal, image: selectedImage, inUse: selectedImage ? isImageInUse(selectedImage.filename) : false, usage: selectedImage ? getImageUsage(selectedImage.filename) : [], isDeleting: deleting, formatFileSize: formatFileSize, formatDate: formatDate, onClose: () => {
210
+ setShowDeleteModal(false);
211
+ setSelectedImage(null);
212
+ }, onConfirm: handleDelete }), _jsx(CleanupLibraryModal, { isOpen: showCleanupModal, stats: stats, unusedCount: getUnusedImagesCount(), isCleaning: cleaning, onClose: () => setShowCleanupModal(false), onConfirm: handleCleanup })] }));
237
213
  }
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@jhits/plugin-images",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "description": "Image management and storage plugin for the JHITS ecosystem",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
- "main": "./src/index.ts",
9
- "types": "./src/index.ts",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
10
  "exports": {
11
11
  ".": {
12
- "types": "./src/index.tsx",
13
- "import": "./src/index.tsx",
14
- "default": "./src/index.tsx"
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
15
  },
16
16
  "./src": {
17
17
  "types": "./src/index.tsx",
@@ -19,12 +19,13 @@
19
19
  "default": "./src/index.tsx"
20
20
  },
21
21
  "./server": {
22
- "types": "./src/index.server.ts",
23
- "import": "./src/index.server.ts",
24
- "default": "./src/index.server.ts"
22
+ "types": "./dist/index.server.d.ts",
23
+ "import": "./dist/index.server.js",
24
+ "default": "./dist/index.server.js"
25
25
  }
26
26
  },
27
27
  "dependencies": {
28
+ "framer-motion": "^12.36.0",
28
29
  "lucide-react": "^0.564.0",
29
30
  "mongodb": "^7.1.0",
30
31
  "@jhits/plugin-core": "0.0.10"