@umituz/web-canvas 1.0.0
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/CanvasConfig-pzoE0RHD.d.mts +310 -0
- package/dist/CanvasConfig-pzoE0RHD.d.ts +310 -0
- package/dist/application/index.d.mts +78 -0
- package/dist/application/index.d.ts +78 -0
- package/dist/application/index.js +1083 -0
- package/dist/application/index.mjs +11 -0
- package/dist/chunk-BN7JTXL7.mjs +346 -0
- package/dist/chunk-MC6PNV2F.mjs +34 -0
- package/dist/chunk-SF6WND7M.mjs +547 -0
- package/dist/chunk-TZGTTYTZ.mjs +0 -0
- package/dist/chunk-WMKPCJFI.mjs +661 -0
- package/dist/chunk-Z6IB4BQV.mjs +31 -0
- package/dist/domain/config/index.d.mts +87 -0
- package/dist/domain/config/index.d.ts +87 -0
- package/dist/domain/config/index.js +92 -0
- package/dist/domain/config/index.mjs +15 -0
- package/dist/domain/index.d.mts +79 -0
- package/dist/domain/index.d.ts +79 -0
- package/dist/domain/index.js +137 -0
- package/dist/domain/index.mjs +55 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +1633 -0
- package/dist/index.mjs +38 -0
- package/dist/infrastructure/index.d.mts +174 -0
- package/dist/infrastructure/index.d.ts +174 -0
- package/dist/infrastructure/index.js +714 -0
- package/dist/infrastructure/index.mjs +13 -0
- package/dist/presentation/index.d.mts +108 -0
- package/dist/presentation/index.d.ts +108 -0
- package/dist/presentation/index.js +1617 -0
- package/dist/presentation/index.mjs +14 -0
- package/package.json +86 -0
- package/src/application/index.ts +7 -0
- package/src/application/services/ReportService.ts +200 -0
- package/src/application/services/ScreenshotStudioService.ts +243 -0
- package/src/domain/config/CanvasConfig.ts +36 -0
- package/src/domain/config/DeviceConfig.ts +47 -0
- package/src/domain/config/ScreenshotStudioConfig.ts +82 -0
- package/src/domain/config/index.ts +8 -0
- package/src/domain/index.ts +7 -0
- package/src/domain/types/canvas.types.ts +85 -0
- package/src/domain/types/index.ts +8 -0
- package/src/domain/types/report.types.ts +83 -0
- package/src/domain/types/screenshot.types.ts +238 -0
- package/src/index.ts +102 -0
- package/src/infrastructure/index.ts +9 -0
- package/src/infrastructure/renderers/CanvasRenderer.ts +287 -0
- package/src/infrastructure/renderers/PatternRenderer.ts +160 -0
- package/src/infrastructure/renderers/ScreenshotRenderer.ts +331 -0
- package/src/infrastructure/services/DeviceConfigService.ts +144 -0
- package/src/presentation/hooks/useCanvasExport.ts +162 -0
- package/src/presentation/hooks/useReportExport.ts +161 -0
- package/src/presentation/hooks/useScreenshotStudio.ts +480 -0
- package/src/presentation/index.ts +8 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useReportExport Hook
|
|
3
|
+
* React hook for report export operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useState, useCallback } from 'react';
|
|
7
|
+
import type { ReportData } from '../../domain/types/report.types';
|
|
8
|
+
import type {
|
|
9
|
+
PDFExportOptions,
|
|
10
|
+
ExcelExportOptions,
|
|
11
|
+
} from '../../domain/types/report.types';
|
|
12
|
+
import { ReportService } from '../../application/services/ReportService';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* useReportExport hook return
|
|
16
|
+
*/
|
|
17
|
+
export interface UseReportExportReturn {
|
|
18
|
+
isExporting: boolean;
|
|
19
|
+
error: string | null;
|
|
20
|
+
exportToPDF: (
|
|
21
|
+
elementId: string,
|
|
22
|
+
options?: PDFExportOptions
|
|
23
|
+
) => Promise<void>;
|
|
24
|
+
exportCanvasToPDF: (
|
|
25
|
+
canvas: HTMLCanvasElement,
|
|
26
|
+
options?: PDFExportOptions
|
|
27
|
+
) => Promise<void>;
|
|
28
|
+
exportMultipleToPDF: (
|
|
29
|
+
elementIds: string[],
|
|
30
|
+
options?: PDFExportOptions
|
|
31
|
+
) => Promise<void>;
|
|
32
|
+
exportToExcel: (
|
|
33
|
+
data: ReportData,
|
|
34
|
+
options?: ExcelExportOptions
|
|
35
|
+
) => Promise<void>;
|
|
36
|
+
exportToCSV: (
|
|
37
|
+
data: ReportData,
|
|
38
|
+
filename?: string,
|
|
39
|
+
delimiter?: ',' | ';'
|
|
40
|
+
) => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* useReportExport hook
|
|
45
|
+
*/
|
|
46
|
+
export function useReportExport(): UseReportExportReturn {
|
|
47
|
+
const [isExporting, setIsExporting] = useState(false);
|
|
48
|
+
const [error, setError] = useState<string | null>(null);
|
|
49
|
+
|
|
50
|
+
const reportService = ReportService.getInstance();
|
|
51
|
+
|
|
52
|
+
const exportToPDF = useCallback(
|
|
53
|
+
async (elementId: string, options?: PDFExportOptions): Promise<void> => {
|
|
54
|
+
setIsExporting(true);
|
|
55
|
+
setError(null);
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
await reportService.exportToPDF(elementId, options);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
const errorMessage =
|
|
61
|
+
err instanceof Error ? err.message : 'PDF export failed';
|
|
62
|
+
setError(errorMessage);
|
|
63
|
+
throw err;
|
|
64
|
+
} finally {
|
|
65
|
+
setIsExporting(false);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
[reportService]
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const exportCanvasToPDF = useCallback(
|
|
72
|
+
async (
|
|
73
|
+
canvas: HTMLCanvasElement,
|
|
74
|
+
options?: PDFExportOptions
|
|
75
|
+
): Promise<void> => {
|
|
76
|
+
setIsExporting(true);
|
|
77
|
+
setError(null);
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
await reportService.exportCanvasToPDF(canvas, options);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
const errorMessage =
|
|
83
|
+
err instanceof Error ? err.message : 'Canvas PDF export failed';
|
|
84
|
+
setError(errorMessage);
|
|
85
|
+
throw err;
|
|
86
|
+
} finally {
|
|
87
|
+
setIsExporting(false);
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
[reportService]
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const exportMultipleToPDF = useCallback(
|
|
94
|
+
async (
|
|
95
|
+
elementIds: string[],
|
|
96
|
+
options?: PDFExportOptions
|
|
97
|
+
): Promise<void> => {
|
|
98
|
+
setIsExporting(true);
|
|
99
|
+
setError(null);
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
await reportService.exportMultipleToPDF(elementIds, options);
|
|
103
|
+
} catch (err) {
|
|
104
|
+
const errorMessage =
|
|
105
|
+
err instanceof Error ? err.message : 'Multi-page PDF export failed';
|
|
106
|
+
setError(errorMessage);
|
|
107
|
+
throw err;
|
|
108
|
+
} finally {
|
|
109
|
+
setIsExporting(false);
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
[reportService]
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const exportToExcel = useCallback(
|
|
116
|
+
async (data: ReportData, options?: ExcelExportOptions): Promise<void> => {
|
|
117
|
+
setIsExporting(true);
|
|
118
|
+
setError(null);
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
await reportService.exportToExcel(data, options);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
const errorMessage =
|
|
124
|
+
err instanceof Error ? err.message : 'Excel export failed';
|
|
125
|
+
setError(errorMessage);
|
|
126
|
+
throw err;
|
|
127
|
+
} finally {
|
|
128
|
+
setIsExporting(false);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
[reportService]
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const exportToCSV = useCallback(
|
|
135
|
+
(
|
|
136
|
+
data: ReportData,
|
|
137
|
+
filename?: string,
|
|
138
|
+
delimiter?: ',' | ';'
|
|
139
|
+
): void => {
|
|
140
|
+
try {
|
|
141
|
+
reportService.exportToCSV(data, filename, delimiter);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
const errorMessage =
|
|
144
|
+
err instanceof Error ? err.message : 'CSV export failed';
|
|
145
|
+
setError(errorMessage);
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
[reportService]
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
isExporting,
|
|
154
|
+
error,
|
|
155
|
+
exportToPDF,
|
|
156
|
+
exportCanvasToPDF,
|
|
157
|
+
exportMultipleToPDF,
|
|
158
|
+
exportToExcel,
|
|
159
|
+
exportToCSV,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useScreenshotStudio Hook
|
|
3
|
+
* React hook for screenshot studio functionality
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { useState, useCallback, useRef } from 'react';
|
|
7
|
+
import type {
|
|
8
|
+
ScreenshotContent,
|
|
9
|
+
ScreenshotDesign,
|
|
10
|
+
Language,
|
|
11
|
+
DeviceType,
|
|
12
|
+
LayoutStyle,
|
|
13
|
+
FrameStyle,
|
|
14
|
+
AppStoreMetadata,
|
|
15
|
+
ScreenshotProject,
|
|
16
|
+
ScreenshotFormat,
|
|
17
|
+
ScreenshotTemplate,
|
|
18
|
+
} from '../../domain/types/screenshot.types';
|
|
19
|
+
import type { ScreenshotStudioConfig } from '../../domain/config/ScreenshotStudioConfig';
|
|
20
|
+
import { DEFAULT_SCREENSHOT_STUDIO_CONFIG } from '../../domain/config/ScreenshotStudioConfig';
|
|
21
|
+
import { ScreenshotStudioService } from '../../application/services/ScreenshotStudioService';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* useScreenshotStudio hook options
|
|
25
|
+
*/
|
|
26
|
+
export interface UseScreenshotStudioOptions {
|
|
27
|
+
config?: Partial<ScreenshotStudioConfig>;
|
|
28
|
+
onShowToast?: (title: string, description?: string) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* useScreenshotStudio hook return
|
|
33
|
+
*/
|
|
34
|
+
export interface UseScreenshotStudioReturn {
|
|
35
|
+
// State
|
|
36
|
+
screens: ScreenshotContent[];
|
|
37
|
+
design: ScreenshotDesign;
|
|
38
|
+
currentLanguage: Language;
|
|
39
|
+
selectedScreenId: string;
|
|
40
|
+
activeScreen: ScreenshotContent;
|
|
41
|
+
isProcessing: boolean;
|
|
42
|
+
isAnalyzing: boolean;
|
|
43
|
+
appMetadata: AppStoreMetadata;
|
|
44
|
+
appIcon: string | null;
|
|
45
|
+
projectId: string | null;
|
|
46
|
+
|
|
47
|
+
// Design management
|
|
48
|
+
updateDesign: (updates: Partial<ScreenshotDesign>) => void;
|
|
49
|
+
updateActiveScreen: (updates: Partial<ScreenshotContent>) => void;
|
|
50
|
+
setSelectedScreenId: (screenId: string) => void;
|
|
51
|
+
|
|
52
|
+
// Language management
|
|
53
|
+
changeLanguage: (newLanguage: Language) => void;
|
|
54
|
+
|
|
55
|
+
// Screen management
|
|
56
|
+
addScreen: () => void;
|
|
57
|
+
removeScreen: (screenId: string) => void;
|
|
58
|
+
duplicateScreen: (screenId: string) => void;
|
|
59
|
+
|
|
60
|
+
// Template management
|
|
61
|
+
applyTemplate: (template: ScreenshotTemplate) => void;
|
|
62
|
+
|
|
63
|
+
// Generation
|
|
64
|
+
generateDesignedScreenshot: (
|
|
65
|
+
screenId: string
|
|
66
|
+
) => Promise<{ url: string; id: string } | undefined>;
|
|
67
|
+
|
|
68
|
+
// Export
|
|
69
|
+
exportAllScreensAsZip: (
|
|
70
|
+
format?: ScreenshotFormat
|
|
71
|
+
) => Promise<void>;
|
|
72
|
+
|
|
73
|
+
// AI features (if available)
|
|
74
|
+
handleAIMagic?: (screenId: string) => Promise<void>;
|
|
75
|
+
handleTranslate?: (
|
|
76
|
+
screenId: string,
|
|
77
|
+
targetLang: Language
|
|
78
|
+
) => Promise<void>;
|
|
79
|
+
handleGenerateMetadata?: () => Promise<void>;
|
|
80
|
+
|
|
81
|
+
// Project management
|
|
82
|
+
setAppMetadata: (metadata: AppStoreMetadata) => void;
|
|
83
|
+
setAppIcon: (icon: string | null) => void;
|
|
84
|
+
setProjectId: (id: string | null) => void;
|
|
85
|
+
_getProjectState: () => ScreenshotProject;
|
|
86
|
+
_loadProjectState: (state: Partial<ScreenshotProject>) => void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Default screens
|
|
91
|
+
*/
|
|
92
|
+
const DEFAULT_SCREENS: ScreenshotContent[] = [
|
|
93
|
+
{
|
|
94
|
+
id: '1',
|
|
95
|
+
title: 'APPGROWTHFACTORY *MODERN*',
|
|
96
|
+
subtitle: 'Create content with AI power',
|
|
97
|
+
screenshot: null,
|
|
98
|
+
translations: {
|
|
99
|
+
'en-US': {
|
|
100
|
+
title: 'APPGROWTHFACTORY *MODERN*',
|
|
101
|
+
subtitle: 'Create content with AI power',
|
|
102
|
+
},
|
|
103
|
+
'tr-TR': {
|
|
104
|
+
title: 'APPGROWTHFACTORY *MODERN*',
|
|
105
|
+
subtitle: 'Yapay zeka gücüyle içerik üret',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
imageFit: 'cover',
|
|
109
|
+
imageZoom: 1,
|
|
110
|
+
imageAnchorX: 50,
|
|
111
|
+
imageAnchorY: 50,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: '2',
|
|
115
|
+
title: 'CONNECT *GLOBAL*',
|
|
116
|
+
subtitle: 'Post to all platforms instantly',
|
|
117
|
+
screenshot: null,
|
|
118
|
+
translations: {
|
|
119
|
+
'en-US': {
|
|
120
|
+
title: 'CONNECT *GLOBAL*',
|
|
121
|
+
subtitle: 'Post to all platforms instantly',
|
|
122
|
+
},
|
|
123
|
+
'tr-TR': {
|
|
124
|
+
title: 'BAĞLAN *KÜRESEL*',
|
|
125
|
+
subtitle: 'Tüm platformlara anında gönder',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
imageFit: 'cover',
|
|
129
|
+
imageZoom: 1,
|
|
130
|
+
imageAnchorX: 50,
|
|
131
|
+
imageAnchorY: 50,
|
|
132
|
+
},
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Default design
|
|
137
|
+
*/
|
|
138
|
+
const DEFAULT_DESIGN: ScreenshotDesign = {
|
|
139
|
+
backgroundColor: '#6366f1',
|
|
140
|
+
backgroundGradientEnd: '#a855f7',
|
|
141
|
+
backgroundImage: null,
|
|
142
|
+
backgroundBlur: 0,
|
|
143
|
+
backgroundOverlayOpacity: 0,
|
|
144
|
+
backgroundPattern: 'none',
|
|
145
|
+
textColor: '#ffffff',
|
|
146
|
+
highlightColor: '#fbbf24',
|
|
147
|
+
textAlign: 'center',
|
|
148
|
+
deviceColor: '#1f2937',
|
|
149
|
+
deviceType: 'iphone-15-pro' as DeviceType,
|
|
150
|
+
frameStyle: 'realistic',
|
|
151
|
+
layout: 'text-top',
|
|
152
|
+
scale: 0.85,
|
|
153
|
+
fontFamily: 'Inter',
|
|
154
|
+
titleFontSize: 60,
|
|
155
|
+
subtitleFontSize: 24,
|
|
156
|
+
titleFontWeight: '900',
|
|
157
|
+
subtitleFontWeight: '500',
|
|
158
|
+
textCase: 'uppercase',
|
|
159
|
+
lineHeight: 0.9,
|
|
160
|
+
letterSpacing: -1,
|
|
161
|
+
rotation: 0,
|
|
162
|
+
rotationX: 0,
|
|
163
|
+
rotationY: 0,
|
|
164
|
+
rotationZ: 0,
|
|
165
|
+
offsetY: 0,
|
|
166
|
+
shadowIntensity: 0.5,
|
|
167
|
+
padding: 40,
|
|
168
|
+
gap: 20,
|
|
169
|
+
isPanoramic: false,
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Default app metadata
|
|
174
|
+
*/
|
|
175
|
+
const DEFAULT_APP_METADATA: AppStoreMetadata = {
|
|
176
|
+
appName: 'Growdo',
|
|
177
|
+
subtitle: 'AI Content Generator',
|
|
178
|
+
promotionalText: 'Unlock your creative potential',
|
|
179
|
+
keywords: 'ai, content, marketing, automation, generator',
|
|
180
|
+
description:
|
|
181
|
+
'The ultimate tool for AI-powered marketing content.',
|
|
182
|
+
whatsNew: 'New Design Studio integration!',
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* useScreenshotStudio hook
|
|
187
|
+
*/
|
|
188
|
+
export function useScreenshotStudio(
|
|
189
|
+
options: UseScreenshotStudioOptions = {}
|
|
190
|
+
): UseScreenshotStudioReturn {
|
|
191
|
+
const {
|
|
192
|
+
config = {},
|
|
193
|
+
onShowToast = () => {},
|
|
194
|
+
} = options;
|
|
195
|
+
|
|
196
|
+
const fullConfig = {
|
|
197
|
+
...DEFAULT_SCREENSHOT_STUDIO_CONFIG,
|
|
198
|
+
...config,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const serviceRef = useRef(
|
|
202
|
+
new ScreenshotStudioService(fullConfig)
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const [screens, setScreens] = useState<ScreenshotContent[]>(DEFAULT_SCREENS);
|
|
206
|
+
const [design, setDesign] = useState<ScreenshotDesign>(DEFAULT_DESIGN);
|
|
207
|
+
const [currentLanguage, setCurrentLanguage] = useState<Language>(
|
|
208
|
+
fullConfig.defaultLanguage || 'en-US'
|
|
209
|
+
);
|
|
210
|
+
const [selectedScreenId, setSelectedScreenId] = useState<string>('1');
|
|
211
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
212
|
+
const [isAnalyzing, setIsAnalyzing] = useState(false);
|
|
213
|
+
const [appMetadata, setAppMetadata] = useState<AppStoreMetadata>(
|
|
214
|
+
DEFAULT_APP_METADATA
|
|
215
|
+
);
|
|
216
|
+
const [appIcon, setAppIcon] = useState<string | null>(null);
|
|
217
|
+
const [projectId, setProjectId] = useState<string | null>(null);
|
|
218
|
+
|
|
219
|
+
const activeScreen =
|
|
220
|
+
screens.find((s) => s.id === selectedScreenId) || screens[0];
|
|
221
|
+
|
|
222
|
+
const updateDesign = useCallback((updates: Partial<ScreenshotDesign>) => {
|
|
223
|
+
setDesign((prev) => ({ ...prev, ...updates }));
|
|
224
|
+
}, []);
|
|
225
|
+
|
|
226
|
+
const updateActiveScreen = useCallback(
|
|
227
|
+
(updates: Partial<ScreenshotContent>) => {
|
|
228
|
+
setScreens((prev) =>
|
|
229
|
+
prev.map((screen) => {
|
|
230
|
+
if (screen.id !== selectedScreenId) return screen;
|
|
231
|
+
const updatedScreen = { ...screen, ...updates };
|
|
232
|
+
|
|
233
|
+
if (updates.title || updates.subtitle) {
|
|
234
|
+
updatedScreen.translations = {
|
|
235
|
+
...updatedScreen.translations,
|
|
236
|
+
[currentLanguage]: {
|
|
237
|
+
title: updatedScreen.title,
|
|
238
|
+
subtitle: updatedScreen.subtitle,
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return updatedScreen;
|
|
243
|
+
})
|
|
244
|
+
);
|
|
245
|
+
},
|
|
246
|
+
[selectedScreenId, currentLanguage]
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const changeLanguage = useCallback(
|
|
250
|
+
(newLanguage: Language) => {
|
|
251
|
+
setCurrentLanguage(newLanguage);
|
|
252
|
+
setScreens((prev) =>
|
|
253
|
+
prev.map((screen) => {
|
|
254
|
+
const translation =
|
|
255
|
+
screen.translations[newLanguage] ||
|
|
256
|
+
screen.translations['en-US'] ||
|
|
257
|
+
{ title: screen.title, subtitle: screen.subtitle };
|
|
258
|
+
return {
|
|
259
|
+
...screen,
|
|
260
|
+
title: translation.title,
|
|
261
|
+
subtitle: translation.subtitle,
|
|
262
|
+
};
|
|
263
|
+
})
|
|
264
|
+
);
|
|
265
|
+
onShowToast('Language Changed', `Switched to ${newLanguage}`);
|
|
266
|
+
},
|
|
267
|
+
[onShowToast]
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const addScreen = useCallback(() => {
|
|
271
|
+
const newScreen: ScreenshotContent = {
|
|
272
|
+
id: Date.now().toString(),
|
|
273
|
+
title: 'NEW *POST*',
|
|
274
|
+
subtitle: 'Engage your audience',
|
|
275
|
+
screenshot: null,
|
|
276
|
+
translations: {
|
|
277
|
+
[currentLanguage]: {
|
|
278
|
+
title: 'NEW *POST*',
|
|
279
|
+
subtitle: 'Engage your audience',
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
imageFit: 'cover',
|
|
283
|
+
imageZoom: 1,
|
|
284
|
+
imageAnchorX: 50,
|
|
285
|
+
imageAnchorY: 50,
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
setScreens((prev) => [...prev, newScreen]);
|
|
289
|
+
setSelectedScreenId(newScreen.id);
|
|
290
|
+
onShowToast('Success', 'New screen added');
|
|
291
|
+
}, [currentLanguage, onShowToast]);
|
|
292
|
+
|
|
293
|
+
const removeScreen = useCallback(
|
|
294
|
+
(screenId: string) => {
|
|
295
|
+
if (screens.length <= 1) return;
|
|
296
|
+
|
|
297
|
+
setScreens((prev) => {
|
|
298
|
+
const newScreens = prev.filter((s) => s.id !== screenId);
|
|
299
|
+
if (selectedScreenId === screenId) {
|
|
300
|
+
setSelectedScreenId(newScreens[0].id);
|
|
301
|
+
}
|
|
302
|
+
return newScreens;
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
onShowToast('Info', 'Screen removed');
|
|
306
|
+
},
|
|
307
|
+
[screens.length, selectedScreenId, onShowToast]
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
const duplicateScreen = useCallback(
|
|
311
|
+
(screenId: string) => {
|
|
312
|
+
const screenToDuplicate = screens.find((s) => s.id === screenId);
|
|
313
|
+
if (!screenToDuplicate) return;
|
|
314
|
+
|
|
315
|
+
const newScreen: ScreenshotContent = {
|
|
316
|
+
...screenToDuplicate,
|
|
317
|
+
id: Date.now().toString(),
|
|
318
|
+
title: `${screenToDuplicate.title} (Copy)`,
|
|
319
|
+
translations: { ...screenToDuplicate.translations },
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
setScreens((prev) => {
|
|
323
|
+
const index = prev.findIndex((s) => s.id === screenId);
|
|
324
|
+
const newScreens = [...prev];
|
|
325
|
+
newScreens.splice(index + 1, 0, newScreen);
|
|
326
|
+
return newScreens;
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
setSelectedScreenId(newScreen.id);
|
|
330
|
+
onShowToast('Success', 'Screen duplicated');
|
|
331
|
+
},
|
|
332
|
+
[screens, onShowToast]
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
const applyTemplate = useCallback(
|
|
336
|
+
(template: ScreenshotTemplate) => {
|
|
337
|
+
setDesign((prev) => ({ ...prev, ...template.design }));
|
|
338
|
+
onShowToast('Template Applied', template.name);
|
|
339
|
+
},
|
|
340
|
+
[onShowToast]
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
const generateDesignedScreenshot = useCallback(
|
|
344
|
+
async (screenId: string) => {
|
|
345
|
+
const screen = screens.find((s) => s.id === screenId);
|
|
346
|
+
if (!screen) return;
|
|
347
|
+
|
|
348
|
+
setIsProcessing(true);
|
|
349
|
+
try {
|
|
350
|
+
const result = await serviceRef.current.generateDesignedScreenshot(
|
|
351
|
+
screen,
|
|
352
|
+
design
|
|
353
|
+
);
|
|
354
|
+
onShowToast('Success', 'Screenshot generated');
|
|
355
|
+
return { url: result.url, id: result.id };
|
|
356
|
+
} catch {
|
|
357
|
+
onShowToast('Error', 'Screenshot generation failed');
|
|
358
|
+
} finally {
|
|
359
|
+
setIsProcessing(false);
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
[screens, design, onShowToast]
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
const exportAllScreensAsZip = useCallback(
|
|
366
|
+
async (format: ScreenshotFormat = 'png') => {
|
|
367
|
+
setIsProcessing(true);
|
|
368
|
+
try {
|
|
369
|
+
const project: ScreenshotProject = {
|
|
370
|
+
id: projectId || 'default-project',
|
|
371
|
+
name: 'Export Project',
|
|
372
|
+
design,
|
|
373
|
+
screens,
|
|
374
|
+
currentLanguage,
|
|
375
|
+
selectedScreenId,
|
|
376
|
+
appMetadata,
|
|
377
|
+
appIcon,
|
|
378
|
+
createdAt: new Date(),
|
|
379
|
+
updatedAt: new Date(),
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
const blob = await serviceRef.current.exportProjectAsZip(project);
|
|
383
|
+
const url = URL.createObjectURL(blob);
|
|
384
|
+
const link = document.createElement('a');
|
|
385
|
+
link.href = url;
|
|
386
|
+
link.download = `Studio_${currentLanguage}_${Date.now()}.zip`;
|
|
387
|
+
link.click();
|
|
388
|
+
URL.revokeObjectURL(url);
|
|
389
|
+
|
|
390
|
+
onShowToast(
|
|
391
|
+
'Success',
|
|
392
|
+
`Exported ${screens.length} screens in ZIP archive.`
|
|
393
|
+
);
|
|
394
|
+
} catch {
|
|
395
|
+
onShowToast('Error', 'ZIP export failed');
|
|
396
|
+
} finally {
|
|
397
|
+
setIsProcessing(false);
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
[
|
|
401
|
+
screens,
|
|
402
|
+
design,
|
|
403
|
+
currentLanguage,
|
|
404
|
+
appMetadata,
|
|
405
|
+
appIcon,
|
|
406
|
+
projectId,
|
|
407
|
+
selectedScreenId,
|
|
408
|
+
onShowToast,
|
|
409
|
+
]
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
const _getProjectState = useCallback((): ScreenshotProject => {
|
|
413
|
+
return {
|
|
414
|
+
id: projectId || 'appgrowthfactory_main_project',
|
|
415
|
+
name: 'Default Project',
|
|
416
|
+
design,
|
|
417
|
+
screens,
|
|
418
|
+
currentLanguage,
|
|
419
|
+
selectedScreenId,
|
|
420
|
+
appMetadata,
|
|
421
|
+
appIcon,
|
|
422
|
+
createdAt: new Date(),
|
|
423
|
+
updatedAt: new Date(),
|
|
424
|
+
};
|
|
425
|
+
}, [
|
|
426
|
+
design,
|
|
427
|
+
screens,
|
|
428
|
+
currentLanguage,
|
|
429
|
+
selectedScreenId,
|
|
430
|
+
appMetadata,
|
|
431
|
+
appIcon,
|
|
432
|
+
projectId,
|
|
433
|
+
]);
|
|
434
|
+
|
|
435
|
+
const _loadProjectState = useCallback(
|
|
436
|
+
(state: Partial<ScreenshotProject>) => {
|
|
437
|
+
if (!state) return;
|
|
438
|
+
if (state.screens) setScreens(state.screens);
|
|
439
|
+
if (state.design) setDesign(state.design);
|
|
440
|
+
if (state.currentLanguage) setCurrentLanguage(state.currentLanguage);
|
|
441
|
+
if (
|
|
442
|
+
state.selectedScreenId &&
|
|
443
|
+
state.screens?.find((s) => s.id === state.selectedScreenId)
|
|
444
|
+
) {
|
|
445
|
+
setSelectedScreenId(state.selectedScreenId);
|
|
446
|
+
}
|
|
447
|
+
if (state.appMetadata) setAppMetadata(state.appMetadata);
|
|
448
|
+
if (state.appIcon !== undefined) setAppIcon(state.appIcon);
|
|
449
|
+
},
|
|
450
|
+
[]
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
return {
|
|
454
|
+
screens,
|
|
455
|
+
design,
|
|
456
|
+
currentLanguage,
|
|
457
|
+
selectedScreenId,
|
|
458
|
+
activeScreen,
|
|
459
|
+
isProcessing,
|
|
460
|
+
isAnalyzing,
|
|
461
|
+
appMetadata,
|
|
462
|
+
appIcon,
|
|
463
|
+
projectId,
|
|
464
|
+
updateDesign,
|
|
465
|
+
updateActiveScreen,
|
|
466
|
+
setSelectedScreenId,
|
|
467
|
+
changeLanguage,
|
|
468
|
+
applyTemplate,
|
|
469
|
+
addScreen,
|
|
470
|
+
removeScreen,
|
|
471
|
+
duplicateScreen,
|
|
472
|
+
generateDesignedScreenshot,
|
|
473
|
+
exportAllScreensAsZip,
|
|
474
|
+
setAppMetadata,
|
|
475
|
+
setAppIcon,
|
|
476
|
+
setProjectId,
|
|
477
|
+
_getProjectState,
|
|
478
|
+
_loadProjectState,
|
|
479
|
+
};
|
|
480
|
+
}
|