@umituz/web-canvas 1.0.1 → 1.0.2
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.
- package/dist/application/index.d.mts +1 -1
- package/dist/application/index.d.ts +1 -1
- package/dist/application/index.js +103 -40
- package/dist/application/index.mjs +2 -2
- package/dist/{chunk-BN7JTXL7.mjs → chunk-DNKRSL6D.mjs} +24 -12
- package/dist/{chunk-SF6WND7M.mjs → chunk-LOVI2LTO.mjs} +88 -28
- package/dist/{chunk-WMKPCJFI.mjs → chunk-UOGTEMIJ.mjs} +80 -29
- package/dist/index.js +186 -63
- package/dist/index.mjs +3 -3
- package/dist/infrastructure/index.d.mts +30 -6
- package/dist/infrastructure/index.d.ts +30 -6
- package/dist/infrastructure/index.js +80 -29
- package/dist/infrastructure/index.mjs +1 -1
- package/dist/presentation/index.js +186 -63
- package/dist/presentation/index.mjs +3 -3
- package/package.json +1 -1
- package/src/application/services/ScreenshotStudioService.ts +35 -12
- package/src/infrastructure/renderers/CanvasRenderer.ts +30 -3
- package/src/infrastructure/renderers/PatternRenderer.ts +43 -23
- package/src/infrastructure/renderers/ScreenshotRenderer.ts +14 -1
- package/src/infrastructure/services/DeviceConfigService.ts +18 -3
- package/src/presentation/hooks/useCanvasExport.ts +48 -8
- package/src/presentation/hooks/useReportExport.ts +38 -12
- package/src/presentation/hooks/useScreenshotStudio.ts +35 -6
package/dist/index.js
CHANGED
|
@@ -107,6 +107,7 @@ var DEFAULT_SCREENSHOT_STUDIO_CONFIG = {
|
|
|
107
107
|
var CanvasRenderer = class {
|
|
108
108
|
constructor(config = DEFAULT_CANVAS_CONFIG) {
|
|
109
109
|
this.config = { ...DEFAULT_CANVAS_CONFIG, ...config };
|
|
110
|
+
this.imageCache = /* @__PURE__ */ new Map();
|
|
110
111
|
}
|
|
111
112
|
/**
|
|
112
113
|
* Create a new canvas element
|
|
@@ -208,17 +209,37 @@ var CanvasRenderer = class {
|
|
|
208
209
|
}
|
|
209
210
|
}
|
|
210
211
|
/**
|
|
211
|
-
* Load image from URL
|
|
212
|
+
* Load image from URL with caching
|
|
212
213
|
*/
|
|
213
214
|
loadImage(url) {
|
|
215
|
+
if (this.imageCache.has(url)) {
|
|
216
|
+
return Promise.resolve(this.imageCache.get(url));
|
|
217
|
+
}
|
|
214
218
|
return new Promise((resolve, reject) => {
|
|
215
219
|
const img = new Image();
|
|
216
|
-
|
|
217
|
-
|
|
220
|
+
if (this.config.useCORS) {
|
|
221
|
+
img.crossOrigin = "anonymous";
|
|
222
|
+
}
|
|
223
|
+
img.onload = () => {
|
|
224
|
+
this.imageCache.set(url, img);
|
|
225
|
+
resolve(img);
|
|
226
|
+
};
|
|
218
227
|
img.onerror = () => reject(new Error(`Failed to load image: ${url}`));
|
|
219
228
|
img.src = url;
|
|
220
229
|
});
|
|
221
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* Clear image cache to free memory
|
|
233
|
+
*/
|
|
234
|
+
clearImageCache() {
|
|
235
|
+
this.imageCache.clear();
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Get cache size for monitoring
|
|
239
|
+
*/
|
|
240
|
+
getCacheSize() {
|
|
241
|
+
return this.imageCache.size;
|
|
242
|
+
}
|
|
222
243
|
/**
|
|
223
244
|
* Create gradient
|
|
224
245
|
*/
|
|
@@ -300,10 +321,15 @@ var CanvasRenderer = class {
|
|
|
300
321
|
|
|
301
322
|
// src/infrastructure/renderers/PatternRenderer.ts
|
|
302
323
|
var PatternRenderer = class {
|
|
324
|
+
constructor() {
|
|
325
|
+
this.patternCanvasCache = /* @__PURE__ */ new Map();
|
|
326
|
+
this.MAX_CACHE_SIZE = 10;
|
|
327
|
+
}
|
|
303
328
|
/**
|
|
304
329
|
* Render pattern on canvas
|
|
305
330
|
*/
|
|
306
331
|
renderPattern(ctx, pattern, dimensions, color = "#000000") {
|
|
332
|
+
if (pattern === "none") return;
|
|
307
333
|
ctx.save();
|
|
308
334
|
ctx.globalAlpha = 0.1;
|
|
309
335
|
switch (pattern) {
|
|
@@ -322,62 +348,65 @@ var PatternRenderer = class {
|
|
|
322
348
|
case "checkers":
|
|
323
349
|
this.renderCheckerPattern(ctx, dimensions, color);
|
|
324
350
|
break;
|
|
325
|
-
case "none":
|
|
326
|
-
default:
|
|
327
|
-
break;
|
|
328
351
|
}
|
|
329
352
|
ctx.restore();
|
|
330
353
|
}
|
|
331
354
|
/**
|
|
332
|
-
*
|
|
355
|
+
* Clear pattern cache to free memory
|
|
356
|
+
*/
|
|
357
|
+
clearCache() {
|
|
358
|
+
this.patternCanvasCache.clear();
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Render dot pattern - optimized with batch rendering
|
|
333
362
|
*/
|
|
334
363
|
renderDotPattern(ctx, dimensions, color) {
|
|
335
364
|
ctx.fillStyle = color;
|
|
336
365
|
const spacing = 30;
|
|
366
|
+
const radius = 2;
|
|
367
|
+
ctx.beginPath();
|
|
337
368
|
for (let x = spacing; x < dimensions.width; x += spacing) {
|
|
338
369
|
for (let y = spacing; y < dimensions.height; y += spacing) {
|
|
339
|
-
ctx.
|
|
340
|
-
ctx.arc(x, y,
|
|
341
|
-
ctx.fill();
|
|
370
|
+
ctx.moveTo(x + radius, y);
|
|
371
|
+
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
342
372
|
}
|
|
343
373
|
}
|
|
374
|
+
ctx.fill();
|
|
344
375
|
}
|
|
345
376
|
/**
|
|
346
|
-
* Render grid pattern
|
|
377
|
+
* Render grid pattern - optimized with single path
|
|
347
378
|
*/
|
|
348
379
|
renderGridPattern(ctx, dimensions, color) {
|
|
349
380
|
ctx.strokeStyle = color;
|
|
350
381
|
ctx.lineWidth = 1;
|
|
351
382
|
const spacing = 30;
|
|
383
|
+
ctx.beginPath();
|
|
352
384
|
for (let x = 0; x <= dimensions.width; x += spacing) {
|
|
353
|
-
ctx.beginPath();
|
|
354
385
|
ctx.moveTo(x, 0);
|
|
355
386
|
ctx.lineTo(x, dimensions.height);
|
|
356
|
-
ctx.stroke();
|
|
357
387
|
}
|
|
358
388
|
for (let y = 0; y <= dimensions.height; y += spacing) {
|
|
359
|
-
ctx.beginPath();
|
|
360
389
|
ctx.moveTo(0, y);
|
|
361
390
|
ctx.lineTo(dimensions.width, y);
|
|
362
|
-
ctx.stroke();
|
|
363
391
|
}
|
|
392
|
+
ctx.stroke();
|
|
364
393
|
}
|
|
365
394
|
/**
|
|
366
|
-
* Render line pattern
|
|
395
|
+
* Render line pattern - optimized with single path
|
|
367
396
|
*/
|
|
368
397
|
renderLinePattern(ctx, dimensions, color) {
|
|
369
398
|
ctx.strokeStyle = color;
|
|
370
399
|
ctx.lineWidth = 2;
|
|
371
400
|
const spacing = 20;
|
|
401
|
+
ctx.beginPath();
|
|
372
402
|
for (let y = spacing; y < dimensions.height; y += spacing) {
|
|
373
|
-
ctx.beginPath();
|
|
374
403
|
ctx.moveTo(0, y);
|
|
375
404
|
ctx.lineTo(dimensions.width, y);
|
|
376
|
-
ctx.stroke();
|
|
377
405
|
}
|
|
406
|
+
ctx.stroke();
|
|
378
407
|
}
|
|
379
408
|
/**
|
|
380
|
-
* Render wave pattern
|
|
409
|
+
* Render wave pattern - optimized with pre-calculated values
|
|
381
410
|
*/
|
|
382
411
|
renderWavePattern(ctx, dimensions, color) {
|
|
383
412
|
ctx.strokeStyle = color;
|
|
@@ -385,15 +414,15 @@ var PatternRenderer = class {
|
|
|
385
414
|
const amplitude = 10;
|
|
386
415
|
const frequency = 0.1;
|
|
387
416
|
const spacing = 40;
|
|
417
|
+
const step = 5;
|
|
388
418
|
for (let y = spacing; y < dimensions.height; y += spacing) {
|
|
389
419
|
ctx.beginPath();
|
|
390
|
-
|
|
420
|
+
const startX = 0;
|
|
421
|
+
const startY = y + Math.sin(startX * frequency) * amplitude;
|
|
422
|
+
ctx.moveTo(startX, startY);
|
|
423
|
+
for (let x = step; x <= dimensions.width; x += step) {
|
|
391
424
|
const waveY = y + Math.sin(x * frequency) * amplitude;
|
|
392
|
-
|
|
393
|
-
ctx.moveTo(x, waveY);
|
|
394
|
-
} else {
|
|
395
|
-
ctx.lineTo(x, waveY);
|
|
396
|
-
}
|
|
425
|
+
ctx.lineTo(x, waveY);
|
|
397
426
|
}
|
|
398
427
|
ctx.stroke();
|
|
399
428
|
}
|
|
@@ -416,6 +445,9 @@ var PatternRenderer = class {
|
|
|
416
445
|
// src/infrastructure/services/DeviceConfigService.ts
|
|
417
446
|
var DeviceConfigService = class _DeviceConfigService {
|
|
418
447
|
constructor() {
|
|
448
|
+
this.phonesCache = null;
|
|
449
|
+
this.tabletsCache = null;
|
|
450
|
+
this.allDevicesCache = null;
|
|
419
451
|
this.devices = /* @__PURE__ */ new Map();
|
|
420
452
|
this.initializeDevices();
|
|
421
453
|
}
|
|
@@ -512,13 +544,22 @@ var DeviceConfigService = class _DeviceConfigService {
|
|
|
512
544
|
return this.devices.get(deviceType);
|
|
513
545
|
}
|
|
514
546
|
getAllDevices() {
|
|
515
|
-
|
|
547
|
+
if (!this.allDevicesCache) {
|
|
548
|
+
this.allDevicesCache = Array.from(this.devices.values());
|
|
549
|
+
}
|
|
550
|
+
return this.allDevicesCache;
|
|
516
551
|
}
|
|
517
552
|
getPhones() {
|
|
518
|
-
|
|
553
|
+
if (!this.phonesCache) {
|
|
554
|
+
this.phonesCache = this.getAllDevices().filter((d) => d.isPhone);
|
|
555
|
+
}
|
|
556
|
+
return this.phonesCache;
|
|
519
557
|
}
|
|
520
558
|
getTablets() {
|
|
521
|
-
|
|
559
|
+
if (!this.tabletsCache) {
|
|
560
|
+
this.tabletsCache = this.getAllDevices().filter((d) => d.isTablet);
|
|
561
|
+
}
|
|
562
|
+
return this.tabletsCache;
|
|
522
563
|
}
|
|
523
564
|
isPhone(deviceType) {
|
|
524
565
|
const config = this.getDeviceConfig(deviceType);
|
|
@@ -537,10 +578,20 @@ var DeviceConfigService = class _DeviceConfigService {
|
|
|
537
578
|
// src/infrastructure/renderers/ScreenshotRenderer.ts
|
|
538
579
|
var ScreenshotRenderer = class {
|
|
539
580
|
constructor() {
|
|
581
|
+
this.renderCache = /* @__PURE__ */ new Map();
|
|
582
|
+
this.MAX_CACHE_SIZE = 5;
|
|
540
583
|
this.canvasRenderer = new CanvasRenderer();
|
|
541
584
|
this.patternRenderer = new PatternRenderer();
|
|
542
585
|
this.deviceConfigService = DeviceConfigService.getInstance();
|
|
543
586
|
}
|
|
587
|
+
/**
|
|
588
|
+
* Clear render cache to free memory
|
|
589
|
+
*/
|
|
590
|
+
clearCache() {
|
|
591
|
+
this.renderCache.clear();
|
|
592
|
+
this.canvasRenderer.clearImageCache?.();
|
|
593
|
+
this.patternRenderer.clearCache?.();
|
|
594
|
+
}
|
|
544
595
|
/**
|
|
545
596
|
* Render device frame
|
|
546
597
|
*/
|
|
@@ -592,7 +643,7 @@ var ScreenshotRenderer = class {
|
|
|
592
643
|
ctx.fill();
|
|
593
644
|
}
|
|
594
645
|
/**
|
|
595
|
-
* Render background
|
|
646
|
+
* Render background - optimized
|
|
596
647
|
*/
|
|
597
648
|
renderBackground(ctx, design, dimensions) {
|
|
598
649
|
if (design.backgroundGradientEnd) {
|
|
@@ -802,10 +853,17 @@ var ScreenshotStudioService = class {
|
|
|
802
853
|
* Generate batch screenshots
|
|
803
854
|
*/
|
|
804
855
|
async generateBatchScreenshots(deviceTypes) {
|
|
805
|
-
const
|
|
806
|
-
|
|
807
|
-
)
|
|
808
|
-
|
|
856
|
+
const BATCH_SIZE = 3;
|
|
857
|
+
const results = [];
|
|
858
|
+
for (let i = 0; i < deviceTypes.length; i += BATCH_SIZE) {
|
|
859
|
+
const batch = deviceTypes.slice(i, i + BATCH_SIZE);
|
|
860
|
+
const batchPromises = batch.map(
|
|
861
|
+
(deviceType) => this.generateScreenshot({ deviceType })
|
|
862
|
+
);
|
|
863
|
+
const batchResults = await Promise.all(batchPromises);
|
|
864
|
+
results.push(...batchResults);
|
|
865
|
+
}
|
|
866
|
+
return results;
|
|
809
867
|
}
|
|
810
868
|
/**
|
|
811
869
|
* Generate designed screenshot
|
|
@@ -846,16 +904,18 @@ WHATS NEW:
|
|
|
846
904
|
${project.appMetadata.whatsNew}`;
|
|
847
905
|
folder.file("aso_metadata.txt", metadataText);
|
|
848
906
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
907
|
+
const screenshotPromises = project.screens.map(
|
|
908
|
+
(screen, index) => this.generateDesignedScreenshot(screen, project.design).then((screenshot) => ({ screenshot, index }))
|
|
909
|
+
);
|
|
910
|
+
const screenshots = await Promise.all(screenshotPromises);
|
|
911
|
+
screenshots.forEach(({ screenshot, index }) => {
|
|
852
912
|
const base64Data = screenshot.url.split(",")[1];
|
|
853
913
|
folder.file(
|
|
854
|
-
`screen_${
|
|
914
|
+
`screen_${index + 1}_${project.design.deviceType}.png`,
|
|
855
915
|
base64Data,
|
|
856
916
|
{ base64: true }
|
|
857
917
|
);
|
|
858
|
-
}
|
|
918
|
+
});
|
|
859
919
|
return await zip.generateAsync({ type: "blob" });
|
|
860
920
|
}
|
|
861
921
|
/**
|
|
@@ -939,10 +999,13 @@ ${project.appMetadata.whatsNew}`;
|
|
|
939
999
|
};
|
|
940
1000
|
}
|
|
941
1001
|
/**
|
|
942
|
-
* Generate unique ID
|
|
1002
|
+
* Generate unique ID - optimized version
|
|
943
1003
|
*/
|
|
944
1004
|
generateId() {
|
|
945
|
-
|
|
1005
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
1006
|
+
return crypto.randomUUID();
|
|
1007
|
+
}
|
|
1008
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 9)}`;
|
|
946
1009
|
}
|
|
947
1010
|
};
|
|
948
1011
|
|
|
@@ -1183,9 +1246,16 @@ function useScreenshotStudio(options = {}) {
|
|
|
1183
1246
|
...DEFAULT_SCREENSHOT_STUDIO_CONFIG,
|
|
1184
1247
|
...config
|
|
1185
1248
|
};
|
|
1186
|
-
const serviceRef = (0, import_react.useRef)(
|
|
1187
|
-
|
|
1188
|
-
|
|
1249
|
+
const serviceRef = (0, import_react.useRef)(null);
|
|
1250
|
+
if (!serviceRef.current) {
|
|
1251
|
+
serviceRef.current = new ScreenshotStudioService(fullConfig);
|
|
1252
|
+
}
|
|
1253
|
+
(0, import_react.useEffect)(() => {
|
|
1254
|
+
return () => {
|
|
1255
|
+
if (serviceRef.current) {
|
|
1256
|
+
}
|
|
1257
|
+
};
|
|
1258
|
+
}, []);
|
|
1189
1259
|
const [screens, setScreens] = (0, import_react.useState)(DEFAULT_SCREENS);
|
|
1190
1260
|
const [design, setDesign] = (0, import_react.useState)(DEFAULT_DESIGN);
|
|
1191
1261
|
const [currentLanguage, setCurrentLanguage] = (0, import_react.useState)(
|
|
@@ -1306,6 +1376,7 @@ function useScreenshotStudio(options = {}) {
|
|
|
1306
1376
|
);
|
|
1307
1377
|
const generateDesignedScreenshot = (0, import_react.useCallback)(
|
|
1308
1378
|
async (screenId) => {
|
|
1379
|
+
if (!serviceRef.current) return;
|
|
1309
1380
|
const screen = screens.find((s) => s.id === screenId);
|
|
1310
1381
|
if (!screen) return;
|
|
1311
1382
|
setIsProcessing(true);
|
|
@@ -1326,7 +1397,9 @@ function useScreenshotStudio(options = {}) {
|
|
|
1326
1397
|
);
|
|
1327
1398
|
const exportAllScreensAsZip = (0, import_react.useCallback)(
|
|
1328
1399
|
async (format = "png") => {
|
|
1400
|
+
if (!serviceRef.current) return;
|
|
1329
1401
|
setIsProcessing(true);
|
|
1402
|
+
let url = null;
|
|
1330
1403
|
try {
|
|
1331
1404
|
const project = {
|
|
1332
1405
|
id: projectId || "default-project",
|
|
@@ -1341,12 +1414,11 @@ function useScreenshotStudio(options = {}) {
|
|
|
1341
1414
|
updatedAt: /* @__PURE__ */ new Date()
|
|
1342
1415
|
};
|
|
1343
1416
|
const blob = await serviceRef.current.exportProjectAsZip(project);
|
|
1344
|
-
|
|
1417
|
+
url = URL.createObjectURL(blob);
|
|
1345
1418
|
const link = document.createElement("a");
|
|
1346
1419
|
link.href = url;
|
|
1347
1420
|
link.download = `Studio_${currentLanguage}_${Date.now()}.zip`;
|
|
1348
1421
|
link.click();
|
|
1349
|
-
URL.revokeObjectURL(url);
|
|
1350
1422
|
onShowToast(
|
|
1351
1423
|
"Success",
|
|
1352
1424
|
`Exported ${screens.length} screens in ZIP archive.`
|
|
@@ -1355,6 +1427,14 @@ function useScreenshotStudio(options = {}) {
|
|
|
1355
1427
|
onShowToast("Error", "ZIP export failed");
|
|
1356
1428
|
} finally {
|
|
1357
1429
|
setIsProcessing(false);
|
|
1430
|
+
if (url) {
|
|
1431
|
+
setTimeout(() => {
|
|
1432
|
+
try {
|
|
1433
|
+
URL.revokeObjectURL(url);
|
|
1434
|
+
} catch (e) {
|
|
1435
|
+
}
|
|
1436
|
+
}, 100);
|
|
1437
|
+
}
|
|
1358
1438
|
}
|
|
1359
1439
|
},
|
|
1360
1440
|
[
|
|
@@ -1440,15 +1520,34 @@ function useCanvasExport(options = {}) {
|
|
|
1440
1520
|
const [isExporting, setIsExporting] = (0, import_react2.useState)(false);
|
|
1441
1521
|
const [progress, setProgress] = (0, import_react2.useState)(0);
|
|
1442
1522
|
const [error, setError] = (0, import_react2.useState)(null);
|
|
1443
|
-
const
|
|
1523
|
+
const rendererRef = (0, import_react2.useRef)(null);
|
|
1524
|
+
const urlCleanupRef = (0, import_react2.useRef)([]);
|
|
1525
|
+
if (!rendererRef.current) {
|
|
1526
|
+
rendererRef.current = new CanvasRenderer(config);
|
|
1527
|
+
}
|
|
1528
|
+
(0, import_react2.useEffect)(() => {
|
|
1529
|
+
return () => {
|
|
1530
|
+
urlCleanupRef.current.forEach((url) => {
|
|
1531
|
+
try {
|
|
1532
|
+
URL.revokeObjectURL(url);
|
|
1533
|
+
} catch (e) {
|
|
1534
|
+
}
|
|
1535
|
+
});
|
|
1536
|
+
urlCleanupRef.current = [];
|
|
1537
|
+
};
|
|
1538
|
+
}, []);
|
|
1444
1539
|
const exportToBlob = (0, import_react2.useCallback)(
|
|
1445
1540
|
async (canvas, options2) => {
|
|
1541
|
+
if (!rendererRef.current) return null;
|
|
1446
1542
|
setIsExporting(true);
|
|
1447
1543
|
setProgress(0);
|
|
1448
1544
|
setError(null);
|
|
1449
1545
|
try {
|
|
1450
|
-
const result = await
|
|
1546
|
+
const result = await rendererRef.current.exportToBlob(canvas, options2);
|
|
1451
1547
|
setProgress(100);
|
|
1548
|
+
if (result?.url) {
|
|
1549
|
+
urlCleanupRef.current.push(result.url);
|
|
1550
|
+
}
|
|
1452
1551
|
return result;
|
|
1453
1552
|
} catch (err) {
|
|
1454
1553
|
const errorMessage = err instanceof Error ? err.message : "Export failed";
|
|
@@ -1458,19 +1557,20 @@ function useCanvasExport(options = {}) {
|
|
|
1458
1557
|
setIsExporting(false);
|
|
1459
1558
|
}
|
|
1460
1559
|
},
|
|
1461
|
-
[
|
|
1560
|
+
[]
|
|
1462
1561
|
);
|
|
1463
1562
|
const exportToDataURL = (0, import_react2.useCallback)(
|
|
1464
1563
|
(canvas, format, quality) => {
|
|
1564
|
+
if (!rendererRef.current) return null;
|
|
1465
1565
|
try {
|
|
1466
|
-
return
|
|
1566
|
+
return rendererRef.current.exportToDataURL(canvas, format, quality);
|
|
1467
1567
|
} catch (err) {
|
|
1468
1568
|
const errorMessage = err instanceof Error ? err.message : "Export failed";
|
|
1469
1569
|
setError(errorMessage);
|
|
1470
1570
|
return null;
|
|
1471
1571
|
}
|
|
1472
1572
|
},
|
|
1473
|
-
[
|
|
1573
|
+
[]
|
|
1474
1574
|
);
|
|
1475
1575
|
const downloadCanvas = (0, import_react2.useCallback)(
|
|
1476
1576
|
async (canvas, filename, options2) => {
|
|
@@ -1490,6 +1590,10 @@ function useCanvasExport(options = {}) {
|
|
|
1490
1590
|
setProgress(100);
|
|
1491
1591
|
setTimeout(() => {
|
|
1492
1592
|
URL.revokeObjectURL(result.url);
|
|
1593
|
+
const index = urlCleanupRef.current.indexOf(result.url);
|
|
1594
|
+
if (index > -1) {
|
|
1595
|
+
urlCleanupRef.current.splice(index, 1);
|
|
1596
|
+
}
|
|
1493
1597
|
}, 100);
|
|
1494
1598
|
} catch (err) {
|
|
1495
1599
|
const errorMessage = err instanceof Error ? err.message : "Download failed";
|
|
@@ -1502,15 +1606,16 @@ function useCanvasExport(options = {}) {
|
|
|
1502
1606
|
);
|
|
1503
1607
|
const cloneCanvas = (0, import_react2.useCallback)(
|
|
1504
1608
|
(canvas) => {
|
|
1609
|
+
if (!rendererRef.current) return null;
|
|
1505
1610
|
try {
|
|
1506
|
-
return
|
|
1611
|
+
return rendererRef.current.cloneCanvas(canvas);
|
|
1507
1612
|
} catch (err) {
|
|
1508
1613
|
const errorMessage = err instanceof Error ? err.message : "Clone failed";
|
|
1509
1614
|
setError(errorMessage);
|
|
1510
1615
|
return null;
|
|
1511
1616
|
}
|
|
1512
1617
|
},
|
|
1513
|
-
[
|
|
1618
|
+
[]
|
|
1514
1619
|
);
|
|
1515
1620
|
return {
|
|
1516
1621
|
isExporting,
|
|
@@ -1528,13 +1633,19 @@ var import_react3 = require("react");
|
|
|
1528
1633
|
function useReportExport() {
|
|
1529
1634
|
const [isExporting, setIsExporting] = (0, import_react3.useState)(false);
|
|
1530
1635
|
const [error, setError] = (0, import_react3.useState)(null);
|
|
1531
|
-
const
|
|
1636
|
+
const reportServiceRef = (0, import_react3.useRef)(null);
|
|
1637
|
+
if (!reportServiceRef.current) {
|
|
1638
|
+
reportServiceRef.current = ReportService.getInstance();
|
|
1639
|
+
}
|
|
1532
1640
|
const exportToPDF = (0, import_react3.useCallback)(
|
|
1533
1641
|
async (elementId, options) => {
|
|
1642
|
+
if (!reportServiceRef.current) {
|
|
1643
|
+
throw new Error("ReportService not initialized");
|
|
1644
|
+
}
|
|
1534
1645
|
setIsExporting(true);
|
|
1535
1646
|
setError(null);
|
|
1536
1647
|
try {
|
|
1537
|
-
await
|
|
1648
|
+
await reportServiceRef.current.exportToPDF(elementId, options);
|
|
1538
1649
|
} catch (err) {
|
|
1539
1650
|
const errorMessage = err instanceof Error ? err.message : "PDF export failed";
|
|
1540
1651
|
setError(errorMessage);
|
|
@@ -1543,14 +1654,17 @@ function useReportExport() {
|
|
|
1543
1654
|
setIsExporting(false);
|
|
1544
1655
|
}
|
|
1545
1656
|
},
|
|
1546
|
-
[
|
|
1657
|
+
[]
|
|
1547
1658
|
);
|
|
1548
1659
|
const exportCanvasToPDF = (0, import_react3.useCallback)(
|
|
1549
1660
|
async (canvas, options) => {
|
|
1661
|
+
if (!reportServiceRef.current) {
|
|
1662
|
+
throw new Error("ReportService not initialized");
|
|
1663
|
+
}
|
|
1550
1664
|
setIsExporting(true);
|
|
1551
1665
|
setError(null);
|
|
1552
1666
|
try {
|
|
1553
|
-
await
|
|
1667
|
+
await reportServiceRef.current.exportCanvasToPDF(canvas, options);
|
|
1554
1668
|
} catch (err) {
|
|
1555
1669
|
const errorMessage = err instanceof Error ? err.message : "Canvas PDF export failed";
|
|
1556
1670
|
setError(errorMessage);
|
|
@@ -1559,14 +1673,17 @@ function useReportExport() {
|
|
|
1559
1673
|
setIsExporting(false);
|
|
1560
1674
|
}
|
|
1561
1675
|
},
|
|
1562
|
-
[
|
|
1676
|
+
[]
|
|
1563
1677
|
);
|
|
1564
1678
|
const exportMultipleToPDF = (0, import_react3.useCallback)(
|
|
1565
1679
|
async (elementIds, options) => {
|
|
1680
|
+
if (!reportServiceRef.current) {
|
|
1681
|
+
throw new Error("ReportService not initialized");
|
|
1682
|
+
}
|
|
1566
1683
|
setIsExporting(true);
|
|
1567
1684
|
setError(null);
|
|
1568
1685
|
try {
|
|
1569
|
-
await
|
|
1686
|
+
await reportServiceRef.current.exportMultipleToPDF(elementIds, options);
|
|
1570
1687
|
} catch (err) {
|
|
1571
1688
|
const errorMessage = err instanceof Error ? err.message : "Multi-page PDF export failed";
|
|
1572
1689
|
setError(errorMessage);
|
|
@@ -1575,14 +1692,17 @@ function useReportExport() {
|
|
|
1575
1692
|
setIsExporting(false);
|
|
1576
1693
|
}
|
|
1577
1694
|
},
|
|
1578
|
-
[
|
|
1695
|
+
[]
|
|
1579
1696
|
);
|
|
1580
1697
|
const exportToExcel = (0, import_react3.useCallback)(
|
|
1581
1698
|
async (data, options) => {
|
|
1699
|
+
if (!reportServiceRef.current) {
|
|
1700
|
+
throw new Error("ReportService not initialized");
|
|
1701
|
+
}
|
|
1582
1702
|
setIsExporting(true);
|
|
1583
1703
|
setError(null);
|
|
1584
1704
|
try {
|
|
1585
|
-
await
|
|
1705
|
+
await reportServiceRef.current.exportToExcel(data, options);
|
|
1586
1706
|
} catch (err) {
|
|
1587
1707
|
const errorMessage = err instanceof Error ? err.message : "Excel export failed";
|
|
1588
1708
|
setError(errorMessage);
|
|
@@ -1591,19 +1711,22 @@ function useReportExport() {
|
|
|
1591
1711
|
setIsExporting(false);
|
|
1592
1712
|
}
|
|
1593
1713
|
},
|
|
1594
|
-
[
|
|
1714
|
+
[]
|
|
1595
1715
|
);
|
|
1596
1716
|
const exportToCSV = (0, import_react3.useCallback)(
|
|
1597
1717
|
(data, filename, delimiter) => {
|
|
1718
|
+
if (!reportServiceRef.current) {
|
|
1719
|
+
throw new Error("ReportService not initialized");
|
|
1720
|
+
}
|
|
1598
1721
|
try {
|
|
1599
|
-
|
|
1722
|
+
reportServiceRef.current.exportToCSV(data, filename, delimiter);
|
|
1600
1723
|
} catch (err) {
|
|
1601
1724
|
const errorMessage = err instanceof Error ? err.message : "CSV export failed";
|
|
1602
1725
|
setError(errorMessage);
|
|
1603
1726
|
throw err;
|
|
1604
1727
|
}
|
|
1605
1728
|
},
|
|
1606
|
-
[
|
|
1729
|
+
[]
|
|
1607
1730
|
);
|
|
1608
1731
|
return {
|
|
1609
1732
|
isExporting,
|
package/dist/index.mjs
CHANGED
|
@@ -2,17 +2,17 @@ import {
|
|
|
2
2
|
useCanvasExport,
|
|
3
3
|
useReportExport,
|
|
4
4
|
useScreenshotStudio
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LOVI2LTO.mjs";
|
|
6
6
|
import {
|
|
7
7
|
ReportService,
|
|
8
8
|
ScreenshotStudioService
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-DNKRSL6D.mjs";
|
|
10
10
|
import {
|
|
11
11
|
CanvasRenderer,
|
|
12
12
|
DeviceConfigService,
|
|
13
13
|
PatternRenderer,
|
|
14
14
|
ScreenshotRenderer
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-UOGTEMIJ.mjs";
|
|
16
16
|
import {
|
|
17
17
|
DEFAULT_SCREENSHOT_STUDIO_CONFIG
|
|
18
18
|
} from "./chunk-Z6IB4BQV.mjs";
|
|
@@ -2,6 +2,7 @@ import { a as CanvasConfig, c as CanvasDimensions, b as CanvasContextConfig, d a
|
|
|
2
2
|
|
|
3
3
|
declare class CanvasRenderer {
|
|
4
4
|
private config;
|
|
5
|
+
private imageCache;
|
|
5
6
|
constructor(config?: CanvasConfig);
|
|
6
7
|
/**
|
|
7
8
|
* Create a new canvas element
|
|
@@ -36,9 +37,17 @@ declare class CanvasRenderer {
|
|
|
36
37
|
*/
|
|
37
38
|
drawImage(ctx: CanvasRenderingContext2D, imageSource: string | HTMLImageElement, x: number, y: number, width?: number, height?: number): Promise<void>;
|
|
38
39
|
/**
|
|
39
|
-
* Load image from URL
|
|
40
|
+
* Load image from URL with caching
|
|
40
41
|
*/
|
|
41
42
|
private loadImage;
|
|
43
|
+
/**
|
|
44
|
+
* Clear image cache to free memory
|
|
45
|
+
*/
|
|
46
|
+
clearImageCache(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Get cache size for monitoring
|
|
49
|
+
*/
|
|
50
|
+
getCacheSize(): number;
|
|
42
51
|
/**
|
|
43
52
|
* Create gradient
|
|
44
53
|
*/
|
|
@@ -70,24 +79,30 @@ declare class CanvasRenderer {
|
|
|
70
79
|
*/
|
|
71
80
|
|
|
72
81
|
declare class PatternRenderer {
|
|
82
|
+
private patternCanvasCache;
|
|
83
|
+
private readonly MAX_CACHE_SIZE;
|
|
73
84
|
/**
|
|
74
85
|
* Render pattern on canvas
|
|
75
86
|
*/
|
|
76
87
|
renderPattern(ctx: CanvasRenderingContext2D, pattern: BackgroundPattern, dimensions: CanvasDimensions, color?: string): void;
|
|
77
88
|
/**
|
|
78
|
-
*
|
|
89
|
+
* Clear pattern cache to free memory
|
|
90
|
+
*/
|
|
91
|
+
clearCache(): void;
|
|
92
|
+
/**
|
|
93
|
+
* Render dot pattern - optimized with batch rendering
|
|
79
94
|
*/
|
|
80
95
|
private renderDotPattern;
|
|
81
96
|
/**
|
|
82
|
-
* Render grid pattern
|
|
97
|
+
* Render grid pattern - optimized with single path
|
|
83
98
|
*/
|
|
84
99
|
private renderGridPattern;
|
|
85
100
|
/**
|
|
86
|
-
* Render line pattern
|
|
101
|
+
* Render line pattern - optimized with single path
|
|
87
102
|
*/
|
|
88
103
|
private renderLinePattern;
|
|
89
104
|
/**
|
|
90
|
-
* Render wave pattern
|
|
105
|
+
* Render wave pattern - optimized with pre-calculated values
|
|
91
106
|
*/
|
|
92
107
|
private renderWavePattern;
|
|
93
108
|
/**
|
|
@@ -105,7 +120,13 @@ declare class ScreenshotRenderer {
|
|
|
105
120
|
private canvasRenderer;
|
|
106
121
|
private patternRenderer;
|
|
107
122
|
private deviceConfigService;
|
|
123
|
+
private renderCache;
|
|
124
|
+
private readonly MAX_CACHE_SIZE;
|
|
108
125
|
constructor();
|
|
126
|
+
/**
|
|
127
|
+
* Clear render cache to free memory
|
|
128
|
+
*/
|
|
129
|
+
clearCache(): void;
|
|
109
130
|
/**
|
|
110
131
|
* Render device frame
|
|
111
132
|
*/
|
|
@@ -123,7 +144,7 @@ declare class ScreenshotRenderer {
|
|
|
123
144
|
*/
|
|
124
145
|
private renderNotch;
|
|
125
146
|
/**
|
|
126
|
-
* Render background
|
|
147
|
+
* Render background - optimized
|
|
127
148
|
*/
|
|
128
149
|
renderBackground(ctx: CanvasRenderingContext2D, design: ScreenshotDesign, dimensions: CanvasDimensions): void;
|
|
129
150
|
/**
|
|
@@ -159,6 +180,9 @@ declare class ScreenshotRenderer {
|
|
|
159
180
|
declare class DeviceConfigService {
|
|
160
181
|
private static instance;
|
|
161
182
|
private devices;
|
|
183
|
+
private phonesCache;
|
|
184
|
+
private tabletsCache;
|
|
185
|
+
private allDevicesCache;
|
|
162
186
|
private constructor();
|
|
163
187
|
static getInstance(): DeviceConfigService;
|
|
164
188
|
private initializeDevices;
|