@umituz/web-canvas 1.0.4 → 1.0.6

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.
@@ -1,765 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/infrastructure/index.ts
21
- var infrastructure_exports = {};
22
- __export(infrastructure_exports, {
23
- CanvasRenderer: () => CanvasRenderer,
24
- DeviceConfigService: () => DeviceConfigService,
25
- PatternRenderer: () => PatternRenderer,
26
- ScreenshotRenderer: () => ScreenshotRenderer
27
- });
28
- module.exports = __toCommonJS(infrastructure_exports);
29
-
30
- // src/domain/config/DeviceConfig.ts
31
- var DEVICE_DIMENSIONS = {
32
- "iphone-15-pro": { width: 1290, height: 2796 },
33
- "iphone-15": { width: 1179, height: 2556 },
34
- "iphone-se": { width: 1080, height: 2340 },
35
- "ipad-pro": { width: 2048, height: 2732 },
36
- "ipad": { width: 1620, height: 2160 },
37
- "android-phone": { width: 1440, height: 3120 },
38
- "android-tablet": { width: 2e3, height: 2200 },
39
- "desktop": { width: 1920, height: 1080 }
40
- };
41
- var DEFAULT_DEVICE_CONFIG = {
42
- devices: Object.keys(DEVICE_DIMENSIONS),
43
- defaultDevice: "iphone-15-pro",
44
- showFrame: true,
45
- frameColor: "#1f2937",
46
- frameThickness: 8
47
- };
48
-
49
- // src/infrastructure/services/DeviceConfigService.ts
50
- var DeviceConfigService = class _DeviceConfigService {
51
- constructor() {
52
- this.phonesCache = null;
53
- this.tabletsCache = null;
54
- this.allDevicesCache = null;
55
- this.devices = /* @__PURE__ */ new Map();
56
- this.initializeDevices();
57
- }
58
- static getInstance() {
59
- if (!_DeviceConfigService.instance) {
60
- _DeviceConfigService.instance = new _DeviceConfigService();
61
- }
62
- return _DeviceConfigService.instance;
63
- }
64
- initializeDevices() {
65
- const deviceConfigs = [
66
- {
67
- name: "iPhone 15 Pro",
68
- dimensions: { width: 1290, height: 2796, scale: 3 },
69
- isPhone: true,
70
- hasNotch: true,
71
- cornerRadius: 20,
72
- frameThickness: 8
73
- },
74
- {
75
- name: "iPhone 15",
76
- dimensions: { width: 1179, height: 2556, scale: 3 },
77
- isPhone: true,
78
- hasNotch: true,
79
- cornerRadius: 20,
80
- frameThickness: 8
81
- },
82
- {
83
- name: "iPhone SE",
84
- dimensions: { width: 1080, height: 2340, scale: 3 },
85
- isPhone: true,
86
- hasNotch: false,
87
- cornerRadius: 16,
88
- frameThickness: 6
89
- },
90
- {
91
- name: "iPad Pro",
92
- dimensions: { width: 2048, height: 2732, scale: 2 },
93
- isTablet: true,
94
- hasNotch: false,
95
- cornerRadius: 24,
96
- frameThickness: 10
97
- },
98
- {
99
- name: "iPad",
100
- dimensions: { width: 1620, height: 2160, scale: 2 },
101
- isTablet: true,
102
- hasNotch: false,
103
- cornerRadius: 20,
104
- frameThickness: 10
105
- },
106
- {
107
- name: "Android Phone",
108
- dimensions: { width: 1440, height: 3120, scale: 3 },
109
- isPhone: true,
110
- hasNotch: false,
111
- cornerRadius: 24,
112
- frameThickness: 8
113
- },
114
- {
115
- name: "Android Tablet",
116
- dimensions: { width: 2e3, height: 2200, scale: 2 },
117
- isTablet: true,
118
- hasNotch: false,
119
- cornerRadius: 24,
120
- frameThickness: 10
121
- },
122
- {
123
- name: "Desktop",
124
- dimensions: { width: 1920, height: 1080, scale: 1 },
125
- isPhone: false,
126
- isTablet: false,
127
- hasNotch: false,
128
- cornerRadius: 0,
129
- frameThickness: 0
130
- }
131
- ];
132
- Object.entries(DEVICE_DIMENSIONS).forEach(([type, dimensions]) => {
133
- const deviceType = type;
134
- const config = deviceConfigs.find((_, index) => {
135
- const types = Object.keys(DEVICE_DIMENSIONS);
136
- return types[index] === type;
137
- });
138
- if (config) {
139
- this.devices.set(deviceType, {
140
- type: deviceType,
141
- ...config,
142
- dimensions
143
- });
144
- }
145
- });
146
- }
147
- getDeviceConfig(deviceType) {
148
- return this.devices.get(deviceType);
149
- }
150
- getAllDevices() {
151
- if (!this.allDevicesCache) {
152
- this.allDevicesCache = Array.from(this.devices.values());
153
- }
154
- return this.allDevicesCache;
155
- }
156
- getPhones() {
157
- if (!this.phonesCache) {
158
- this.phonesCache = this.getAllDevices().filter((d) => d.isPhone);
159
- }
160
- return this.phonesCache;
161
- }
162
- getTablets() {
163
- if (!this.tabletsCache) {
164
- this.tabletsCache = this.getAllDevices().filter((d) => d.isTablet);
165
- }
166
- return this.tabletsCache;
167
- }
168
- isPhone(deviceType) {
169
- const config = this.getDeviceConfig(deviceType);
170
- return config?.isPhone || false;
171
- }
172
- isTablet(deviceType) {
173
- const config = this.getDeviceConfig(deviceType);
174
- return config?.isTablet || false;
175
- }
176
- hasNotch(deviceType) {
177
- const config = this.getDeviceConfig(deviceType);
178
- return config?.hasNotch || false;
179
- }
180
- };
181
-
182
- // src/domain/config/CanvasConfig.ts
183
- var DEFAULT_CANVAS_CONFIG = {
184
- defaultFormat: "png",
185
- defaultQuality: 0.9,
186
- maxDimension: 4096,
187
- scale: 2,
188
- backgroundColor: "#ffffff",
189
- useCORS: true
190
- };
191
-
192
- // src/infrastructure/renderers/CanvasRenderer.ts
193
- var CanvasRenderer = class {
194
- constructor(config = DEFAULT_CANVAS_CONFIG) {
195
- this.config = { ...DEFAULT_CANVAS_CONFIG, ...config };
196
- this.imageCache = /* @__PURE__ */ new Map();
197
- }
198
- /**
199
- * Create a new canvas element
200
- */
201
- createCanvas(dimensions) {
202
- const canvas = document.createElement("canvas");
203
- canvas.width = dimensions.width;
204
- canvas.height = dimensions.height;
205
- return canvas;
206
- }
207
- /**
208
- * Get canvas 2D context
209
- */
210
- getContext(canvas) {
211
- return canvas.getContext("2d");
212
- }
213
- /**
214
- * Apply context configuration
215
- */
216
- configureContext(ctx, config) {
217
- if (config.fillStyle !== void 0) {
218
- ctx.fillStyle = config.fillStyle;
219
- }
220
- if (config.strokeStyle !== void 0) {
221
- ctx.strokeStyle = config.strokeStyle;
222
- }
223
- if (config.lineWidth !== void 0) {
224
- ctx.lineWidth = config.lineWidth;
225
- }
226
- if (config.lineCap !== void 0) {
227
- ctx.lineCap = config.lineCap;
228
- }
229
- if (config.lineJoin !== void 0) {
230
- ctx.lineJoin = config.lineJoin;
231
- }
232
- if (config.globalAlpha !== void 0) {
233
- ctx.globalAlpha = config.globalAlpha;
234
- }
235
- if (config.font !== void 0) {
236
- ctx.font = config.font;
237
- }
238
- if (config.textAlign !== void 0) {
239
- ctx.textAlign = config.textAlign;
240
- }
241
- if (config.textBaseline !== void 0) {
242
- ctx.textBaseline = config.textBaseline;
243
- }
244
- }
245
- /**
246
- * Clear canvas
247
- */
248
- clearCanvas(ctx, dimensions) {
249
- ctx.clearRect(0, 0, dimensions.width, dimensions.height);
250
- }
251
- /**
252
- * Fill rectangle
253
- */
254
- fillRect(ctx, x, y, width, height, color) {
255
- if (color) {
256
- ctx.fillStyle = color;
257
- }
258
- ctx.fillRect(x, y, width, height);
259
- }
260
- /**
261
- * Stroke rectangle
262
- */
263
- strokeRect(ctx, x, y, width, height, color, lineWidth) {
264
- if (color) {
265
- ctx.strokeStyle = color;
266
- }
267
- if (lineWidth !== void 0) {
268
- ctx.lineWidth = lineWidth;
269
- }
270
- ctx.strokeRect(x, y, width, height);
271
- }
272
- /**
273
- * Draw text
274
- */
275
- drawText(ctx, text, x, y, config) {
276
- if (config) {
277
- this.configureContext(ctx, config);
278
- }
279
- ctx.fillText(text, x, y);
280
- }
281
- /**
282
- * Draw image
283
- */
284
- async drawImage(ctx, imageSource, x, y, width, height) {
285
- let img;
286
- if (typeof imageSource === "string") {
287
- img = await this.loadImage(imageSource);
288
- } else {
289
- img = imageSource;
290
- }
291
- if (width && height) {
292
- ctx.drawImage(img, x, y, width, height);
293
- } else {
294
- ctx.drawImage(img, x, y);
295
- }
296
- }
297
- /**
298
- * Load image from URL with caching
299
- */
300
- loadImage(url) {
301
- if (this.imageCache.has(url)) {
302
- return Promise.resolve(this.imageCache.get(url));
303
- }
304
- return new Promise((resolve, reject) => {
305
- const img = new Image();
306
- if (this.config.useCORS) {
307
- img.crossOrigin = "anonymous";
308
- }
309
- img.onload = () => {
310
- this.imageCache.set(url, img);
311
- resolve(img);
312
- };
313
- img.onerror = () => reject(new Error(`Failed to load image: ${url}`));
314
- img.src = url;
315
- });
316
- }
317
- /**
318
- * Clear image cache to free memory
319
- */
320
- clearImageCache() {
321
- this.imageCache.clear();
322
- }
323
- /**
324
- * Get cache size for monitoring
325
- */
326
- getCacheSize() {
327
- return this.imageCache.size;
328
- }
329
- /**
330
- * Create gradient
331
- */
332
- createLinearGradient(ctx, x1, y1, x2, y2, colorStops) {
333
- const gradient = ctx.createLinearGradient(x1, y1, x2, y2);
334
- colorStops.forEach(({ offset, color }) => {
335
- gradient.addColorStop(offset, color);
336
- });
337
- return gradient;
338
- }
339
- /**
340
- * Export canvas to blob
341
- */
342
- async exportToBlob(canvas, options = {}) {
343
- const format = options.format || this.config.defaultFormat;
344
- const quality = options.quality || this.config.defaultQuality;
345
- const scale = options.scale || this.config.scale;
346
- const scaledCanvas = (scale || 1) !== 1 ? this.scaleCanvas(canvas, scale || 1) : canvas;
347
- const mimeType = `image/${format || "png"}`;
348
- const blob = await new Promise((resolve) => {
349
- scaledCanvas.toBlob(
350
- (result) => resolve(result),
351
- mimeType,
352
- quality
353
- );
354
- });
355
- if (!blob) {
356
- throw new Error("Failed to export canvas to blob");
357
- }
358
- const url = URL.createObjectURL(blob);
359
- return {
360
- blob,
361
- url,
362
- dimensions: {
363
- width: scaledCanvas.width,
364
- height: scaledCanvas.height
365
- },
366
- format: format || "png",
367
- size: blob.size
368
- };
369
- }
370
- /**
371
- * Export canvas to data URL
372
- */
373
- exportToDataURL(canvas, format, quality) {
374
- const mimeType = format || `image/${this.config.defaultFormat}`;
375
- const q = quality || this.config.defaultQuality;
376
- return canvas.toDataURL(mimeType, q);
377
- }
378
- /**
379
- * Scale canvas
380
- */
381
- scaleCanvas(canvas, scale) {
382
- const scaled = document.createElement("canvas");
383
- scaled.width = canvas.width * scale;
384
- scaled.height = canvas.height * scale;
385
- const ctx = scaled.getContext("2d");
386
- if (ctx) {
387
- ctx.imageSmoothingEnabled = true;
388
- ctx.imageSmoothingQuality = "high";
389
- ctx.drawImage(canvas, 0, 0, scaled.width, scaled.height);
390
- }
391
- return scaled;
392
- }
393
- /**
394
- * Clone canvas
395
- */
396
- cloneCanvas(canvas) {
397
- const clone = document.createElement("canvas");
398
- clone.width = canvas.width;
399
- clone.height = canvas.height;
400
- const ctx = clone.getContext("2d");
401
- if (ctx) {
402
- ctx.drawImage(canvas, 0, 0);
403
- }
404
- return clone;
405
- }
406
- };
407
-
408
- // src/infrastructure/renderers/PatternRenderer.ts
409
- var PatternRenderer = class {
410
- constructor() {
411
- this.patternCanvasCache = /* @__PURE__ */ new Map();
412
- this.MAX_CACHE_SIZE = 10;
413
- }
414
- /**
415
- * Render pattern on canvas
416
- */
417
- renderPattern(ctx, pattern, dimensions, color = "#000000") {
418
- if (pattern === "none") return;
419
- ctx.save();
420
- ctx.globalAlpha = 0.1;
421
- switch (pattern) {
422
- case "dots":
423
- this.renderDotPattern(ctx, dimensions, color);
424
- break;
425
- case "grid":
426
- this.renderGridPattern(ctx, dimensions, color);
427
- break;
428
- case "lines":
429
- this.renderLinePattern(ctx, dimensions, color);
430
- break;
431
- case "waves":
432
- this.renderWavePattern(ctx, dimensions, color);
433
- break;
434
- case "checkers":
435
- this.renderCheckerPattern(ctx, dimensions, color);
436
- break;
437
- }
438
- ctx.restore();
439
- }
440
- /**
441
- * Clear pattern cache to free memory
442
- */
443
- clearCache() {
444
- this.patternCanvasCache.clear();
445
- }
446
- /**
447
- * Render dot pattern - optimized with batch rendering
448
- */
449
- renderDotPattern(ctx, dimensions, color) {
450
- ctx.fillStyle = color;
451
- const spacing = 30;
452
- const radius = 2;
453
- ctx.beginPath();
454
- for (let x = spacing; x < dimensions.width; x += spacing) {
455
- for (let y = spacing; y < dimensions.height; y += spacing) {
456
- ctx.moveTo(x + radius, y);
457
- ctx.arc(x, y, radius, 0, Math.PI * 2);
458
- }
459
- }
460
- ctx.fill();
461
- }
462
- /**
463
- * Render grid pattern - optimized with single path
464
- */
465
- renderGridPattern(ctx, dimensions, color) {
466
- ctx.strokeStyle = color;
467
- ctx.lineWidth = 1;
468
- const spacing = 30;
469
- ctx.beginPath();
470
- for (let x = 0; x <= dimensions.width; x += spacing) {
471
- ctx.moveTo(x, 0);
472
- ctx.lineTo(x, dimensions.height);
473
- }
474
- for (let y = 0; y <= dimensions.height; y += spacing) {
475
- ctx.moveTo(0, y);
476
- ctx.lineTo(dimensions.width, y);
477
- }
478
- ctx.stroke();
479
- }
480
- /**
481
- * Render line pattern - optimized with single path
482
- */
483
- renderLinePattern(ctx, dimensions, color) {
484
- ctx.strokeStyle = color;
485
- ctx.lineWidth = 2;
486
- const spacing = 20;
487
- ctx.beginPath();
488
- for (let y = spacing; y < dimensions.height; y += spacing) {
489
- ctx.moveTo(0, y);
490
- ctx.lineTo(dimensions.width, y);
491
- }
492
- ctx.stroke();
493
- }
494
- /**
495
- * Render wave pattern - optimized with pre-calculated values
496
- */
497
- renderWavePattern(ctx, dimensions, color) {
498
- ctx.strokeStyle = color;
499
- ctx.lineWidth = 2;
500
- const amplitude = 10;
501
- const frequency = 0.1;
502
- const spacing = 40;
503
- const step = 5;
504
- for (let y = spacing; y < dimensions.height; y += spacing) {
505
- ctx.beginPath();
506
- const startX = 0;
507
- const startY = y + Math.sin(startX * frequency) * amplitude;
508
- ctx.moveTo(startX, startY);
509
- for (let x = step; x <= dimensions.width; x += step) {
510
- const waveY = y + Math.sin(x * frequency) * amplitude;
511
- ctx.lineTo(x, waveY);
512
- }
513
- ctx.stroke();
514
- }
515
- }
516
- /**
517
- * Render checker pattern
518
- */
519
- renderCheckerPattern(ctx, dimensions, color) {
520
- ctx.fillStyle = color;
521
- const size = 20;
522
- for (let x = 0; x < dimensions.width; x += size * 2) {
523
- for (let y = 0; y < dimensions.height; y += size * 2) {
524
- ctx.fillRect(x, y, size, size);
525
- ctx.fillRect(x + size, y + size, size, size);
526
- }
527
- }
528
- }
529
- };
530
-
531
- // src/infrastructure/renderers/ScreenshotRenderer.ts
532
- var ScreenshotRenderer = class {
533
- constructor() {
534
- this.renderCache = /* @__PURE__ */ new Map();
535
- this.MAX_CACHE_SIZE = 5;
536
- this.canvasRenderer = new CanvasRenderer();
537
- this.patternRenderer = new PatternRenderer();
538
- this.deviceConfigService = DeviceConfigService.getInstance();
539
- }
540
- /**
541
- * Clear render cache to free memory
542
- */
543
- clearCache() {
544
- this.renderCache.clear();
545
- this.canvasRenderer.clearImageCache?.();
546
- this.patternRenderer.clearCache?.();
547
- }
548
- /**
549
- * Render device frame
550
- */
551
- renderDeviceFrame(ctx, dimensions, deviceType, frameStyle, frameColor) {
552
- const deviceConfig = this.deviceConfigService.getDeviceConfig(deviceType);
553
- if (!deviceConfig) return;
554
- if (frameStyle === "none") return;
555
- const isPhone = this.deviceConfigService.isPhone(deviceType);
556
- const isTablet = this.deviceConfigService.isTablet(deviceType);
557
- if (!isPhone && !isTablet) return;
558
- const frameThickness = deviceConfig.frameThickness || 8;
559
- if (frameStyle === "realistic") {
560
- this.renderRealisticFrame(ctx, dimensions, frameColor, frameThickness);
561
- if (isPhone && this.deviceConfigService.hasNotch(deviceType)) {
562
- this.renderNotch(ctx, dimensions);
563
- }
564
- } else if (frameStyle === "minimal") {
565
- this.renderMinimalFrame(ctx, dimensions, frameColor, frameThickness);
566
- }
567
- }
568
- /**
569
- * Render realistic device frame
570
- */
571
- renderRealisticFrame(ctx, dimensions, color, thickness) {
572
- ctx.strokeStyle = color;
573
- ctx.lineWidth = thickness;
574
- ctx.strokeRect(
575
- thickness / 2,
576
- thickness / 2,
577
- dimensions.width - thickness,
578
- dimensions.height - thickness
579
- );
580
- }
581
- /**
582
- * Render minimal device frame
583
- */
584
- renderMinimalFrame(ctx, dimensions, color, thickness) {
585
- ctx.strokeStyle = color;
586
- ctx.lineWidth = thickness;
587
- ctx.strokeRect(0, 0, dimensions.width, dimensions.height);
588
- }
589
- /**
590
- * Render device notch
591
- */
592
- renderNotch(ctx, dimensions) {
593
- ctx.fillStyle = "#1f2937";
594
- ctx.beginPath();
595
- ctx.arc(dimensions.width / 2, 20, 10, 0, Math.PI * 2);
596
- ctx.fill();
597
- }
598
- /**
599
- * Render background - optimized
600
- */
601
- renderBackground(ctx, design, dimensions) {
602
- if (design.backgroundGradientEnd) {
603
- const gradient = this.canvasRenderer.createLinearGradient(
604
- ctx,
605
- 0,
606
- 0,
607
- dimensions.width,
608
- dimensions.height,
609
- [
610
- { offset: 0, color: design.backgroundColor },
611
- { offset: 1, color: design.backgroundGradientEnd }
612
- ]
613
- );
614
- ctx.fillStyle = gradient;
615
- } else {
616
- ctx.fillStyle = design.backgroundColor;
617
- }
618
- ctx.fillRect(0, 0, dimensions.width, dimensions.height);
619
- if (design.backgroundPattern && design.backgroundPattern !== "none") {
620
- this.patternRenderer.renderPattern(
621
- ctx,
622
- design.backgroundPattern,
623
- dimensions
624
- );
625
- }
626
- }
627
- /**
628
- * Render text content
629
- */
630
- renderTextContent(ctx, content, design, dimensions) {
631
- const titleConfig = {
632
- fillStyle: design.textColor,
633
- font: `bold ${design.titleFontSize}px ${design.fontFamily}`,
634
- textAlign: design.textAlign
635
- };
636
- const subtitleConfig = {
637
- fillStyle: design.textColor,
638
- font: `${design.subtitleFontSize}px ${design.fontFamily}`,
639
- textAlign: design.textAlign
640
- };
641
- const titleY = design.layout === "text-bottom" ? dimensions.height - 150 : 100 + (design.offsetY || 0);
642
- this.canvasRenderer.drawText(
643
- ctx,
644
- this.formatText(content.title, design.textCase),
645
- dimensions.width / 2,
646
- titleY,
647
- titleConfig
648
- );
649
- const subtitleY = titleY + design.titleFontSize + (design.gap || 20);
650
- this.canvasRenderer.drawText(
651
- ctx,
652
- this.formatText(content.subtitle, design.textCase),
653
- dimensions.width / 2,
654
- subtitleY,
655
- subtitleConfig
656
- );
657
- }
658
- /**
659
- * Format text case
660
- */
661
- formatText(text, textCase = "none") {
662
- switch (textCase) {
663
- case "uppercase":
664
- return text.toUpperCase();
665
- case "lowercase":
666
- return text.toLowerCase();
667
- default:
668
- return text;
669
- }
670
- }
671
- /**
672
- * Render screenshot image
673
- */
674
- async renderScreenshotImage(ctx, imageUrl, design, dimensions) {
675
- if (!imageUrl) return;
676
- const deviceConfig = this.deviceConfigService.getDeviceConfig(
677
- design.deviceType
678
- );
679
- if (!deviceConfig) return;
680
- const frameThickness = deviceConfig.frameThickness || 8;
681
- const padding = 60 + frameThickness;
682
- const screenshotWidth = dimensions.width - padding * 2;
683
- const screenshotHeight = dimensions.height - padding * 2;
684
- try {
685
- await this.canvasRenderer.drawImage(
686
- ctx,
687
- imageUrl,
688
- padding,
689
- padding,
690
- screenshotWidth,
691
- screenshotHeight
692
- );
693
- } catch (error) {
694
- console.error("Failed to render screenshot image:", error);
695
- }
696
- }
697
- /**
698
- * Render complete screenshot
699
- */
700
- async renderDesignedScreenshot(content, design) {
701
- const deviceConfig = this.deviceConfigService.getDeviceConfig(design.deviceType);
702
- if (!deviceConfig) {
703
- throw new Error(`Device config not found: ${design.deviceType}`);
704
- }
705
- const dimensions = deviceConfig.dimensions;
706
- const canvas = this.canvasRenderer.createCanvas(dimensions);
707
- const ctx = canvas.getContext("2d");
708
- if (!ctx) {
709
- throw new Error("Failed to get canvas context");
710
- }
711
- this.renderBackground(ctx, design, dimensions);
712
- if (content.screenshot) {
713
- await this.renderScreenshotImage(ctx, content.screenshot, design, dimensions);
714
- }
715
- this.renderTextContent(ctx, content, design, dimensions);
716
- this.renderDeviceFrame(
717
- ctx,
718
- dimensions,
719
- design.deviceType,
720
- design.frameStyle,
721
- design.deviceColor
722
- );
723
- return canvas;
724
- }
725
- /**
726
- * Render placeholder content
727
- */
728
- renderPlaceholderContent(ctx, dimensions, deviceType) {
729
- this.canvasRenderer.fillRect(ctx, 0, 0, dimensions.width, dimensions.height, "#ffffff");
730
- this.canvasRenderer.fillRect(ctx, 0, 0, dimensions.width, 60, "#f3f4f6");
731
- this.canvasRenderer.fillRect(
732
- ctx,
733
- 0,
734
- dimensions.height - 80,
735
- dimensions.width,
736
- 80,
737
- "#f3f4f6"
738
- );
739
- ctx.fillStyle = "#e5e7eb";
740
- for (let i = 0; i < 5; i++) {
741
- const y = 80 + i * 120;
742
- const height = Math.min(100, dimensions.height - y - 100);
743
- if (y < dimensions.height - 100) {
744
- this.canvasRenderer.fillRect(ctx, 20, y, dimensions.width - 40, height);
745
- }
746
- }
747
- const deviceConfig = this.deviceConfigService.getDeviceConfig(deviceType);
748
- if (deviceConfig) {
749
- this.renderDeviceFrame(
750
- ctx,
751
- dimensions,
752
- deviceType,
753
- "realistic",
754
- "#1f2937"
755
- );
756
- }
757
- }
758
- };
759
- // Annotate the CommonJS export names for ESM import in node:
760
- 0 && (module.exports = {
761
- CanvasRenderer,
762
- DeviceConfigService,
763
- PatternRenderer,
764
- ScreenshotRenderer
765
- });