@jhits/plugin-images 0.0.5 → 0.0.7

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 (175) hide show
  1. package/dist/api/fallback/route.d.ts +7 -0
  2. package/dist/api/fallback/route.d.ts.map +1 -0
  3. package/dist/api/fallback/route.js +65 -0
  4. package/dist/api/index.d.ts +9 -0
  5. package/dist/api/index.d.ts.map +1 -0
  6. package/dist/api/index.js +8 -0
  7. package/dist/api/list/index.d.ts +21 -0
  8. package/dist/api/list/index.d.ts.map +1 -0
  9. package/dist/api/list/index.js +80 -0
  10. package/dist/api/resolve/route.d.ts +39 -0
  11. package/dist/api/resolve/route.d.ts.map +1 -0
  12. package/dist/api/resolve/route.js +213 -0
  13. package/dist/api/router.d.ts +14 -0
  14. package/dist/api/router.d.ts.map +1 -0
  15. package/dist/api/router.js +67 -0
  16. package/dist/api/upload/index.d.ts +20 -0
  17. package/dist/api/upload/index.d.ts.map +1 -0
  18. package/dist/api/upload/index.js +65 -0
  19. package/dist/api/uploads/[filename]/route.d.ts +21 -0
  20. package/dist/api/uploads/[filename]/route.d.ts.map +1 -0
  21. package/dist/api/uploads/[filename]/route.js +80 -0
  22. package/dist/api-server.d.ts +9 -0
  23. package/dist/api-server.d.ts.map +1 -0
  24. package/dist/api-server.js +9 -0
  25. package/dist/components/BackgroundImage.d.ts +11 -0
  26. package/dist/components/BackgroundImage.d.ts.map +1 -0
  27. package/dist/components/BackgroundImage.js +33 -0
  28. package/dist/components/GlobalImageEditor/config.d.ts +9 -0
  29. package/dist/components/GlobalImageEditor/config.d.ts.map +1 -0
  30. package/dist/components/GlobalImageEditor/config.js +17 -0
  31. package/dist/components/GlobalImageEditor/eventHandlers.d.ts +20 -0
  32. package/dist/components/GlobalImageEditor/eventHandlers.d.ts.map +1 -0
  33. package/dist/components/GlobalImageEditor/eventHandlers.js +210 -0
  34. package/dist/components/GlobalImageEditor/imageDetection.d.ts +16 -0
  35. package/dist/components/GlobalImageEditor/imageDetection.d.ts.map +1 -0
  36. package/dist/components/GlobalImageEditor/imageDetection.js +135 -0
  37. package/dist/components/GlobalImageEditor/imageSetup.d.ts +9 -0
  38. package/dist/components/GlobalImageEditor/imageSetup.d.ts.map +1 -0
  39. package/dist/components/GlobalImageEditor/imageSetup.js +260 -0
  40. package/dist/components/GlobalImageEditor/saveLogic.d.ts +26 -0
  41. package/dist/components/GlobalImageEditor/saveLogic.d.ts.map +1 -0
  42. package/dist/components/GlobalImageEditor/saveLogic.js +98 -0
  43. package/dist/components/GlobalImageEditor/stylingDetection.d.ts +9 -0
  44. package/dist/components/GlobalImageEditor/stylingDetection.d.ts.map +1 -0
  45. package/dist/components/GlobalImageEditor/stylingDetection.js +110 -0
  46. package/dist/components/GlobalImageEditor/transformParsing.d.ts +16 -0
  47. package/dist/components/GlobalImageEditor/transformParsing.d.ts.map +1 -0
  48. package/dist/components/GlobalImageEditor/transformParsing.js +68 -0
  49. package/dist/components/GlobalImageEditor/types.d.ts +36 -0
  50. package/dist/components/GlobalImageEditor/types.d.ts.map +1 -0
  51. package/dist/components/GlobalImageEditor/types.js +4 -0
  52. package/dist/components/GlobalImageEditor.d.ts +8 -0
  53. package/dist/components/GlobalImageEditor.d.ts.map +1 -0
  54. package/dist/components/GlobalImageEditor.js +232 -0
  55. package/dist/components/Image.d.ts +22 -0
  56. package/dist/components/Image.d.ts.map +1 -0
  57. package/dist/components/Image.js +227 -0
  58. package/dist/components/ImageBrowserModal.d.ts +13 -0
  59. package/dist/components/ImageBrowserModal.d.ts.map +1 -0
  60. package/dist/components/ImageBrowserModal.js +507 -0
  61. package/dist/components/ImageEditor.d.ts +27 -0
  62. package/dist/components/ImageEditor.d.ts.map +1 -0
  63. package/dist/components/ImageEditor.js +172 -0
  64. package/dist/components/ImageEffectsPanel.d.ts +10 -0
  65. package/dist/components/ImageEffectsPanel.d.ts.map +1 -0
  66. package/dist/components/ImageEffectsPanel.js +11 -0
  67. package/dist/components/ImagePicker.d.ts +3 -0
  68. package/dist/components/ImagePicker.d.ts.map +1 -0
  69. package/dist/components/ImagePicker.js +142 -0
  70. package/dist/components/ImagesPluginInit.d.ts +24 -0
  71. package/dist/components/ImagesPluginInit.d.ts.map +1 -0
  72. package/dist/components/ImagesPluginInit.js +28 -0
  73. package/dist/components/index.d.ts +9 -0
  74. package/dist/components/index.d.ts.map +1 -0
  75. package/dist/components/index.js +7 -0
  76. package/dist/config.d.ts +14 -0
  77. package/dist/config.d.ts.map +1 -0
  78. package/dist/config.js +172 -0
  79. package/dist/hooks/useImagePicker.d.ts +20 -0
  80. package/dist/hooks/useImagePicker.d.ts.map +1 -0
  81. package/dist/hooks/useImagePicker.js +320 -0
  82. package/dist/index.d.ts +23 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +28 -0
  85. package/dist/index.server.d.ts +11 -0
  86. package/dist/index.server.d.ts.map +1 -0
  87. package/dist/index.server.js +10 -0
  88. package/dist/init.d.ts +33 -0
  89. package/dist/init.d.ts.map +1 -0
  90. package/dist/init.js +43 -0
  91. package/dist/types/index.d.ts +80 -0
  92. package/dist/types/index.d.ts.map +1 -0
  93. package/dist/types/index.js +4 -0
  94. package/dist/utils/fallback.d.ts +27 -0
  95. package/dist/utils/fallback.d.ts.map +1 -0
  96. package/dist/utils/fallback.js +63 -0
  97. package/dist/utils/transforms.d.ts +26 -0
  98. package/dist/utils/transforms.d.ts.map +1 -0
  99. package/dist/utils/transforms.js +38 -0
  100. package/dist/views/ImageManager.d.ts +10 -0
  101. package/dist/views/ImageManager.d.ts.map +1 -0
  102. package/dist/views/ImageManager.js +9 -0
  103. package/package.json +26 -22
  104. package/src/assets/noimagefound.jpg +0 -0
  105. package/src/components/BackgroundImage.d.ts +11 -0
  106. package/src/components/BackgroundImage.d.ts.map +1 -0
  107. package/src/components/BackgroundImage.js +35 -0
  108. package/src/components/GlobalImageEditor/config.d.ts +9 -0
  109. package/src/components/GlobalImageEditor/config.d.ts.map +1 -0
  110. package/src/components/GlobalImageEditor/config.js +18 -0
  111. package/src/components/GlobalImageEditor/eventHandlers.d.ts +20 -0
  112. package/src/components/GlobalImageEditor/eventHandlers.d.ts.map +1 -0
  113. package/src/components/GlobalImageEditor/eventHandlers.js +206 -0
  114. package/src/components/GlobalImageEditor/imageDetection.d.ts +16 -0
  115. package/src/components/GlobalImageEditor/imageDetection.d.ts.map +1 -0
  116. package/src/components/GlobalImageEditor/imageDetection.js +130 -0
  117. package/src/components/GlobalImageEditor/imageSetup.d.ts +9 -0
  118. package/src/components/GlobalImageEditor/imageSetup.d.ts.map +1 -0
  119. package/src/components/GlobalImageEditor/imageSetup.js +261 -0
  120. package/src/components/GlobalImageEditor/saveLogic.d.ts +26 -0
  121. package/src/components/GlobalImageEditor/saveLogic.d.ts.map +1 -0
  122. package/src/components/GlobalImageEditor/saveLogic.js +99 -0
  123. package/src/components/GlobalImageEditor/stylingDetection.d.ts +9 -0
  124. package/src/components/GlobalImageEditor/stylingDetection.d.ts.map +1 -0
  125. package/src/components/GlobalImageEditor/stylingDetection.js +110 -0
  126. package/src/components/GlobalImageEditor/transformParsing.d.ts +16 -0
  127. package/src/components/GlobalImageEditor/transformParsing.d.ts.map +1 -0
  128. package/src/components/GlobalImageEditor/transformParsing.js +68 -0
  129. package/src/components/GlobalImageEditor/types.d.ts +36 -0
  130. package/src/components/GlobalImageEditor/types.d.ts.map +1 -0
  131. package/src/components/GlobalImageEditor/types.js +4 -0
  132. package/src/components/GlobalImageEditor.d.ts +8 -0
  133. package/src/components/GlobalImageEditor.d.ts.map +1 -0
  134. package/src/components/GlobalImageEditor.js +227 -0
  135. package/src/components/GlobalImageEditor.tsx +2 -2
  136. package/src/components/Image.d.ts +22 -0
  137. package/src/components/Image.d.ts.map +1 -0
  138. package/src/components/Image.js +229 -0
  139. package/src/components/ImageBrowserModal.d.ts +13 -0
  140. package/src/components/ImageBrowserModal.d.ts.map +1 -0
  141. package/src/components/ImageBrowserModal.js +504 -0
  142. package/src/components/ImageBrowserModal.tsx +18 -5
  143. package/src/components/ImageEditor.d.ts +27 -0
  144. package/src/components/ImageEditor.d.ts.map +1 -0
  145. package/src/components/ImageEditor.js +173 -0
  146. package/src/components/ImagePicker.d.ts +3 -0
  147. package/src/components/ImagePicker.d.ts.map +1 -0
  148. package/src/components/ImagePicker.js +143 -0
  149. package/src/components/ImagePicker.tsx +53 -15
  150. package/src/components/ImagesPluginInit.d.ts +24 -0
  151. package/src/components/ImagesPluginInit.d.ts.map +1 -0
  152. package/src/components/ImagesPluginInit.js +28 -0
  153. package/src/hooks/useImagePicker.d.ts +20 -0
  154. package/src/hooks/useImagePicker.d.ts.map +1 -0
  155. package/src/hooks/useImagePicker.js +322 -0
  156. package/src/hooks/useImagePicker.ts +28 -6
  157. package/src/index.d.ts +23 -0
  158. package/src/index.d.ts.map +1 -0
  159. package/src/index.js +28 -0
  160. package/src/init.d.ts +33 -0
  161. package/src/init.d.ts.map +1 -0
  162. package/src/init.js +43 -0
  163. package/src/types/index.d.ts +80 -0
  164. package/src/types/index.d.ts.map +1 -0
  165. package/src/types/index.js +4 -0
  166. package/src/utils/fallback.d.ts +27 -0
  167. package/src/utils/fallback.d.ts.map +1 -0
  168. package/src/utils/fallback.js +63 -0
  169. package/src/utils/transforms.d.ts +26 -0
  170. package/src/utils/transforms.d.ts.map +1 -0
  171. package/src/utils/transforms.js +38 -0
  172. package/src/views/ImageManager.d.ts +10 -0
  173. package/src/views/ImageManager.d.ts.map +1 -0
  174. package/src/views/ImageManager.js +9 -0
  175. package/src/components/GlobalImageEditor/GlobalImageEditor.tsx +0 -374
package/dist/config.js ADDED
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Plugin Images Configuration
3
+ * Automatically creates required API routes in client apps
4
+ */
5
+ import { writeFileSync, mkdirSync, existsSync } from 'fs';
6
+ import { join } from 'path';
7
+ /**
8
+ * Automatically creates plugin-images API catch-all route
9
+ * This route forwards requests to the plugin's API router
10
+ */
11
+ /**
12
+ * @deprecated Routes are now handled by the unified /api/[pluginId]/[...path]/route.ts
13
+ * This function is kept for backwards compatibility but does nothing
14
+ */
15
+ export function ensureImagesRoutes() {
16
+ // Routes are now handled by the unified /api/[pluginId]/[...path]/route.ts
17
+ // No need to generate individual routes anymore
18
+ return;
19
+ try {
20
+ // Find the host app directory (where next.config.ts is)
21
+ let appDir = process.cwd();
22
+ const possiblePaths = [
23
+ appDir,
24
+ join(appDir, '..'),
25
+ join(appDir, '..', '..'),
26
+ ];
27
+ for (const basePath of possiblePaths) {
28
+ const configPath = join(basePath, 'next.config.ts');
29
+ if (existsSync(configPath)) {
30
+ appDir = basePath;
31
+ break;
32
+ }
33
+ }
34
+ const apiDir = join(appDir, 'src', 'app', 'api');
35
+ const pluginImagesApiDir = join(apiDir, 'plugin-images', '[...path]');
36
+ const pluginImagesApiPath = join(pluginImagesApiDir, 'route.ts');
37
+ // Check if route already exists
38
+ if (existsSync(pluginImagesApiPath)) {
39
+ const fs = require('fs');
40
+ const existingContent = fs.readFileSync(pluginImagesApiPath, 'utf8');
41
+ if (existingContent.includes('@jhits/plugin-images') || existingContent.includes('plugin-images')) {
42
+ // Already set up, skip
43
+ return;
44
+ }
45
+ }
46
+ // Create plugin-images API catch-all route
47
+ mkdirSync(pluginImagesApiDir, { recursive: true });
48
+ writeFileSync(pluginImagesApiPath, `// Auto-generated by @jhits/plugin-images - Images Plugin API
49
+ // This route is automatically created for the images plugin
50
+ import { NextRequest } from 'next/server';
51
+ import { handleImagesApi } from '@jhits/plugin-images/api';
52
+
53
+ export async function GET(
54
+ req: NextRequest,
55
+ { params }: { params: Promise<{ path: string[] }> }
56
+ ) {
57
+ const { path } = await params;
58
+ return handleImagesApi(req, path);
59
+ }
60
+
61
+ export async function POST(
62
+ req: NextRequest,
63
+ { params }: { params: Promise<{ path: string[] }> }
64
+ ) {
65
+ const { path } = await params;
66
+ return handleImagesApi(req, path);
67
+ }
68
+
69
+ export async function PUT(
70
+ req: NextRequest,
71
+ { params }: { params: Promise<{ path: string[] }> }
72
+ ) {
73
+ const { path } = await params;
74
+ return handleImagesApi(req, path);
75
+ }
76
+
77
+ export async function DELETE(
78
+ req: NextRequest,
79
+ { params }: { params: Promise<{ path: string[] }> }
80
+ ) {
81
+ const { path } = await params;
82
+ return handleImagesApi(req, path);
83
+ }
84
+
85
+ export async function PATCH(
86
+ req: NextRequest,
87
+ { params }: { params: Promise<{ path: string[] }> }
88
+ ) {
89
+ const { path } = await params;
90
+ return handleImagesApi(req, path);
91
+ }
92
+ `);
93
+ // Also create uploads route for serving files
94
+ const uploadsApiDir = join(apiDir, 'uploads', '[filename]');
95
+ const uploadsApiPath = join(uploadsApiDir, 'route.ts');
96
+ if (!existsSync(uploadsApiPath)) {
97
+ mkdirSync(uploadsApiDir, { recursive: true });
98
+ writeFileSync(uploadsApiPath, `// Auto-generated by @jhits/plugin-images - Image Uploads API
99
+ // This route is automatically created for the images plugin
100
+ import { NextRequest, NextResponse } from 'next/server';
101
+ import { readFile } from 'fs/promises';
102
+ import path from 'path';
103
+ import { existsSync } from 'fs';
104
+
105
+ export async function GET(
106
+ request: NextRequest,
107
+ { params }: { params: Promise<{ filename: string }> }
108
+ ) {
109
+ const { filename } = await params;
110
+
111
+ // Security: Prevent directory traversal (only allow the filename)
112
+ const sanitizedFilename = path.basename(filename);
113
+ const filePath = path.join(process.cwd(), 'data', 'uploads', sanitizedFilename);
114
+
115
+ try {
116
+ const fileBuffer = await readFile(filePath);
117
+
118
+ // Determine content type based on extension
119
+ const ext = path.extname(sanitizedFilename).toLowerCase();
120
+ let contentType = 'application/octet-stream';
121
+ if (ext === '.png') contentType = 'image/png';
122
+ else if (ext === '.jpg' || ext === '.jpeg') contentType = 'image/jpeg';
123
+ else if (ext === '.gif') contentType = 'image/gif';
124
+ else if (ext === '.webp') contentType = 'image/webp';
125
+ else if (ext === '.svg') contentType = 'image/svg+xml';
126
+
127
+ return new NextResponse(fileBuffer, {
128
+ headers: {
129
+ 'Content-Type': contentType,
130
+ 'Cache-Control': 'public, max-age=31536000, immutable',
131
+ },
132
+ });
133
+ } catch (e) {
134
+ console.error('File serving error:', e);
135
+ return new NextResponse('File not found', { status: 404 });
136
+ }
137
+ }
138
+
139
+ export async function DELETE(
140
+ request: NextRequest,
141
+ { params }: { params: Promise<{ filename: string }> }
142
+ ) {
143
+ const { filename } = await params;
144
+
145
+ if (!filename) {
146
+ return new NextResponse('Missing filename', { status: 400 });
147
+ }
148
+
149
+ const sanitizedFilename = path.basename(filename);
150
+ const filePath = path.join(process.cwd(), 'data', 'uploads', sanitizedFilename);
151
+
152
+ if (!existsSync(filePath)) {
153
+ return new NextResponse('File not found', { status: 404 });
154
+ }
155
+
156
+ try {
157
+ const { unlink } = require('fs/promises');
158
+ await unlink(filePath);
159
+ return NextResponse.json({ success: true, message: \`"\${sanitizedFilename}" deleted successfully.\` });
160
+ } catch (error) {
161
+ console.error('Failed to delete file:', error);
162
+ return new NextResponse('Failed to delete file', { status: 500 });
163
+ }
164
+ }
165
+ `);
166
+ }
167
+ }
168
+ catch (error) {
169
+ // Ignore errors - route might already exist or app structure is different
170
+ console.warn('[plugin-images] Could not ensure images routes:', error);
171
+ }
172
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Hook for Image Picker Logic
3
+ */
4
+ import type { ImageMetadata } from '../types';
5
+ export interface UseImagePickerOptions {
6
+ value?: string;
7
+ images: ImageMetadata[];
8
+ }
9
+ export declare function useImagePicker({ value, images }: UseImagePickerOptions): {
10
+ selectedImage: ImageMetadata | null;
11
+ setSelectedImage: import("react").Dispatch<import("react").SetStateAction<ImageMetadata | null>>;
12
+ uploading: boolean;
13
+ fileInputRef: import("react").RefObject<HTMLInputElement | null>;
14
+ handleFileSelect: (e?: React.ChangeEvent<HTMLInputElement> | {
15
+ target: {
16
+ files: File[] | null;
17
+ };
18
+ }) => Promise<any>;
19
+ };
20
+ //# sourceMappingURL=useImagePicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useImagePicker.d.ts","sourceRoot":"","sources":["../../src/hooks/useImagePicker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,WAAW,qBAAqB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,qBAAqB;;;;;2BA6R/B,KAAK,CAAC,WAAW,CAAC,gBAAgB,CAAC,GAAG;QAAE,MAAM,EAAE;YAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAA;SAAE,CAAA;KAAE;EA8CjH"}
@@ -0,0 +1,320 @@
1
+ /**
2
+ * Hook for Image Picker Logic
3
+ */
4
+ import { useState, useEffect, useRef } from 'react';
5
+ export function useImagePicker({ value, images }) {
6
+ const [selectedImage, setSelectedImage] = useState(null);
7
+ const [uploading, setUploading] = useState(false);
8
+ const fileInputRef = useRef(null);
9
+ const lastResolvedValueRef = useRef(undefined);
10
+ const isResolvingRef = useRef(false);
11
+ const lastImagesRef = useRef('');
12
+ // Find selected image from value (can be ID or URL)
13
+ useEffect(() => {
14
+ // Create a stable reference for images array to detect actual changes
15
+ const imagesKey = JSON.stringify(images.map(img => ({ id: img.id, url: img.url })));
16
+ // Prevent infinite loops by checking if we're already resolving or if value and images haven't changed
17
+ if (isResolvingRef.current || (lastResolvedValueRef.current === value && lastImagesRef.current === imagesKey)) {
18
+ return;
19
+ }
20
+ lastImagesRef.current = imagesKey;
21
+ if (!value) {
22
+ if (selectedImage !== null) {
23
+ setSelectedImage(null);
24
+ }
25
+ lastResolvedValueRef.current = value;
26
+ return;
27
+ }
28
+ isResolvingRef.current = true;
29
+ const resolveImage = async () => {
30
+ // Normalize the value - extract filename if it's a URL
31
+ const isFullUrl = value.startsWith('http://') || value.startsWith('https://');
32
+ const isRelativeUrl = value.startsWith('/');
33
+ const isUrl = isFullUrl || isRelativeUrl;
34
+ // Extract filename from URL if it's a URL
35
+ let filenameFromUrl = null;
36
+ let imageIdToResolve = value;
37
+ if (isUrl) {
38
+ const urlParts = value.split('/');
39
+ filenameFromUrl = urlParts[urlParts.length - 1]?.split('?')[0] || null;
40
+ // If it's a full URL, try to resolve using the filename instead
41
+ if (isFullUrl && filenameFromUrl) {
42
+ imageIdToResolve = filenameFromUrl;
43
+ }
44
+ }
45
+ // First, try to find by ID (preferred method)
46
+ let found = images.find(img => img.id === value || img.id === imageIdToResolve);
47
+ // If not found by ID, try to find by URL
48
+ if (!found) {
49
+ found = images.find(img => img.url === value);
50
+ }
51
+ // If still not found, try to match by filename extracted from URL
52
+ if (!found && filenameFromUrl) {
53
+ found = images.find(img => img.id === filenameFromUrl || img.filename === filenameFromUrl);
54
+ }
55
+ if (found) {
56
+ // Only update if the image is actually different
57
+ setSelectedImage(prev => {
58
+ if (prev?.id === found.id && prev?.url === found.url) {
59
+ return prev;
60
+ }
61
+ return found;
62
+ });
63
+ }
64
+ else {
65
+ // If value is already a full URL, use it directly
66
+ if (isFullUrl) {
67
+ const newImage = {
68
+ id: value,
69
+ filename: filenameFromUrl || value,
70
+ url: value, // Use the full URL as-is
71
+ size: 0,
72
+ mimeType: 'image/jpeg',
73
+ uploadedAt: new Date().toISOString(),
74
+ };
75
+ setSelectedImage(prev => {
76
+ if (prev?.id === newImage.id && prev?.url === newImage.url) {
77
+ return prev;
78
+ }
79
+ return newImage;
80
+ });
81
+ lastResolvedValueRef.current = value;
82
+ isResolvingRef.current = false;
83
+ return;
84
+ }
85
+ // If value is a relative URL, use it directly
86
+ if (isRelativeUrl) {
87
+ const newImage = {
88
+ id: value,
89
+ filename: filenameFromUrl || value,
90
+ url: value, // Use the relative URL as-is
91
+ size: 0,
92
+ mimeType: 'image/jpeg',
93
+ uploadedAt: new Date().toISOString(),
94
+ };
95
+ setSelectedImage(prev => {
96
+ if (prev?.id === newImage.id && prev?.url === newImage.url) {
97
+ return prev;
98
+ }
99
+ return newImage;
100
+ });
101
+ lastResolvedValueRef.current = value;
102
+ isResolvingRef.current = false;
103
+ return;
104
+ }
105
+ // Check if value looks like a filename (has extension or starts with digits)
106
+ const isLikelyPath = /\.(jpg|jpeg|png|webp|gif|svg)$/i.test(value) || /^\d+-/.test(value);
107
+ if (isLikelyPath) {
108
+ // It's likely a filename, create image object directly
109
+ const newImage = {
110
+ id: value,
111
+ filename: value,
112
+ url: `/api/uploads/${value}`,
113
+ size: 0,
114
+ mimeType: 'image/jpeg',
115
+ uploadedAt: new Date().toISOString(),
116
+ };
117
+ setSelectedImage(prev => {
118
+ if (prev?.id === newImage.id && prev?.url === newImage.url) {
119
+ return prev;
120
+ }
121
+ return newImage;
122
+ });
123
+ lastResolvedValueRef.current = value;
124
+ isResolvingRef.current = false;
125
+ }
126
+ else {
127
+ // It might be a semantic ID, try to resolve via API
128
+ // Use the extracted ID (filename) if value was a URL, otherwise use value
129
+ try {
130
+ const response = await fetch(`/api/plugin-images/resolve?id=${encodeURIComponent(imageIdToResolve)}`);
131
+ if (response.ok) {
132
+ const data = await response.json();
133
+ // Normalize the URL - ensure it's a proper URL
134
+ let imageUrl;
135
+ if (data.url) {
136
+ // If API returns a URL, use it (but check if it's already a full URL)
137
+ if (data.url.startsWith('http://') || data.url.startsWith('https://')) {
138
+ imageUrl = data.url;
139
+ }
140
+ else if (data.url.startsWith('/')) {
141
+ imageUrl = data.url;
142
+ }
143
+ else {
144
+ // Relative path without leading slash
145
+ imageUrl = `/api/uploads/${data.url}`;
146
+ }
147
+ }
148
+ else if (data.filename) {
149
+ // Use filename to construct URL
150
+ imageUrl = `/api/uploads/${data.filename}`;
151
+ }
152
+ else {
153
+ // Fallback: if original value was a URL, use it; otherwise construct
154
+ imageUrl = isFullUrl ? value : (isRelativeUrl ? value : `/api/uploads/${value}`);
155
+ }
156
+ const newImage = {
157
+ id: value,
158
+ filename: data.filename || filenameFromUrl || value,
159
+ url: imageUrl,
160
+ size: 0,
161
+ mimeType: 'image/jpeg',
162
+ uploadedAt: new Date().toISOString(),
163
+ };
164
+ setSelectedImage(prev => {
165
+ if (prev?.id === newImage.id && prev?.url === newImage.url) {
166
+ return prev;
167
+ }
168
+ return newImage;
169
+ });
170
+ lastResolvedValueRef.current = value;
171
+ isResolvingRef.current = false;
172
+ }
173
+ else {
174
+ // API resolution failed - check if it's a semantic ID or actual filename
175
+ const isLikelyFilename = /\.(jpg|jpeg|png|webp|gif|svg)$/i.test(imageIdToResolve) || /^\d+-/.test(imageIdToResolve);
176
+ if (!isLikelyFilename) {
177
+ // It's a semantic ID that couldn't be resolved - clear selection
178
+ // This prevents showing broken URLs like /api/uploads/blog-featured-...
179
+ setSelectedImage(null);
180
+ lastResolvedValueRef.current = value;
181
+ isResolvingRef.current = false;
182
+ return;
183
+ }
184
+ // It's a filename, create fallback object
185
+ const isFullUrl = value.startsWith('http://') || value.startsWith('https://');
186
+ const isRelativeUrl = value.startsWith('/');
187
+ let imageUrl;
188
+ if (isFullUrl) {
189
+ // Already a full URL, use as-is
190
+ imageUrl = value;
191
+ }
192
+ else if (isRelativeUrl) {
193
+ // Already a relative URL starting with /, use as-is
194
+ imageUrl = value;
195
+ }
196
+ else {
197
+ // It's a filename, construct the URL
198
+ imageUrl = `/api/uploads/${value}`;
199
+ }
200
+ const urlParts = value.split('/');
201
+ const filename = urlParts[urlParts.length - 1]?.split('?')[0] || value;
202
+ const newImage = {
203
+ id: value,
204
+ filename: filename,
205
+ url: imageUrl,
206
+ size: 0,
207
+ mimeType: 'image/jpeg',
208
+ uploadedAt: new Date().toISOString(),
209
+ };
210
+ setSelectedImage(prev => {
211
+ if (prev?.id === newImage.id && prev?.url === newImage.url) {
212
+ return prev;
213
+ }
214
+ return newImage;
215
+ });
216
+ lastResolvedValueRef.current = value;
217
+ isResolvingRef.current = false;
218
+ }
219
+ }
220
+ catch (error) {
221
+ // API call failed - check if it's a semantic ID or actual filename
222
+ const isLikelyFilename = /\.(jpg|jpeg|png|webp|gif|svg)$/i.test(imageIdToResolve) || /^\d+-/.test(imageIdToResolve);
223
+ if (!isLikelyFilename) {
224
+ // It's a semantic ID that couldn't be resolved - clear selection
225
+ // This prevents showing broken URLs like /api/uploads/blog-featured-...
226
+ setSelectedImage(null);
227
+ lastResolvedValueRef.current = value;
228
+ isResolvingRef.current = false;
229
+ return;
230
+ }
231
+ // It's a filename, create fallback object
232
+ const isFullUrl = value.startsWith('http://') || value.startsWith('https://');
233
+ const isRelativeUrl = value.startsWith('/');
234
+ let imageUrl;
235
+ if (isFullUrl) {
236
+ // Already a full URL, use as-is
237
+ imageUrl = value;
238
+ }
239
+ else if (isRelativeUrl) {
240
+ // Already a relative URL starting with /, use as-is
241
+ imageUrl = value;
242
+ }
243
+ else {
244
+ // It's a filename, construct the URL
245
+ imageUrl = `/api/uploads/${value}`;
246
+ }
247
+ const urlParts = value.split('/');
248
+ const filename = urlParts[urlParts.length - 1]?.split('?')[0] || value;
249
+ const newImage = {
250
+ id: value,
251
+ filename: filename,
252
+ url: imageUrl,
253
+ size: 0,
254
+ mimeType: 'image/jpeg',
255
+ uploadedAt: new Date().toISOString(),
256
+ };
257
+ setSelectedImage(prev => {
258
+ if (prev?.id === newImage.id && prev?.url === newImage.url) {
259
+ return prev;
260
+ }
261
+ return newImage;
262
+ });
263
+ lastResolvedValueRef.current = value;
264
+ isResolvingRef.current = false;
265
+ }
266
+ }
267
+ }
268
+ };
269
+ resolveImage().catch(() => {
270
+ // Error already handled in catch block
271
+ isResolvingRef.current = false;
272
+ });
273
+ }, [value, images]);
274
+ // Handle file upload
275
+ const handleFileSelect = async (e) => {
276
+ const file = e?.target?.files?.[0];
277
+ if (!file)
278
+ return null;
279
+ setUploading(true);
280
+ try {
281
+ const formData = new FormData();
282
+ formData.append('file', file);
283
+ const response = await fetch('/api/plugin-images/upload', {
284
+ method: 'POST',
285
+ body: formData,
286
+ });
287
+ const data = await response.json();
288
+ if (data.success && data.image) {
289
+ // Select the newly uploaded image
290
+ setSelectedImage(data.image);
291
+ if (fileInputRef.current) {
292
+ fileInputRef.current.value = '';
293
+ }
294
+ return data.image;
295
+ }
296
+ else {
297
+ alert(data.error || 'Failed to upload image');
298
+ return null;
299
+ }
300
+ }
301
+ catch (error) {
302
+ console.error('Upload error:', error);
303
+ alert('Failed to upload image');
304
+ return null;
305
+ }
306
+ finally {
307
+ setUploading(false);
308
+ if (fileInputRef.current) {
309
+ fileInputRef.current.value = '';
310
+ }
311
+ }
312
+ };
313
+ return {
314
+ selectedImage,
315
+ setSelectedImage,
316
+ uploading,
317
+ fileInputRef,
318
+ handleFileSelect,
319
+ };
320
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Plugin Images - Main Entry Point
3
+ * Image management plugin for uploading, searching, and managing images
4
+ */
5
+ export interface PluginProps {
6
+ subPath: string[];
7
+ siteId: string;
8
+ locale: string;
9
+ }
10
+ export default function ImagesPlugin({ subPath, siteId, locale }: PluginProps): import("react/jsx-runtime").JSX.Element;
11
+ export { ImagesPlugin as Index };
12
+ export { ImagePicker } from './components/ImagePicker';
13
+ export { GlobalImageEditor } from './components/GlobalImageEditor';
14
+ export { ImagesPluginInit } from './components/ImagesPluginInit';
15
+ export { Image } from './components/Image';
16
+ export { BackgroundImage } from './components/BackgroundImage';
17
+ export type { ImagePickerProps, ImageMetadata } from './types';
18
+ export type { PluginImageProps } from './components/Image';
19
+ export type { BackgroundImageProps } from './components/BackgroundImage';
20
+ export { initImagesPlugin } from './init';
21
+ export type { ImagesPluginConfig } from './init';
22
+ export { getFallbackImageUrl, isValidImageUrl, getSafeImageUrl, constructImageUrl } from './utils/fallback';
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,WAAW,2CAU5E;AAGD,OAAO,EAAE,YAAY,IAAI,KAAK,EAAE,CAAC;AAGjC,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAGzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAC1C,YAAY,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAGjD,OAAO,EACH,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,iBAAiB,EACpB,MAAM,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Plugin Images - Main Entry Point
3
+ * Image management plugin for uploading, searching, and managing images
4
+ */
5
+ 'use client';
6
+ import { jsx as _jsx } from "react/jsx-runtime";
7
+ import { ImageManagerView } from './views/ImageManager';
8
+ export default function ImagesPlugin({ subPath, siteId, locale }) {
9
+ const route = subPath[0] || 'manager';
10
+ switch (route) {
11
+ case 'manager':
12
+ return _jsx(ImageManagerView, { siteId: siteId, locale: locale });
13
+ default:
14
+ return _jsx(ImageManagerView, { siteId: siteId, locale: locale });
15
+ }
16
+ }
17
+ // Export for use as default
18
+ export { ImagesPlugin as Index };
19
+ // Export components for use in other plugins
20
+ export { ImagePicker } from './components/ImagePicker';
21
+ export { GlobalImageEditor } from './components/GlobalImageEditor';
22
+ export { ImagesPluginInit } from './components/ImagesPluginInit';
23
+ export { Image } from './components/Image';
24
+ export { BackgroundImage } from './components/BackgroundImage';
25
+ // Export initialization utility
26
+ export { initImagesPlugin } from './init';
27
+ // Export utility functions
28
+ export { getFallbackImageUrl, isValidImageUrl, getSafeImageUrl, constructImageUrl } from './utils/fallback';
@@ -0,0 +1,11 @@
1
+ import 'server-only';
2
+ /**
3
+ * Plugin Images - Server-Only Entry Point
4
+ * This file exports only server-side API handlers
5
+ * Used by the dynamic plugin router via @jhits/plugin-images/server
6
+ *
7
+ * Note: This file is server-only (no 'use server' needed - that's only for Server Actions)
8
+ */
9
+ export { handleImagesApi as handleApi } from './api/router';
10
+ export { handleImagesApi } from './api/router';
11
+ //# sourceMappingURL=index.server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.server.d.ts","sourceRoot":"","sources":["../src/index.server.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AACrB;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,IAAI,SAAS,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,10 @@
1
+ import 'server-only';
2
+ /**
3
+ * Plugin Images - Server-Only Entry Point
4
+ * This file exports only server-side API handlers
5
+ * Used by the dynamic plugin router via @jhits/plugin-images/server
6
+ *
7
+ * Note: This file is server-only (no 'use server' needed - that's only for Server Actions)
8
+ */
9
+ export { handleImagesApi as handleApi } from './api/router';
10
+ export { handleImagesApi } from './api/router'; // Keep original export for backward compatibility
package/dist/init.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Images Plugin Initialization Utility
3
+ *
4
+ * Simple function to initialize the images plugin with client configuration.
5
+ * Call this once in your app (e.g., in root layout) to enable global image editing.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { initImagesPlugin } from '@jhits/plugin-images/init';
10
+ * import { imagesConfig } from '@/plugins/images-config';
11
+ *
12
+ * // Call once when your app loads
13
+ * initImagesPlugin(imagesConfig);
14
+ * ```
15
+ */
16
+ export interface ImagesPluginConfig {
17
+ /** Enable global image editor (default: true) */
18
+ enabled?: boolean;
19
+ /** Custom styling for the editor modal */
20
+ className?: string;
21
+ /** Custom styling for the modal overlay */
22
+ overlayClassName?: string;
23
+ }
24
+ /**
25
+ * Initialize the images plugin with client configuration
26
+ *
27
+ * This function sets up the window global that the plugin reads from automatically.
28
+ * Call this once when your app loads, before the plugin component is rendered.
29
+ *
30
+ * @param config - Images plugin configuration (enabled, styling, etc.)
31
+ */
32
+ export declare function initImagesPlugin(config?: ImagesPluginConfig): void;
33
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,WAAW,kBAAkB;IAC/B,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAqBlE"}