@merchi/product-editor 0.1.9

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 (123) hide show
  1. package/.turbo/turbo-build.log +1 -0
  2. package/README.md +174 -0
  3. package/dist/components/AlignMenu.d.ts +3 -0
  4. package/dist/components/AlignMenu.js +61 -0
  5. package/dist/components/FloatingToolbar.d.ts +4 -0
  6. package/dist/components/FloatingToolbar.js +97 -0
  7. package/dist/components/FormatButtons.d.ts +2 -0
  8. package/dist/components/FormatButtons.js +22 -0
  9. package/dist/components/ImageNavButton.d.ts +7 -0
  10. package/dist/components/ImageNavButton.js +14 -0
  11. package/dist/components/ImageZoomModal.d.ts +12 -0
  12. package/dist/components/ImageZoomModal.js +129 -0
  13. package/dist/components/LayerPanel.d.ts +4 -0
  14. package/dist/components/LayerPanel.js +223 -0
  15. package/dist/components/LoadingOverlay.d.ts +9 -0
  16. package/dist/components/LoadingOverlay.js +17 -0
  17. package/dist/components/ProductEditor.d.ts +4 -0
  18. package/dist/components/ProductEditor.js +67 -0
  19. package/dist/components/ProductImageGallery.d.ts +6 -0
  20. package/dist/components/ProductImageGallery.js +148 -0
  21. package/dist/components/ProductPreviews.d.ts +7 -0
  22. package/dist/components/ProductPreviews.js +281 -0
  23. package/dist/components/TextToolbar.d.ts +4 -0
  24. package/dist/components/TextToolbar.js +117 -0
  25. package/dist/components/Toolbar.d.ts +3 -0
  26. package/dist/components/Toolbar.js +47 -0
  27. package/dist/config/colorConfig.d.ts +1 -0
  28. package/dist/config/colorConfig.js +10 -0
  29. package/dist/config/fontConfig.d.ts +5 -0
  30. package/dist/config/fontConfig.js +21 -0
  31. package/dist/context/ProductEditorContext.d.ts +60 -0
  32. package/dist/context/ProductEditorContext.js +743 -0
  33. package/dist/hooks/useSwipeNavigate.d.ts +11 -0
  34. package/dist/hooks/useSwipeNavigate.js +35 -0
  35. package/dist/index.d.ts +6 -0
  36. package/dist/index.js +30 -0
  37. package/dist/styles/BottomPreviewDisplay.css +65 -0
  38. package/dist/styles/FloatingToolbar.css +41 -0
  39. package/dist/styles/ImageGallery.css +147 -0
  40. package/dist/styles/ImageNavButton.css +35 -0
  41. package/dist/styles/ImageZoomModal.css +101 -0
  42. package/dist/styles/LayerPanel.css +179 -0
  43. package/dist/styles/LoadingOverlay.css +48 -0
  44. package/dist/styles/ProductEditor.css +192 -0
  45. package/dist/styles/TextToolbar.css +327 -0
  46. package/dist/styles/Toolbar.css +125 -0
  47. package/dist/styles/index.css +5 -0
  48. package/dist/styles/textToolbarSelectStyles.d.ts +3 -0
  49. package/dist/styles/textToolbarSelectStyles.js +30 -0
  50. package/dist/types.d.ts +228 -0
  51. package/dist/types.js +17 -0
  52. package/dist/utils/canvasAddText.d.ts +11 -0
  53. package/dist/utils/canvasAddText.js +66 -0
  54. package/dist/utils/canvasUtils.d.ts +37 -0
  55. package/dist/utils/canvasUtils.js +342 -0
  56. package/dist/utils/deviceUtils.d.ts +5 -0
  57. package/dist/utils/deviceUtils.js +33 -0
  58. package/dist/utils/draftTemplateUtils.d.ts +23 -0
  59. package/dist/utils/draftTemplateUtils.js +66 -0
  60. package/dist/utils/grid.d.ts +32 -0
  61. package/dist/utils/grid.js +151 -0
  62. package/dist/utils/imageUtils.d.ts +12 -0
  63. package/dist/utils/imageUtils.js +178 -0
  64. package/dist/utils/job.d.ts +40 -0
  65. package/dist/utils/job.js +115 -0
  66. package/dist/utils/keyboard.d.ts +1 -0
  67. package/dist/utils/keyboard.js +26 -0
  68. package/dist/utils/previewUtils.d.ts +9 -0
  69. package/dist/utils/previewUtils.js +39 -0
  70. package/dist/utils/psdConverter.d.ts +40 -0
  71. package/dist/utils/psdConverter.js +516 -0
  72. package/dist/utils/psdRenderUtils.d.ts +12 -0
  73. package/dist/utils/psdRenderUtils.js +465 -0
  74. package/dist/utils/renderUtils.d.ts +2 -0
  75. package/dist/utils/renderUtils.js +85 -0
  76. package/package.json +66 -0
  77. package/src/components/AlignMenu.tsx +49 -0
  78. package/src/components/FloatingToolbar.tsx +83 -0
  79. package/src/components/FormatButtons.tsx +31 -0
  80. package/src/components/ImageNavButton.tsx +23 -0
  81. package/src/components/ImageZoomModal.tsx +163 -0
  82. package/src/components/LayerPanel.tsx +256 -0
  83. package/src/components/LoadingOverlay.tsx +27 -0
  84. package/src/components/ProductEditor.tsx +104 -0
  85. package/src/components/ProductImageGallery.tsx +181 -0
  86. package/src/components/ProductPreviews.tsx +243 -0
  87. package/src/components/TextToolbar.tsx +164 -0
  88. package/src/components/Toolbar.tsx +86 -0
  89. package/src/config/colorConfig.ts +7 -0
  90. package/src/config/fontConfig.ts +23 -0
  91. package/src/context/ProductEditorContext.tsx +829 -0
  92. package/src/hooks/useSwipeNavigate.ts +40 -0
  93. package/src/index.ts +6 -0
  94. package/src/styles/BottomPreviewDisplay.css +65 -0
  95. package/src/styles/FloatingToolbar.css +41 -0
  96. package/src/styles/ImageGallery.css +147 -0
  97. package/src/styles/ImageNavButton.css +35 -0
  98. package/src/styles/ImageZoomModal.css +101 -0
  99. package/src/styles/LayerPanel.css +179 -0
  100. package/src/styles/LoadingOverlay.css +48 -0
  101. package/src/styles/ProductEditor.css +192 -0
  102. package/src/styles/TextToolbar.css +327 -0
  103. package/src/styles/Toolbar.css +125 -0
  104. package/src/styles/index.css +5 -0
  105. package/src/styles/textToolbarSelectStyles.ts +60 -0
  106. package/src/types/ag-psd/index.d.ts +1 -0
  107. package/src/types/fabric.d.ts +14 -0
  108. package/src/types/lodash/index.d.ts +1 -0
  109. package/src/types/psd.d.ts +53 -0
  110. package/src/types.ts +247 -0
  111. package/src/utils/canvasAddText.ts +72 -0
  112. package/src/utils/canvasUtils.ts +406 -0
  113. package/src/utils/deviceUtils.ts +31 -0
  114. package/src/utils/draftTemplateUtils.ts +70 -0
  115. package/src/utils/grid.ts +154 -0
  116. package/src/utils/imageUtils.ts +218 -0
  117. package/src/utils/job.ts +128 -0
  118. package/src/utils/keyboard.ts +28 -0
  119. package/src/utils/previewUtils.ts +43 -0
  120. package/src/utils/psdConverter.ts +595 -0
  121. package/src/utils/psdRenderUtils.ts +453 -0
  122. package/src/utils/renderUtils.ts +44 -0
  123. package/tsconfig.json +17 -0
@@ -0,0 +1,453 @@
1
+ import { readPsd } from 'ag-psd';
2
+ import { MappedPreview } from '../types';
3
+
4
+ /**
5
+ * Find a layer by name within a PSD layer structure
6
+ *
7
+ * @param layers The array of PSD layers to search through
8
+ * @param name The name of the layer to find
9
+ * @returns The found layer or null if not found
10
+ */
11
+ function findLayerByName(layers: any[] | undefined, name: string): any | null {
12
+ if (!layers) return null;
13
+
14
+ for (const layer of layers) {
15
+ if (layer.name === name) {
16
+ return layer;
17
+ }
18
+
19
+ if (layer.children) {
20
+ const found = findLayerByName(layer.children, name);
21
+ if (found) return found;
22
+ }
23
+ }
24
+
25
+ return null;
26
+ }
27
+
28
+ /**
29
+ * Load an image from a URL
30
+ *
31
+ * @param url URL of the image to load
32
+ * @returns Promise that resolves to an HTMLImageElement
33
+ */
34
+ async function loadImage(url: string): Promise<HTMLImageElement> {
35
+ return new Promise((resolve, reject) => {
36
+ const img = new Image();
37
+ img.crossOrigin = 'anonymous';
38
+
39
+ img.onload = () => resolve(img);
40
+ img.onerror = (e) => reject(new Error(`Failed to load image from URL: ${url}, error: ${e}`));
41
+
42
+ img.src = url;
43
+ });
44
+ }
45
+
46
+ /**
47
+ * Validates if a data URL represents a valid image
48
+ *
49
+ * @param dataUrl The data URL to validate
50
+ * @returns Promise that resolves to true if valid, false otherwise
51
+ */
52
+ async function isValidImageDataUrl(dataUrl: string): Promise<boolean> {
53
+ return new Promise((resolve) => {
54
+ if (!dataUrl || !dataUrl.startsWith('data:image/')) {
55
+ console.warn('Invalid data URL format');
56
+ resolve(false);
57
+ return;
58
+ }
59
+
60
+ const img = new Image();
61
+ img.onload = () => {
62
+ // Check if the image has actual dimensions
63
+ if (img.width > 0 && img.height > 0) {
64
+ resolve(true);
65
+ } else {
66
+ console.warn('Image loaded but has zero dimensions');
67
+ resolve(false);
68
+ }
69
+ };
70
+ img.onerror = () => {
71
+ console.warn('Failed to load image from data URL');
72
+ resolve(false);
73
+ };
74
+ img.src = dataUrl;
75
+ });
76
+ }
77
+
78
+ /**
79
+ * Creates a fallback preview image with debugging information
80
+ *
81
+ * @param width Width of the image
82
+ * @param height Height of the image
83
+ * @param draftPreviewId ID of the draft preview
84
+ * @param error Optional error message
85
+ * @returns PNG data URL
86
+ */
87
+ function createFallbackImage(width: number, height: number, draftPreviewId: number | undefined, error?: string): string {
88
+ const canvas = document.createElement('canvas');
89
+ canvas.width = Math.max(width || 300, 300);
90
+ canvas.height = Math.max(height || 200, 200);
91
+ const ctx = canvas.getContext('2d');
92
+
93
+ if (!ctx) {
94
+ // Even more basic fallback if context fails
95
+ const basicCanvas = document.createElement('canvas');
96
+ basicCanvas.width = 300;
97
+ basicCanvas.height = 200;
98
+ const basicCtx = basicCanvas.getContext('2d');
99
+ if (basicCtx) {
100
+ basicCtx.fillStyle = '#ffcccc';
101
+ basicCtx.fillRect(0, 0, 300, 200);
102
+ basicCtx.fillStyle = '#990000';
103
+ basicCtx.font = '16px Arial';
104
+ basicCtx.textAlign = 'center';
105
+ basicCtx.fillText('Preview failed', 150, 100);
106
+ return basicCanvas.toDataURL('image/png');
107
+ }
108
+ return '';
109
+ }
110
+
111
+ // Background
112
+ ctx.fillStyle = '#f8f8f8';
113
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
114
+
115
+ // Draw a grid pattern
116
+ ctx.strokeStyle = '#e0e0e0';
117
+ ctx.lineWidth = 1;
118
+ const gridSize = 20;
119
+
120
+ for (let x = 0; x < canvas.width; x += gridSize) {
121
+ ctx.beginPath();
122
+ ctx.moveTo(x, 0);
123
+ ctx.lineTo(x, canvas.height);
124
+ ctx.stroke();
125
+ }
126
+
127
+ for (let y = 0; y < canvas.height; y += gridSize) {
128
+ ctx.beginPath();
129
+ ctx.moveTo(0, y);
130
+ ctx.lineTo(canvas.width, y);
131
+ ctx.stroke();
132
+ }
133
+
134
+ // Border
135
+ ctx.strokeStyle = '#cccccc';
136
+ ctx.lineWidth = 2;
137
+ ctx.strokeRect(0, 0, canvas.width, canvas.height);
138
+
139
+ // Preview text
140
+ ctx.fillStyle = '#666666';
141
+ ctx.font = 'bold 24px Arial';
142
+ ctx.textAlign = 'center';
143
+ ctx.fillText('Preview', canvas.width / 2, canvas.height / 2 - 15);
144
+
145
+ // Preview ID
146
+ if (draftPreviewId !== undefined) {
147
+ ctx.font = '16px Arial';
148
+ ctx.fillText(`ID: ${draftPreviewId}`, canvas.width / 2, canvas.height / 2 + 15);
149
+ }
150
+
151
+ // Error message if provided
152
+ if (error) {
153
+ ctx.fillStyle = '#cc0000';
154
+ ctx.font = '14px Arial';
155
+ ctx.fillText(error, canvas.width / 2, canvas.height / 2 + 40);
156
+ }
157
+
158
+ return canvas.toDataURL('image/png');
159
+ }
160
+
161
+ /**
162
+ * Converts a data URL to a canvas element
163
+ *
164
+ * @param dataUrl The data URL to convert
165
+ * @returns Promise that resolves to a Canvas element
166
+ */
167
+ async function dataUrlToCanvas(dataUrl: string): Promise<HTMLImageElement> {
168
+ // First load the data URL as an image
169
+ return await loadImage(dataUrl);
170
+ }
171
+
172
+ /**
173
+ * Process mapped previews, replacing layers in PSD files with rendered layers
174
+ * and converting the results to PNG images
175
+ *
176
+ * @param mappedPreviews Array of mapped previews with draftPreview and draftPreviewLayers
177
+ * @returns Promise that resolves to an array of processed previews with PNG data URLs
178
+ */
179
+ export async function renderDraftPreviewsWithLayers(
180
+ mappedPreviews: MappedPreview[]
181
+ ): Promise<Array<{ draftPreviewId: number | undefined; pngDataUrl: string }>> {
182
+ const results: Array<{ draftPreviewId: number | undefined; pngDataUrl: string }> = [];
183
+
184
+ // Process each preview one by one
185
+ for (const mappedPreview of mappedPreviews) {
186
+ const { draftPreview, draftPreviewLayers } = mappedPreview;
187
+
188
+ // Skip if there's no file to process
189
+ if (!draftPreview?.file?.viewUrl) {
190
+ console.warn('Skipping preview without a file URL', draftPreview?.id);
191
+ continue;
192
+ }
193
+
194
+ // Check if there are any actual rendered layers we need to use
195
+ const hasRenderedLayers = draftPreviewLayers.some(layer => layer.renderedLayer !== null);
196
+
197
+ // Only try the simple approach if it's not a PSD file
198
+ let simplePngDataUrl: string | null = null;
199
+
200
+ // If we need to do layer replacement, proceed with PSD processing
201
+ try {
202
+ // Fetch the PSD file
203
+ const response = await fetch(draftPreview.file.viewUrl, {
204
+ mode: 'cors',
205
+ credentials: 'same-origin',
206
+ headers: {
207
+ 'Accept': '*/*',
208
+ }
209
+ });
210
+
211
+ if (!response.ok) {
212
+ throw new Error(`Failed to fetch PSD file: ${response.status} ${response.statusText}`);
213
+ }
214
+
215
+ const arrayBuffer = await response.arrayBuffer();
216
+
217
+ // Check if we actually got a PSD file (PSD files start with '8BPS')
218
+ const firstBytes = new Uint8Array(arrayBuffer, 0, 4);
219
+ const signature =
220
+ String.fromCharCode(firstBytes[0]) +
221
+ String.fromCharCode(firstBytes[1]) +
222
+ String.fromCharCode(firstBytes[2]) +
223
+ String.fromCharCode(firstBytes[3]);
224
+
225
+ if (signature !== '8BPS') {
226
+ // Use the simple approach result if we have it
227
+ if (simplePngDataUrl) {
228
+ results.push({
229
+ draftPreviewId: draftPreview.id,
230
+ pngDataUrl: simplePngDataUrl
231
+ });
232
+ continue;
233
+ }
234
+ throw new Error('Not a PSD file and simple approach failed');
235
+ }
236
+
237
+ // Read the PSD file
238
+ const psd = readPsd(arrayBuffer, {
239
+ skipCompositeImageData: false,
240
+ skipLayerImageData: false,
241
+ skipThumbnail: false
242
+ });
243
+
244
+ // Create a canvas to draw the composite image
245
+ const canvas = document.createElement('canvas');
246
+ canvas.width = psd.width || 500; // Default size if missing
247
+ canvas.height = psd.height || 400;
248
+ const ctx = canvas.getContext('2d');
249
+
250
+ if (!ctx) {
251
+ throw new Error('Failed to get canvas 2d context');
252
+ }
253
+
254
+ // Draw a background to ensure we have something visible
255
+ ctx.fillStyle = 'transparent';
256
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
257
+
258
+ // First draw the composite image as a base
259
+ if (psd.imageData) {
260
+ const imageData = new ImageData(
261
+ new Uint8ClampedArray(psd.imageData.data),
262
+ psd.width,
263
+ psd.height
264
+ );
265
+ ctx.putImageData(imageData, 0, 0);
266
+ }
267
+
268
+ // Now draw base layers that aren't being replaced
269
+ if (psd.children && psd.children.length > 0) {
270
+ for (const layer of psd.children) {
271
+ // Skip if this layer is hidden or matches one of our replaceable layers
272
+ const shouldReplace = draftPreviewLayers.some(dpl =>
273
+ dpl.layerName === layer.name && dpl.renderedLayer !== null
274
+ );
275
+
276
+ if (!shouldReplace && !layer.hidden && layer.canvas) {
277
+ // Draw the original layer
278
+ ctx.drawImage(layer.canvas, layer.left || 0, layer.top || 0);
279
+ ctx.globalAlpha = 1;
280
+ }
281
+ }
282
+ }
283
+
284
+ // Now process and draw each replacement layer
285
+ if (hasRenderedLayers) {
286
+ for (const draftPreviewLayer of draftPreviewLayers) {
287
+ // Skip if no layer name or no rendered layer
288
+ if (!draftPreviewLayer.layerName || !draftPreviewLayer.renderedLayer) {
289
+ continue;
290
+ }
291
+
292
+ // Find the corresponding layer in the PSD
293
+ const psdLayer = findLayerByName(psd.children, draftPreviewLayer.layerName);
294
+
295
+ if (!psdLayer) {
296
+ console.warn(`Layer "${draftPreviewLayer.layerName}" not found in PSD`);
297
+ continue;
298
+ }
299
+
300
+ // Load the rendered image
301
+ try {
302
+ // Validate the rendered layer image URL
303
+ if (!draftPreviewLayer.renderedLayer.draft ||
304
+ !draftPreviewLayer.renderedLayer.draft.startsWith('data:image/')) {
305
+ throw new Error('Invalid image URL format');
306
+ }
307
+
308
+ // Convert the data URL to a canvas element instead of an image
309
+ const layerCanvas = await dataUrlToCanvas(draftPreviewLayer.renderedLayer.draft);
310
+
311
+ // Validate the canvas dimensions
312
+ if (layerCanvas.width <= 0 || layerCanvas.height <= 0) {
313
+ console.error('Canvas has invalid dimensions');
314
+ throw new Error('Invalid canvas dimensions');
315
+ }
316
+
317
+ // Use the position and dimensions of the original PSD layer
318
+ const left = psdLayer.left || 0;
319
+ const top = psdLayer.top || 0;
320
+ const width = (psdLayer.right || 0) - (psdLayer.left || 0);
321
+ const height = (psdLayer.bottom || 0) - (psdLayer.top || 0);
322
+
323
+ if (width <= 0 || height <= 0) {
324
+ console.warn(`Invalid layer dimensions: ${width}x${height}, skipping`);
325
+ continue;
326
+ }
327
+
328
+ // Calculate how to fit the canvas within the layer bounds
329
+ const aspectRatio = layerCanvas.width / layerCanvas.height;
330
+ const targetAspectRatio = width / height;
331
+
332
+ let drawWidth, drawHeight, offsetX = 0, offsetY = 0;
333
+
334
+ if (aspectRatio > targetAspectRatio) {
335
+ // Canvas is wider than target area
336
+ drawWidth = width;
337
+ drawHeight = width / aspectRatio;
338
+ offsetY = (height - drawHeight) / 2;
339
+ } else {
340
+ // Canvas is taller than target area
341
+ drawHeight = height;
342
+ drawWidth = height * aspectRatio;
343
+ offsetX = (width - drawWidth) / 2;
344
+ }
345
+
346
+ // Draw the rendered layer canvas
347
+ // ctx.globalAlpha = psdLayer.opacity !== undefined ? psdLayer.opacity / 255 : 1;
348
+ ctx.restore();
349
+ ctx.drawImage(layerCanvas, left + offsetX, top + offsetY, drawWidth, drawHeight);
350
+ ctx.globalAlpha = 1;
351
+
352
+ ctx.save();
353
+
354
+
355
+
356
+
357
+ } catch (error) {
358
+ console.error(`Failed to load rendered layer image for "${draftPreviewLayer.layerName}"`, error);
359
+
360
+ // If we fail to load the replacement, draw the original layer as fallback
361
+ if (psdLayer.canvas) {
362
+ ctx.globalAlpha = psdLayer.opacity !== undefined ? psdLayer.opacity / 255 : 1;
363
+ ctx.drawImage(psdLayer.canvas, psdLayer.left || 0, psdLayer.top || 0);
364
+ ctx.globalAlpha = 1;
365
+ }
366
+ }
367
+ }
368
+ }
369
+
370
+ // Verify that something was drawn
371
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
372
+ let hasVisiblePixels = false;
373
+
374
+ for (let i = 3; i < imageData.data.length; i += 4) {
375
+ if (imageData.data[i] > 0) {
376
+ hasVisiblePixels = true;
377
+ break;
378
+ }
379
+ }
380
+
381
+ if (!hasVisiblePixels) {
382
+ console.warn('WARNING: Canvas appears to be completely transparent!');
383
+
384
+ // Use the simple approach result if available
385
+ if (simplePngDataUrl) {
386
+ results.push({
387
+ draftPreviewId: draftPreview.id,
388
+ pngDataUrl: simplePngDataUrl
389
+ });
390
+ continue;
391
+ }
392
+
393
+ // As a last resort, draw a visible placeholder
394
+ ctx.fillStyle = 'rgba(200, 200, 200, 0.5)';
395
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
396
+ ctx.fillStyle = 'rgba(150, 150, 150, 1)';
397
+ ctx.font = '20px Arial';
398
+ ctx.textAlign = 'center';
399
+ ctx.fillText('Preview', canvas.width / 2, canvas.height / 2);
400
+ }
401
+
402
+ // Convert the canvas to a PNG data URL
403
+ const pngDataUrl = canvas.toDataURL('image/png');
404
+
405
+ // Validate the PNG data URL
406
+ const isValid = await isValidImageDataUrl(pngDataUrl);
407
+ if (!isValid) {
408
+ console.warn('Generated PNG data URL is not valid!');
409
+
410
+ // Use the simple approach as fallback if available
411
+ if (simplePngDataUrl) {
412
+ results.push({
413
+ draftPreviewId: draftPreview.id,
414
+ pngDataUrl: simplePngDataUrl
415
+ });
416
+ } else {
417
+ // Create a debug fallback image with error information
418
+ const fallbackPng = createFallbackImage(500, 400, draftPreview.id, 'Invalid PNG data');
419
+ results.push({
420
+ draftPreviewId: draftPreview.id,
421
+ pngDataUrl: fallbackPng
422
+ });
423
+ }
424
+ } else {
425
+ // Add the result to our array
426
+ results.push({
427
+ draftPreviewId: draftPreview.id,
428
+ pngDataUrl
429
+ });
430
+ }
431
+
432
+ } catch (error) {
433
+ console.error('Failed to process PSD file for draft preview:', error);
434
+
435
+ // Use the simple approach as fallback if available
436
+ if (simplePngDataUrl) {
437
+ results.push({
438
+ draftPreviewId: draftPreview.id,
439
+ pngDataUrl: simplePngDataUrl
440
+ });
441
+ } else {
442
+ // Create a debug fallback image with error information
443
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
444
+ const fallbackPng = createFallbackImage(500, 400, draftPreview.id, errorMessage);
445
+ results.push({
446
+ draftPreviewId: draftPreview.id,
447
+ pngDataUrl: fallbackPng
448
+ });
449
+ }
450
+ }
451
+ }
452
+ return results;
453
+ }
@@ -0,0 +1,44 @@
1
+ import { DraftTemplate, SavedCanvasObject } from "../types";
2
+ import { loadRegularImagePromise } from "./imageUtils";
3
+ import { loadPsdOntoCanvas } from "./psdConverter";
4
+
5
+ // Simplify loadTemplateImage to just load the template without variations
6
+ export const renderEditorOrPreview = async (
7
+ fabricCanvas: fabric.Canvas,
8
+ template: DraftTemplate,
9
+ variations: any[],
10
+ savedObjects: SavedCanvasObject[],
11
+ width: number,
12
+ height: number,
13
+ ): Promise<void> => {
14
+ // Check if we have a valid image URL
15
+ if (!template.file?.viewUrl) {
16
+ return Promise.resolve();
17
+ }
18
+
19
+ const file = template.file;
20
+
21
+ const imageUrl = file.viewUrl;
22
+
23
+ // Check if this might be a PSD file
24
+ const isPsd = imageUrl.toLowerCase().endsWith('.psd')
25
+ || (file.mimetype && file.mimetype.includes('photoshop'));
26
+
27
+ // If it's a PSD file, use our specialized function to process it
28
+ if (isPsd) {
29
+ // First, check if the canvas is still valid
30
+ try {
31
+ // Process the PSD file
32
+ await loadPsdOntoCanvas(fabricCanvas, imageUrl, variations, savedObjects, width, height);
33
+ return Promise.resolve();
34
+ } catch (error) {
35
+ // If PSD processing fails, fall back to our regular image loading
36
+ await loadRegularImagePromise(fabricCanvas, template, width, height);
37
+ return Promise.resolve();
38
+ }
39
+ }
40
+
41
+ // For non-PSD files, use the regular image loading approach
42
+ await loadRegularImagePromise(fabricCanvas, template, width, height);
43
+ return Promise.resolve();
44
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es5",
4
+ "module": "commonjs",
5
+ "lib": ["dom", "es2015"],
6
+ "jsx": "react",
7
+ "declaration": true,
8
+ "outDir": "./dist",
9
+ "strict": true,
10
+ "moduleResolution": "node",
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "forceConsistentCasingInFileNames": true
14
+ },
15
+ "include": ["src"],
16
+ "exclude": ["node_modules", "dist"]
17
+ }