@umituz/web-canvas 1.2.0 → 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.
Files changed (35) hide show
  1. package/dist/{chunk-YRC4YPMO.mjs → chunk-7YLVULSN.mjs} +91 -4
  2. package/dist/domain/index.d.mts +2 -22
  3. package/dist/domain/index.d.ts +2 -22
  4. package/dist/index.js +91 -4
  5. package/dist/index.mjs +1 -1
  6. package/dist/presentation/index.d.mts +0 -26
  7. package/dist/presentation/index.d.ts +0 -26
  8. package/dist/presentation/index.js +91 -4
  9. package/dist/presentation/index.mjs +1 -1
  10. package/package.json +1 -1
  11. package/src/application/index.ts +0 -4
  12. package/src/application/services/ReportService.ts +0 -22
  13. package/src/application/services/ScreenshotStudioService.ts +1 -35
  14. package/src/domain/config/CanvasConfig.ts +0 -16
  15. package/src/domain/config/DeviceConfig.ts +0 -18
  16. package/src/domain/config/ScreenshotStudioConfig.ts +0 -29
  17. package/src/domain/config/index.ts +0 -4
  18. package/src/domain/index.ts +0 -4
  19. package/src/domain/types/canvas.types.ts +1 -35
  20. package/src/domain/types/freehand.types.ts +2 -23
  21. package/src/domain/types/index.ts +0 -4
  22. package/src/domain/types/report.types.ts +0 -31
  23. package/src/domain/types/screenshot.types.ts +1 -65
  24. package/src/index.ts +0 -12
  25. package/src/infrastructure/index.ts +0 -4
  26. package/src/infrastructure/renderers/CanvasRenderer.ts +0 -55
  27. package/src/infrastructure/renderers/PatternRenderer.ts +0 -30
  28. package/src/infrastructure/renderers/ScreenshotRenderer.ts +0 -48
  29. package/src/infrastructure/services/DeviceConfigService.ts +0 -10
  30. package/src/infrastructure/services/minimalXlsx.ts +0 -11
  31. package/src/presentation/hooks/useCanvasExport.ts +2 -22
  32. package/src/presentation/hooks/useFreehandCanvas.ts +102 -40
  33. package/src/presentation/hooks/useReportExport.ts +0 -12
  34. package/src/presentation/hooks/useScreenshotStudio.ts +1 -45
  35. package/src/presentation/index.ts +0 -4
@@ -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,8 +1,3 @@
1
- /**
2
- * useFreehandCanvas Hook
3
- * React hook for pointer-based freehand drawing on a canvas
4
- */
5
-
6
1
  import { useCallback, useEffect, useRef, useState } from 'react';
7
2
  import type {
8
3
  FreehandPoint,
@@ -12,64 +7,53 @@ import type {
12
7
 
13
8
  const DEFAULT_BRUSH_SIZE = 3;
14
9
  const DEFAULT_COLOR = '#1a1a1a';
10
+ const DEFAULT_KEYBOARD_STEP = 4;
11
+ const KEYBOARD_FAST_FACTOR = 4;
15
12
 
16
- /**
17
- * Width for a point: constant for mouse/touch, pressure-scaled for pens
18
- */
19
13
  function widthFor(stroke: FreehandStroke, point: FreehandPoint): number {
20
14
  if (!stroke.dynamic) return stroke.size;
21
- // pressure is 0 (some mice) – 1 (hard press); clamp the visual range
22
15
  return stroke.size * (0.5 + Math.min(1, Math.max(0, point.pressure)));
23
16
  }
24
17
 
25
- /**
26
- * Return value of the useFreehandCanvas hook
27
- */
28
18
  export interface UseFreehandCanvasReturn {
29
- /** Ref to attach to the <canvas> element that captures drawing. */
30
19
  canvasRef: React.RefObject<HTMLCanvasElement | null>;
31
- /** Remove the most recent stroke. */
32
20
  undo: () => void;
33
- /** Remove all strokes. */
34
21
  clear: () => void;
35
- /** Number of committed strokes. */
36
22
  strokeCount: number;
37
- /** True when no strokes are committed. */
38
23
  isEmpty: boolean;
39
- /** Snapshot of committed strokes (safe to serialize). */
40
24
  getStrokes: () => FreehandStroke[];
41
25
  }
42
26
 
43
- /**
44
- * useFreehandCanvas hook
45
- *
46
- * Attaches pointer-event drawing to a canvas element. The canvas is sized to
47
- * its CSS box at device-pixel resolution (and re-rendered on resize), strokes
48
- * are replayed from captured points so undo/clear are lossless, and stylus
49
- * pressure modulates line width when the pointer reports it.
50
- *
51
- * The canvas gets `touch-action: none` applied so drawing works on touch
52
- * screens without scrolling the page. Consumers typically layer the canvas
53
- * over their own guide content (e.g. a faint glyph to trace).
54
- */
27
+ interface KeyboardPen {
28
+ x: number;
29
+ y: number;
30
+ down: boolean;
31
+ engaged: boolean;
32
+ }
33
+
55
34
  export function useFreehandCanvas(
56
35
  options: UseFreehandCanvasOptions = {}
57
36
  ): UseFreehandCanvasReturn {
58
- const { brushSize = DEFAULT_BRUSH_SIZE, color = DEFAULT_COLOR, pressureEnabled = true } = options;
37
+ const {
38
+ brushSize = DEFAULT_BRUSH_SIZE,
39
+ color = DEFAULT_COLOR,
40
+ pressureEnabled = true,
41
+ keyboard = false,
42
+ keyboardStep = DEFAULT_KEYBOARD_STEP,
43
+ } = options;
59
44
 
60
- // Latest option values for event handlers, without re-binding listeners.
61
- const optionsRef = useRef({ brushSize, color, pressureEnabled, onStrokeEnd: options.onStrokeEnd });
62
- optionsRef.current = { brushSize, color, pressureEnabled, onStrokeEnd: options.onStrokeEnd };
45
+ const optionsRef = useRef({ brushSize, color, pressureEnabled, keyboardStep, onStrokeEnd: options.onStrokeEnd });
46
+ optionsRef.current = { brushSize, color, pressureEnabled, keyboardStep, onStrokeEnd: options.onStrokeEnd };
63
47
 
64
48
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
65
49
  const strokesRef = useRef<FreehandStroke[]>([]);
66
50
  const activeStrokeRef = useRef<FreehandStroke | null>(null);
67
51
  const activePointerIdRef = useRef<number | null>(null);
68
52
  const dprRef = useRef(1);
53
+ const penRef = useRef<KeyboardPen | null>(null);
69
54
 
70
55
  const [strokeCount, setStrokeCount] = useState(0);
71
56
 
72
- /** Replay every committed stroke (and the active one) at current DPR. */
73
57
  const redraw = useCallback(() => {
74
58
  const canvas = canvasRef.current;
75
59
  const ctx = canvas?.getContext('2d');
@@ -103,9 +87,20 @@ export function useFreehandCanvas(
103
87
  ctx.stroke();
104
88
  }
105
89
  }
90
+ const pen = penRef.current;
91
+ if (pen?.engaged && !pen.down) {
92
+ const reach = 5;
93
+ ctx.strokeStyle = 'rgba(35, 37, 43, 0.55)';
94
+ ctx.lineWidth = 1;
95
+ ctx.beginPath();
96
+ ctx.moveTo(pen.x - reach, pen.y);
97
+ ctx.lineTo(pen.x + reach, pen.y);
98
+ ctx.moveTo(pen.x, pen.y - reach);
99
+ ctx.lineTo(pen.x, pen.y + reach);
100
+ ctx.stroke();
101
+ }
106
102
  }, []);
107
103
 
108
- /** Match the backing store to the CSS box; returns true when it changed. */
109
104
  const syncDimensions = useCallback((): boolean => {
110
105
  const canvas = canvasRef.current;
111
106
  if (!canvas) return false;
@@ -125,8 +120,9 @@ export function useFreehandCanvas(
125
120
 
126
121
  const el: HTMLCanvasElement = canvas;
127
122
 
128
- // Touch screens must draw, not scroll, while the pointer is down.
129
123
  el.style.touchAction = 'none';
124
+ if (keyboard && !el.hasAttribute('tabindex')) el.tabIndex = 0;
125
+ if (!penRef.current) penRef.current = { x: el.clientWidth / 2, y: el.clientHeight / 2, down: false, engaged: false };
130
126
 
131
127
  syncDimensions();
132
128
  redraw();
@@ -169,8 +165,6 @@ export function useFreehandCanvas(
169
165
 
170
166
  function onPointerDown(event: PointerEvent): void {
171
167
  if (event.button !== 0 && event.pointerType === 'mouse') return;
172
- // Only one stroke at a time: a resting palm or second finger touching
173
- // mid-stroke must not hijack or corrupt the active stroke.
174
168
  if (activeStrokeRef.current != null) return;
175
169
  event.preventDefault();
176
170
  el.setPointerCapture(event.pointerId);
@@ -183,6 +177,7 @@ export function useFreehandCanvas(
183
177
  points: [pointFrom(event)],
184
178
  };
185
179
  activeStrokeRef.current = stroke;
180
+ penRef.current = { x: stroke.points[0].x, y: stroke.points[0].y, down: false, engaged: false };
186
181
  drawDot(stroke, stroke.points[0]);
187
182
  }
188
183
 
@@ -209,10 +204,76 @@ export function useFreehandCanvas(
209
204
  optionsRef.current.onStrokeEnd?.(stroke);
210
205
  }
211
206
 
207
+ function clampPen(pen: KeyboardPen): void {
208
+ pen.x = Math.min(Math.max(pen.x, 0), Math.max(0, el.clientWidth - 1));
209
+ pen.y = Math.min(Math.max(pen.y, 0), Math.max(0, el.clientHeight - 1));
210
+ }
211
+
212
+ function startKeyboardStroke(pen: KeyboardPen): void {
213
+ const { brushSize: size, color: ink } = optionsRef.current;
214
+ const stroke: FreehandStroke = {
215
+ size,
216
+ color: ink,
217
+ dynamic: false,
218
+ points: [{ x: pen.x, y: pen.y, pressure: 0.5 }],
219
+ };
220
+ activeStrokeRef.current = stroke;
221
+ pen.down = true;
222
+ drawDot(stroke, stroke.points[0]);
223
+ }
224
+
225
+ function commitKeyboardStroke(pen: KeyboardPen): void {
226
+ const stroke = activeStrokeRef.current;
227
+ if (!stroke) return;
228
+ activeStrokeRef.current = null;
229
+ pen.down = false;
230
+ strokesRef.current = [...strokesRef.current, stroke];
231
+ setStrokeCount(strokesRef.current.length);
232
+ optionsRef.current.onStrokeEnd?.(stroke);
233
+ redraw();
234
+ }
235
+
236
+ function onKeyDown(event: KeyboardEvent): void {
237
+ const pen = penRef.current;
238
+ if (!pen) return;
239
+ const fast = event.shiftKey ? KEYBOARD_FAST_FACTOR : 1;
240
+ const step = optionsRef.current.keyboardStep * fast;
241
+ if (event.key === 'ArrowLeft' || event.key === 'ArrowRight' || event.key === 'ArrowUp' || event.key === 'ArrowDown') {
242
+ event.preventDefault();
243
+ pen.engaged = true;
244
+ pen.x += event.key === 'ArrowLeft' ? -step : event.key === 'ArrowRight' ? step : 0;
245
+ pen.y += event.key === 'ArrowUp' ? -step : event.key === 'ArrowDown' ? step : 0;
246
+ clampPen(pen);
247
+ const stroke = activeStrokeRef.current;
248
+ if (pen.down && stroke) {
249
+ const point: FreehandPoint = { x: pen.x, y: pen.y, pressure: 0.5 };
250
+ const previous = stroke.points[stroke.points.length - 1];
251
+ stroke.points.push(point);
252
+ drawSegment(stroke, previous, point);
253
+ } else {
254
+ redraw();
255
+ }
256
+ return;
257
+ }
258
+ if (event.key === ' ' || event.key === 'Enter') {
259
+ event.preventDefault();
260
+ pen.engaged = true;
261
+ if (pen.down) commitKeyboardStroke(pen);
262
+ else startKeyboardStroke(pen);
263
+ return;
264
+ }
265
+ if (event.key === 'Escape' && pen.down) {
266
+ activeStrokeRef.current = null;
267
+ pen.down = false;
268
+ redraw();
269
+ }
270
+ }
271
+
212
272
  el.addEventListener('pointerdown', onPointerDown);
213
273
  el.addEventListener('pointermove', onPointerMove);
214
274
  el.addEventListener('pointerup', commit);
215
275
  el.addEventListener('pointercancel', commit);
276
+ if (keyboard) el.addEventListener('keydown', onKeyDown);
216
277
 
217
278
  return () => {
218
279
  observer.disconnect();
@@ -220,8 +281,9 @@ export function useFreehandCanvas(
220
281
  el.removeEventListener('pointermove', onPointerMove);
221
282
  el.removeEventListener('pointerup', commit);
222
283
  el.removeEventListener('pointercancel', commit);
284
+ el.removeEventListener('keydown', onKeyDown);
223
285
  };
224
- }, [redraw, syncDimensions]);
286
+ }, [redraw, syncDimensions, keyboard]);
225
287
 
226
288
  const undo = useCallback(() => {
227
289
  if (strokesRef.current.length === 0) return;
@@ -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
  }
@@ -1,7 +1,3 @@
1
- /**
2
- * useScreenshotStudio Hook
3
- * React hook for screenshot studio functionality
4
- */
5
1
 
6
2
  import { useState, useCallback, useRef, useEffect } from 'react';
7
3
  import type {
@@ -20,19 +16,12 @@ import type { ScreenshotStudioConfig } from '../../domain/config/ScreenshotStudi
20
16
  import { DEFAULT_SCREENSHOT_STUDIO_CONFIG } from '../../domain/config/ScreenshotStudioConfig';
21
17
  import { ScreenshotStudioService } from '../../application/services/ScreenshotStudioService';
22
18
 
23
- /**
24
- * useScreenshotStudio hook options
25
- */
26
19
  export interface UseScreenshotStudioOptions {
27
20
  config?: Partial<ScreenshotStudioConfig>;
28
21
  onShowToast?: (title: string, description?: string) => void;
29
22
  }
30
23
 
31
- /**
32
- * useScreenshotStudio hook return
33
- */
34
24
  export interface UseScreenshotStudioReturn {
35
- // State
36
25
  screens: ScreenshotContent[];
37
26
  design: ScreenshotDesign;
38
27
  currentLanguage: Language;
@@ -44,33 +33,26 @@ export interface UseScreenshotStudioReturn {
44
33
  appIcon: string | null;
45
34
  projectId: string | null;
46
35
 
47
- // Design management
48
36
  updateDesign: (updates: Partial<ScreenshotDesign>) => void;
49
37
  updateActiveScreen: (updates: Partial<ScreenshotContent>) => void;
50
38
  setSelectedScreenId: (screenId: string) => void;
51
39
 
52
- // Language management
53
40
  changeLanguage: (newLanguage: Language) => void;
54
41
 
55
- // Screen management
56
42
  addScreen: () => void;
57
43
  removeScreen: (screenId: string) => void;
58
44
  duplicateScreen: (screenId: string) => void;
59
45
 
60
- // Template management
61
46
  applyTemplate: (template: ScreenshotTemplate) => void;
62
47
 
63
- // Generation
64
48
  generateDesignedScreenshot: (
65
49
  screenId: string
66
50
  ) => Promise<{ url: string; id: string } | undefined>;
67
51
 
68
- // Export
69
52
  exportAllScreensAsZip: (
70
53
  format?: ScreenshotFormat
71
54
  ) => Promise<void>;
72
55
 
73
- // AI features (if available)
74
56
  handleAIMagic?: (screenId: string) => Promise<void>;
75
57
  handleTranslate?: (
76
58
  screenId: string,
@@ -78,7 +60,6 @@ export interface UseScreenshotStudioReturn {
78
60
  ) => Promise<void>;
79
61
  handleGenerateMetadata?: () => Promise<void>;
80
62
 
81
- // Project management
82
63
  setAppMetadata: (metadata: AppStoreMetadata) => void;
83
64
  setAppIcon: (icon: string | null) => void;
84
65
  setProjectId: (id: string | null) => void;
@@ -86,9 +67,6 @@ export interface UseScreenshotStudioReturn {
86
67
  _loadProjectState: (state: Partial<ScreenshotProject>) => void;
87
68
  }
88
69
 
89
- /**
90
- * Default screens
91
- */
92
70
  const DEFAULT_SCREENS: ScreenshotContent[] = [
93
71
  {
94
72
  id: '1',
@@ -132,9 +110,6 @@ const DEFAULT_SCREENS: ScreenshotContent[] = [
132
110
  },
133
111
  ];
134
112
 
135
- /**
136
- * Default design
137
- */
138
113
  const DEFAULT_DESIGN: ScreenshotDesign = {
139
114
  backgroundColor: '#6366f1',
140
115
  backgroundGradientEnd: '#a855f7',
@@ -169,9 +144,6 @@ const DEFAULT_DESIGN: ScreenshotDesign = {
169
144
  isPanoramic: false,
170
145
  };
171
146
 
172
- /**
173
- * Default app metadata
174
- */
175
147
  const DEFAULT_APP_METADATA: AppStoreMetadata = {
176
148
  appName: 'Growdo',
177
149
  subtitle: 'AI Content Generator',
@@ -182,9 +154,6 @@ const DEFAULT_APP_METADATA: AppStoreMetadata = {
182
154
  whatsNew: 'New Design Studio integration!',
183
155
  };
184
156
 
185
- /**
186
- * useScreenshotStudio hook
187
- */
188
157
  export function useScreenshotStudio(
189
158
  options: UseScreenshotStudioOptions = {}
190
159
  ): UseScreenshotStudioReturn {
@@ -200,21 +169,10 @@ export function useScreenshotStudio(
200
169
 
201
170
  const serviceRef = useRef<ScreenshotStudioService | null>(null);
202
171
 
203
- // Initialize service once
204
172
  if (!serviceRef.current) {
205
173
  serviceRef.current = new ScreenshotStudioService(fullConfig);
206
174
  }
207
175
 
208
- // Cleanup on unmount
209
- useEffect(() => {
210
- return () => {
211
- // Clean up service resources if needed
212
- if (serviceRef.current) {
213
- // Any cleanup logic can go here
214
- }
215
- };
216
- }, []);
217
-
218
176
  const [screens, setScreens] = useState<ScreenshotContent[]>(DEFAULT_SCREENS);
219
177
  const [design, setDesign] = useState<ScreenshotDesign>(DEFAULT_DESIGN);
220
178
  const [currentLanguage, setCurrentLanguage] = useState<Language>(
@@ -414,13 +372,11 @@ export function useScreenshotStudio(
414
372
  } finally {
415
373
  setIsProcessing(false);
416
374
 
417
- // Clean up object URL after a delay
418
375
  if (url) {
419
376
  setTimeout(() => {
420
377
  try {
421
378
  URL.revokeObjectURL(url!);
422
- } catch (e) {
423
- // Ignore cleanup errors
379
+ } catch {
424
380
  }
425
381
  }, 100);
426
382
  }
@@ -1,7 +1,3 @@
1
- /**
2
- * Presentation Layer Index
3
- * Export all presentation hooks
4
- */
5
1
 
6
2
  export * from './hooks/useScreenshotStudio';
7
3
  export * from './hooks/useCanvasExport';