@umituz/web-canvas 1.2.1 → 1.2.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/src/index.ts CHANGED
@@ -1,11 +1,5 @@
1
- /**
2
- * @umituz/web-canvas
3
- * Comprehensive canvas and screenshot generation library
4
- */
5
1
 
6
- // Domain exports
7
2
  export type {
8
- // Canvas types
9
3
  CanvasFormat,
10
4
  CanvasQuality,
11
5
  CanvasDimensions,
@@ -17,7 +11,6 @@ export type {
17
11
  CanvasResult,
18
12
  CanvasOperation,
19
13
 
20
- // Screenshot types
21
14
  DeviceType,
22
15
  FrameStyle,
23
16
  LayoutStyle,
@@ -34,7 +27,6 @@ export type {
34
27
  AppStoreMetadata,
35
28
  ScreenshotProject,
36
29
 
37
- // Report types
38
30
  ReportItemType,
39
31
  ReportItem,
40
32
  ReportSummary,
@@ -45,7 +37,6 @@ export type {
45
37
  CSVExportOptions,
46
38
  ReportExportResult,
47
39
 
48
- // Freehand types
49
40
  FreehandPoint,
50
41
  FreehandStroke,
51
42
  UseFreehandCanvasOptions,
@@ -72,7 +63,6 @@ export {
72
63
  DEFAULT_SCREENSHOT_STUDIO_CONFIG,
73
64
  } from './domain/config/ScreenshotStudioConfig';
74
65
 
75
- // Infrastructure exports
76
66
  export { CanvasRenderer } from './infrastructure/renderers/CanvasRenderer';
77
67
  export { PatternRenderer } from './infrastructure/renderers/PatternRenderer';
78
68
  export {
@@ -82,13 +72,11 @@ export {
82
72
  DeviceConfigService,
83
73
  } from './infrastructure/services/DeviceConfigService';
84
74
 
85
- // Application exports
86
75
  export {
87
76
  ScreenshotStudioService,
88
77
  } from './application/services/ScreenshotStudioService';
89
78
  export { ReportService } from './application/services/ReportService';
90
79
 
91
- // Presentation exports
92
80
  export {
93
81
  useScreenshotStudio,
94
82
  type UseScreenshotStudioOptions,
@@ -1,7 +1,3 @@
1
- /**
2
- * Infrastructure Layer Index
3
- * Export all infrastructure services and renderers
4
- */
5
1
 
6
2
  export * from './services/DeviceConfigService';
7
3
  export * from './renderers/CanvasRenderer';
@@ -1,7 +1,3 @@
1
- /**
2
- * Canvas Renderer
3
- * Core canvas rendering operations
4
- */
5
1
 
6
2
  import type {
7
3
  CanvasDimensions,
@@ -20,9 +16,6 @@ export class CanvasRenderer {
20
16
  this.imageCache = new Map();
21
17
  }
22
18
 
23
- /**
24
- * Create a new canvas element
25
- */
26
19
  public createCanvas(dimensions: CanvasDimensions): HTMLCanvasElement {
27
20
  const canvas = document.createElement('canvas');
28
21
  canvas.width = dimensions.width;
@@ -30,18 +23,12 @@ export class CanvasRenderer {
30
23
  return canvas;
31
24
  }
32
25
 
33
- /**
34
- * Get canvas 2D context
35
- */
36
26
  public getContext(
37
27
  canvas: HTMLCanvasElement
38
28
  ): CanvasRenderingContext2D | null {
39
29
  return canvas.getContext('2d');
40
30
  }
41
31
 
42
- /**
43
- * Apply context configuration
44
- */
45
32
  public configureContext(
46
33
  ctx: CanvasRenderingContext2D,
47
34
  config: CanvasContextConfig
@@ -75,9 +62,6 @@ export class CanvasRenderer {
75
62
  }
76
63
  }
77
64
 
78
- /**
79
- * Clear canvas
80
- */
81
65
  public clearCanvas(
82
66
  ctx: CanvasRenderingContext2D,
83
67
  dimensions: CanvasDimensions
@@ -85,9 +69,6 @@ export class CanvasRenderer {
85
69
  ctx.clearRect(0, 0, dimensions.width, dimensions.height);
86
70
  }
87
71
 
88
- /**
89
- * Fill rectangle
90
- */
91
72
  public fillRect(
92
73
  ctx: CanvasRenderingContext2D,
93
74
  x: number,
@@ -102,9 +83,6 @@ export class CanvasRenderer {
102
83
  ctx.fillRect(x, y, width, height);
103
84
  }
104
85
 
105
- /**
106
- * Stroke rectangle
107
- */
108
86
  public strokeRect(
109
87
  ctx: CanvasRenderingContext2D,
110
88
  x: number,
@@ -123,9 +101,6 @@ export class CanvasRenderer {
123
101
  ctx.strokeRect(x, y, width, height);
124
102
  }
125
103
 
126
- /**
127
- * Draw text
128
- */
129
104
  public drawText(
130
105
  ctx: CanvasRenderingContext2D,
131
106
  text: string,
@@ -139,9 +114,6 @@ export class CanvasRenderer {
139
114
  ctx.fillText(text, x, y);
140
115
  }
141
116
 
142
- /**
143
- * Draw image
144
- */
145
117
  public async drawImage(
146
118
  ctx: CanvasRenderingContext2D,
147
119
  imageSource: string | HTMLImageElement,
@@ -165,11 +137,7 @@ export class CanvasRenderer {
165
137
  }
166
138
  }
167
139
 
168
- /**
169
- * Load image from URL with caching
170
- */
171
140
  private loadImage(url: string): Promise<HTMLImageElement> {
172
- // Check cache first
173
141
  if (this.imageCache.has(url)) {
174
142
  return Promise.resolve(this.imageCache.get(url)!);
175
143
  }
@@ -180,7 +148,6 @@ export class CanvasRenderer {
180
148
  img.crossOrigin = 'anonymous';
181
149
  }
182
150
  img.onload = () => {
183
- // Cache the loaded image
184
151
  this.imageCache.set(url, img);
185
152
  resolve(img);
186
153
  };
@@ -189,23 +156,14 @@ export class CanvasRenderer {
189
156
  });
190
157
  }
191
158
 
192
- /**
193
- * Clear image cache to free memory
194
- */
195
159
  public clearImageCache(): void {
196
160
  this.imageCache.clear();
197
161
  }
198
162
 
199
- /**
200
- * Get cache size for monitoring
201
- */
202
163
  public getCacheSize(): number {
203
164
  return this.imageCache.size;
204
165
  }
205
166
 
206
- /**
207
- * Create gradient
208
- */
209
167
  public createLinearGradient(
210
168
  ctx: CanvasRenderingContext2D,
211
169
  x1: number,
@@ -221,9 +179,6 @@ export class CanvasRenderer {
221
179
  return gradient;
222
180
  }
223
181
 
224
- /**
225
- * Export canvas to blob
226
- */
227
182
  public async exportToBlob(
228
183
  canvas: HTMLCanvasElement,
229
184
  options: CanvasExportOptions = {}
@@ -232,7 +187,6 @@ export class CanvasRenderer {
232
187
  const quality = options.quality || this.config.defaultQuality;
233
188
  const scale = options.scale || this.config.scale;
234
189
 
235
- // Scale canvas if needed
236
190
  const scaledCanvas = (scale || 1) !== 1 ? this.scaleCanvas(canvas, scale || 1) : canvas;
237
191
 
238
192
  const mimeType = `image/${format || 'png'}`;
@@ -262,9 +216,6 @@ export class CanvasRenderer {
262
216
  };
263
217
  }
264
218
 
265
- /**
266
- * Export canvas to data URL
267
- */
268
219
  public exportToDataURL(
269
220
  canvas: HTMLCanvasElement,
270
221
  format?: string,
@@ -275,9 +226,6 @@ export class CanvasRenderer {
275
226
  return canvas.toDataURL(mimeType, q);
276
227
  }
277
228
 
278
- /**
279
- * Scale canvas
280
- */
281
229
  private scaleCanvas(
282
230
  canvas: HTMLCanvasElement,
283
231
  scale: number
@@ -296,9 +244,6 @@ export class CanvasRenderer {
296
244
  return scaled;
297
245
  }
298
246
 
299
- /**
300
- * Clone canvas
301
- */
302
247
  public cloneCanvas(canvas: HTMLCanvasElement): HTMLCanvasElement {
303
248
  const clone = document.createElement('canvas');
304
249
  clone.width = canvas.width;
@@ -1,7 +1,3 @@
1
- /**
2
- * Pattern Renderer
3
- * Background pattern rendering operations
4
- */
5
1
 
6
2
  import type { CanvasDimensions } from '../../domain/types/canvas.types';
7
3
  import type { BackgroundPattern } from '../../domain/types/screenshot.types';
@@ -10,9 +6,6 @@ export class PatternRenderer {
10
6
  private patternCanvasCache: Map<string, HTMLCanvasElement> = new Map();
11
7
  private readonly MAX_CACHE_SIZE = 10;
12
8
 
13
- /**
14
- * Render pattern on canvas
15
- */
16
9
  public renderPattern(
17
10
  ctx: CanvasRenderingContext2D,
18
11
  pattern: BackgroundPattern,
@@ -45,16 +38,10 @@ export class PatternRenderer {
45
38
  ctx.restore();
46
39
  }
47
40
 
48
- /**
49
- * Clear pattern cache to free memory
50
- */
51
41
  public clearCache(): void {
52
42
  this.patternCanvasCache.clear();
53
43
  }
54
44
 
55
- /**
56
- * Render dot pattern - optimized with batch rendering
57
- */
58
45
  private renderDotPattern(
59
46
  ctx: CanvasRenderingContext2D,
60
47
  dimensions: CanvasDimensions,
@@ -64,7 +51,6 @@ export class PatternRenderer {
64
51
  const spacing = 30;
65
52
  const radius = 2;
66
53
 
67
- // Batch rendering for better performance
68
54
  ctx.beginPath();
69
55
  for (let x = spacing; x < dimensions.width; x += spacing) {
70
56
  for (let y = spacing; y < dimensions.height; y += spacing) {
@@ -75,9 +61,6 @@ export class PatternRenderer {
75
61
  ctx.fill();
76
62
  }
77
63
 
78
- /**
79
- * Render grid pattern - optimized with single path
80
- */
81
64
  private renderGridPattern(
82
65
  ctx: CanvasRenderingContext2D,
83
66
  dimensions: CanvasDimensions,
@@ -89,13 +72,11 @@ export class PatternRenderer {
89
72
 
90
73
  ctx.beginPath();
91
74
 
92
- // Vertical lines
93
75
  for (let x = 0; x <= dimensions.width; x += spacing) {
94
76
  ctx.moveTo(x, 0);
95
77
  ctx.lineTo(x, dimensions.height);
96
78
  }
97
79
 
98
- // Horizontal lines
99
80
  for (let y = 0; y <= dimensions.height; y += spacing) {
100
81
  ctx.moveTo(0, y);
101
82
  ctx.lineTo(dimensions.width, y);
@@ -104,9 +85,6 @@ export class PatternRenderer {
104
85
  ctx.stroke();
105
86
  }
106
87
 
107
- /**
108
- * Render line pattern - optimized with single path
109
- */
110
88
  private renderLinePattern(
111
89
  ctx: CanvasRenderingContext2D,
112
90
  dimensions: CanvasDimensions,
@@ -118,7 +96,6 @@ export class PatternRenderer {
118
96
 
119
97
  ctx.beginPath();
120
98
 
121
- // Draw all lines in a single path
122
99
  for (let y = spacing; y < dimensions.height; y += spacing) {
123
100
  ctx.moveTo(0, y);
124
101
  ctx.lineTo(dimensions.width, y);
@@ -127,9 +104,6 @@ export class PatternRenderer {
127
104
  ctx.stroke();
128
105
  }
129
106
 
130
- /**
131
- * Render wave pattern - optimized with pre-calculated values
132
- */
133
107
  private renderWavePattern(
134
108
  ctx: CanvasRenderingContext2D,
135
109
  dimensions: CanvasDimensions,
@@ -145,7 +119,6 @@ export class PatternRenderer {
145
119
  for (let y = spacing; y < dimensions.height; y += spacing) {
146
120
  ctx.beginPath();
147
121
 
148
- // Pre-calculate wave points and use more efficient path drawing
149
122
  const startX = 0;
150
123
  const startY = y + Math.sin(startX * frequency) * amplitude;
151
124
  ctx.moveTo(startX, startY);
@@ -159,9 +132,6 @@ export class PatternRenderer {
159
132
  }
160
133
  }
161
134
 
162
- /**
163
- * Render checker pattern
164
- */
165
135
  private renderCheckerPattern(
166
136
  ctx: CanvasRenderingContext2D,
167
137
  dimensions: CanvasDimensions,
@@ -1,7 +1,3 @@
1
- /**
2
- * Screenshot Renderer
3
- * Device frame and screenshot rendering operations
4
- */
5
1
 
6
2
  import type {
7
3
  CanvasDimensions,
@@ -31,18 +27,12 @@ export class ScreenshotRenderer {
31
27
  this.deviceConfigService = DeviceConfigService.getInstance();
32
28
  }
33
29
 
34
- /**
35
- * Clear render cache to free memory
36
- */
37
30
  public clearCache(): void {
38
31
  this.renderCache.clear();
39
32
  this.canvasRenderer.clearImageCache?.();
40
33
  this.patternRenderer.clearCache?.();
41
34
  }
42
35
 
43
- /**
44
- * Render device frame
45
- */
46
36
  public renderDeviceFrame(
47
37
  ctx: CanvasRenderingContext2D,
48
38
  dimensions: CanvasDimensions,
@@ -72,9 +62,6 @@ export class ScreenshotRenderer {
72
62
  }
73
63
  }
74
64
 
75
- /**
76
- * Render realistic device frame
77
- */
78
65
  private renderRealisticFrame(
79
66
  ctx: CanvasRenderingContext2D,
80
67
  dimensions: CanvasDimensions,
@@ -91,9 +78,6 @@ export class ScreenshotRenderer {
91
78
  );
92
79
  }
93
80
 
94
- /**
95
- * Render minimal device frame
96
- */
97
81
  private renderMinimalFrame(
98
82
  ctx: CanvasRenderingContext2D,
99
83
  dimensions: CanvasDimensions,
@@ -105,9 +89,6 @@ export class ScreenshotRenderer {
105
89
  ctx.strokeRect(0, 0, dimensions.width, dimensions.height);
106
90
  }
107
91
 
108
- /**
109
- * Render device notch
110
- */
111
92
  private renderNotch(
112
93
  ctx: CanvasRenderingContext2D,
113
94
  dimensions: CanvasDimensions
@@ -118,15 +99,11 @@ export class ScreenshotRenderer {
118
99
  ctx.fill();
119
100
  }
120
101
 
121
- /**
122
- * Render background - optimized
123
- */
124
102
  public renderBackground(
125
103
  ctx: CanvasRenderingContext2D,
126
104
  design: ScreenshotDesign,
127
105
  dimensions: CanvasDimensions
128
106
  ): void {
129
- // Fill background
130
107
  if (design.backgroundGradientEnd) {
131
108
  const gradient = this.canvasRenderer.createLinearGradient(
132
109
  ctx,
@@ -146,7 +123,6 @@ export class ScreenshotRenderer {
146
123
 
147
124
  ctx.fillRect(0, 0, dimensions.width, dimensions.height);
148
125
 
149
- // Render pattern if specified (and not none)
150
126
  if (design.backgroundPattern && design.backgroundPattern !== 'none') {
151
127
  this.patternRenderer.renderPattern(
152
128
  ctx,
@@ -156,9 +132,6 @@ export class ScreenshotRenderer {
156
132
  }
157
133
  }
158
134
 
159
- /**
160
- * Render text content
161
- */
162
135
  public renderTextContent(
163
136
  ctx: CanvasRenderingContext2D,
164
137
  content: ScreenshotContent,
@@ -200,9 +173,6 @@ export class ScreenshotRenderer {
200
173
  );
201
174
  }
202
175
 
203
- /**
204
- * Format text case
205
- */
206
176
  private formatText(
207
177
  text: string,
208
178
  textCase: 'uppercase' | 'lowercase' | 'none' = 'none'
@@ -217,9 +187,6 @@ export class ScreenshotRenderer {
217
187
  }
218
188
  }
219
189
 
220
- /**
221
- * Render screenshot image
222
- */
223
190
  public async renderScreenshotImage(
224
191
  ctx: CanvasRenderingContext2D,
225
192
  imageUrl: string,
@@ -233,7 +200,6 @@ export class ScreenshotRenderer {
233
200
  );
234
201
  if (!deviceConfig) return;
235
202
 
236
- // Calculate screenshot area (inside device frame)
237
203
  const frameThickness = deviceConfig.frameThickness || 8;
238
204
  const padding = 60 + frameThickness;
239
205
  const screenshotWidth = dimensions.width - padding * 2;
@@ -253,9 +219,6 @@ export class ScreenshotRenderer {
253
219
  }
254
220
  }
255
221
 
256
- /**
257
- * Render complete screenshot
258
- */
259
222
  public async renderDesignedScreenshot(
260
223
  content: ScreenshotContent,
261
224
  design: ScreenshotDesign
@@ -274,18 +237,14 @@ export class ScreenshotRenderer {
274
237
  throw new Error('Failed to get canvas context');
275
238
  }
276
239
 
277
- // Render background
278
240
  this.renderBackground(ctx, design, dimensions);
279
241
 
280
- // Render screenshot image if available
281
242
  if (content.screenshot) {
282
243
  await this.renderScreenshotImage(ctx, content.screenshot, design, dimensions);
283
244
  }
284
245
 
285
- // Render text content
286
246
  this.renderTextContent(ctx, content, design, dimensions);
287
247
 
288
- // Render device frame
289
248
  this.renderDeviceFrame(
290
249
  ctx,
291
250
  dimensions,
@@ -297,18 +256,13 @@ export class ScreenshotRenderer {
297
256
  return canvas;
298
257
  }
299
258
 
300
- /**
301
- * Render placeholder content
302
- */
303
259
  public renderPlaceholderContent(
304
260
  ctx: CanvasRenderingContext2D,
305
261
  dimensions: CanvasDimensions,
306
262
  deviceType: DeviceType
307
263
  ): void {
308
- // Background
309
264
  this.canvasRenderer.fillRect(ctx, 0, 0, dimensions.width, dimensions.height, '#ffffff');
310
265
 
311
- // Header area
312
266
  this.canvasRenderer.fillRect(ctx, 0, 0, dimensions.width, 60, '#f3f4f6');
313
267
  this.canvasRenderer.fillRect(
314
268
  ctx,
@@ -319,7 +273,6 @@ export class ScreenshotRenderer {
319
273
  '#f3f4f6'
320
274
  );
321
275
 
322
- // Placeholder content blocks
323
276
  ctx.fillStyle = '#e5e7eb';
324
277
  for (let i = 0; i < 5; i++) {
325
278
  const y = 80 + i * 120;
@@ -329,7 +282,6 @@ export class ScreenshotRenderer {
329
282
  }
330
283
  }
331
284
 
332
- // Device frame
333
285
  const deviceConfig = this.deviceConfigService.getDeviceConfig(deviceType);
334
286
  if (deviceConfig) {
335
287
  this.renderDeviceFrame(
@@ -1,14 +1,7 @@
1
- /**
2
- * Device Configuration Service
3
- * Manages device configurations and dimensions
4
- */
5
1
 
6
2
  import { DeviceType, DeviceConfig } from '../../domain/types/screenshot.types';
7
3
  import { DEVICE_DIMENSIONS } from '../../domain/config/DeviceConfig';
8
4
 
9
- /**
10
- * Device configuration service
11
- */
12
5
  export class DeviceConfigService {
13
6
  private static instance: DeviceConfigService;
14
7
  private devices: Map<DeviceType, DeviceConfig>;
@@ -119,7 +112,6 @@ export class DeviceConfigService {
119
112
  }
120
113
 
121
114
  public getAllDevices(): DeviceConfig[] {
122
- // Use cached value if available
123
115
  if (!this.allDevicesCache) {
124
116
  this.allDevicesCache = Array.from(this.devices.values());
125
117
  }
@@ -127,7 +119,6 @@ export class DeviceConfigService {
127
119
  }
128
120
 
129
121
  public getPhones(): DeviceConfig[] {
130
- // Use cached value if available
131
122
  if (!this.phonesCache) {
132
123
  this.phonesCache = this.getAllDevices().filter((d) => d.isPhone);
133
124
  }
@@ -135,7 +126,6 @@ export class DeviceConfigService {
135
126
  }
136
127
 
137
128
  public getTablets(): DeviceConfig[] {
138
- // Use cached value if available
139
129
  if (!this.tabletsCache) {
140
130
  this.tabletsCache = this.getAllDevices().filter((d) => d.isTablet);
141
131
  }
@@ -1,15 +1,6 @@
1
- /**
2
- * Minimal XLSX writer — replaces the unmaintained npm `xlsx` (SheetJS)
3
- * dependency, whose registry build carries open prototype-pollution and
4
- * ReDoS advisories with no registry fix. The report export only ever
5
- * writes flat tables (header row + primitive cells), so a hand-rolled
6
- * SpreadsheetML package on top of jszip covers the need: inline-string
7
- * and numeric cells, multiple sheets, Excel/LibreOffice/Sheets-compatible.
8
- */
9
1
 
10
2
  export interface XlsxSheet {
11
3
  name: string;
12
- /** Rows of primitive cells; the first row is the header. */
13
4
  rows: (string | number | boolean | null | undefined)[][];
14
5
  }
15
6
 
@@ -24,7 +15,6 @@ function escapeXml(value: string): string {
24
15
  .replace(/'/g, '&apos;');
25
16
  }
26
17
 
27
- /** Excel sheet names: max 31 chars, no : \ / ? * [ ]. */
28
18
  function safeSheetName(name: string, index: number): string {
29
19
  const cleaned = name.replace(/[:\\/?*[\]]/g, ' ').slice(0, 31).trim();
30
20
  return cleaned.length > 0 ? cleaned : `Sheet${index + 1}`;
@@ -66,7 +56,6 @@ function sheetXml(rows: XlsxSheet['rows']): string {
66
56
  return `${XML_HEADER}<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>${body}</sheetData></worksheet>`;
67
57
  }
68
58
 
69
- /** Assembles the sheets into a .xlsx zip blob using jszip. */
70
59
  export async function writeXlsxBlob(sheets: XlsxSheet[], JSZip: typeof import('jszip')): Promise<Blob> {
71
60
  const zip = new JSZip();
72
61
  const safeNames = sheets.map((sheet, index) => safeSheetName(sheet.name, index));
@@ -1,7 +1,3 @@
1
- /**
2
- * useCanvasExport Hook
3
- * React hook for canvas export operations
4
- */
5
1
 
6
2
  import { useState, useCallback, useRef, useEffect } from 'react';
7
3
  import type { CanvasExportOptions, CanvasResult } from '../../domain/types/canvas.types';
@@ -9,16 +5,10 @@ import type { CanvasConfig } from '../../domain/config/CanvasConfig';
9
5
  import { DEFAULT_CANVAS_CONFIG } from '../../domain/config/CanvasConfig';
10
6
  import { CanvasRenderer } from '../../infrastructure/renderers/CanvasRenderer';
11
7
 
12
- /**
13
- * useCanvasExport hook options
14
- */
15
8
  export interface UseCanvasExportOptions {
16
9
  config?: CanvasConfig;
17
10
  }
18
11
 
19
- /**
20
- * useCanvasExport hook return
21
- */
22
12
  export interface UseCanvasExportReturn {
23
13
  isExporting: boolean;
24
14
  progress: number;
@@ -40,9 +30,6 @@ export interface UseCanvasExportReturn {
40
30
  cloneCanvas: (canvas: HTMLCanvasElement) => HTMLCanvasElement | null;
41
31
  }
42
32
 
43
- /**
44
- * useCanvasExport hook
45
- */
46
33
  export function useCanvasExport(
47
34
  options: UseCanvasExportOptions = {}
48
35
  ): UseCanvasExportReturn {
@@ -52,26 +39,22 @@ export function useCanvasExport(
52
39
  const [progress, setProgress] = useState(0);
53
40
  const [error, setError] = useState<string | null>(null);
54
41
 
55
- // Use ref to prevent recreating renderer on every render
56
42
  const rendererRef = useRef<CanvasRenderer | null>(null);
57
43
  const urlCleanupRef = useRef<string[]>([]);
58
44
 
59
- // Initialize renderer once
60
45
  if (!rendererRef.current) {
61
46
  rendererRef.current = new CanvasRenderer(config);
62
47
  }
63
48
 
64
- // Cleanup URLs on unmount
65
49
  useEffect(() => {
66
50
  return () => {
67
- // Clean up any remaining URLs
68
51
  urlCleanupRef.current.forEach(url => {
69
52
  try {
70
53
  URL.revokeObjectURL(url);
71
- } catch (e) {
72
- // Ignore errors during cleanup
54
+ } catch {
73
55
  }
74
56
  });
57
+
75
58
  urlCleanupRef.current = [];
76
59
  };
77
60
  }, []);
@@ -91,7 +74,6 @@ export function useCanvasExport(
91
74
  const result = await rendererRef.current.exportToBlob(canvas, options);
92
75
  setProgress(100);
93
76
 
94
- // Track URL for cleanup
95
77
  if (result?.url) {
96
78
  urlCleanupRef.current.push(result.url);
97
79
  }
@@ -154,10 +136,8 @@ export function useCanvasExport(
154
136
 
155
137
  setProgress(100);
156
138
 
157
- // Schedule URL cleanup after a delay
158
139
  setTimeout(() => {
159
140
  URL.revokeObjectURL(result.url);
160
- // Remove from cleanup tracking
161
141
  const index = urlCleanupRef.current.indexOf(result.url);
162
142
  if (index > -1) {
163
143
  urlCleanupRef.current.splice(index, 1);
@@ -1,7 +1,3 @@
1
- /**
2
- * useReportExport Hook
3
- * React hook for report export operations
4
- */
5
1
 
6
2
  import { useState, useCallback, useRef, useEffect } from 'react';
7
3
  import type { ReportData } from '../../domain/types/report.types';
@@ -11,9 +7,6 @@ import type {
11
7
  } from '../../domain/types/report.types';
12
8
  import { ReportService } from '../../application/services/ReportService';
13
9
 
14
- /**
15
- * useReportExport hook return
16
- */
17
10
  export interface UseReportExportReturn {
18
11
  isExporting: boolean;
19
12
  error: string | null;
@@ -40,17 +33,12 @@ export interface UseReportExportReturn {
40
33
  ) => void;
41
34
  }
42
35
 
43
- /**
44
- * useReportExport hook
45
- */
46
36
  export function useReportExport(): UseReportExportReturn {
47
37
  const [isExporting, setIsExporting] = useState(false);
48
38
  const [error, setError] = useState<string | null>(null);
49
39
 
50
- // Use ref to prevent recreating service instance on every render
51
40
  const reportServiceRef = useRef<ReportService | null>(null);
52
41
 
53
- // Initialize service once
54
42
  if (!reportServiceRef.current) {
55
43
  reportServiceRef.current = ReportService.getInstance();
56
44
  }