@cognima/banners 0.0.1-beta

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 (48) hide show
  1. package/assets/fonts/Manrope/Manrope-Bold.ttf +0 -0
  2. package/assets/fonts/Manrope/Manrope-Regular.ttf +0 -0
  3. package/assets/fonts/Others/AbyssinicaSIL-Regular.ttf +0 -0
  4. package/assets/fonts/Others/ChirpRegular.ttf +0 -0
  5. package/assets/fonts/Poppins/Poppins-Bold.ttf +0 -0
  6. package/assets/fonts/Poppins/Poppins-Medium.ttf +0 -0
  7. package/assets/fonts/Poppins/Poppins-Regular.ttf +0 -0
  8. package/assets/placeholders/album_art.png +0 -0
  9. package/assets/placeholders/avatar.png +0 -0
  10. package/assets/placeholders/badge.jpg +0 -0
  11. package/assets/placeholders/badge.png +0 -0
  12. package/assets/placeholders/badge_2.jpg +0 -0
  13. package/assets/placeholders/badge_3.jpg +0 -0
  14. package/assets/placeholders/badge_4.jpg +0 -0
  15. package/assets/placeholders/badge_5.jpg +0 -0
  16. package/assets/placeholders/banner.jpeg +0 -0
  17. package/assets/placeholders/images.jpeg +0 -0
  18. package/index.js +153 -0
  19. package/package.json +34 -0
  20. package/src/animation-effects.js +631 -0
  21. package/src/cache-manager.js +258 -0
  22. package/src/community-banner.js +1536 -0
  23. package/src/constants.js +208 -0
  24. package/src/discord-profile.js +584 -0
  25. package/src/e-commerce-banner.js +1214 -0
  26. package/src/effects.js +355 -0
  27. package/src/error-handler.js +305 -0
  28. package/src/event-banner.js +1319 -0
  29. package/src/facebook-post.js +679 -0
  30. package/src/gradient-welcome.js +430 -0
  31. package/src/image-filters.js +1034 -0
  32. package/src/image-processor.js +1014 -0
  33. package/src/instagram-post.js +504 -0
  34. package/src/interactive-elements.js +1208 -0
  35. package/src/linkedin-post.js +658 -0
  36. package/src/marketing-banner.js +1089 -0
  37. package/src/minimalist-banner.js +892 -0
  38. package/src/modern-profile.js +755 -0
  39. package/src/performance-optimizer.js +216 -0
  40. package/src/telegram-header.js +544 -0
  41. package/src/test-runner.js +645 -0
  42. package/src/tiktok-post.js +713 -0
  43. package/src/twitter-header.js +604 -0
  44. package/src/validator.js +442 -0
  45. package/src/welcome-leave.js +445 -0
  46. package/src/whatsapp-status.js +386 -0
  47. package/src/youtube-thumbnail.js +681 -0
  48. package/utils.js +710 -0
@@ -0,0 +1,386 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Módulo de Banner de Status do WhatsApp
5
+ *
6
+ * Este módulo gera banners no estilo de status do WhatsApp com texto personalizado,
7
+ * gradientes e efeitos visuais.
8
+ *
9
+ * @author Cognima Team (melhorado)
10
+ * @version 2.0.0
11
+ */
12
+
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+
15
+ const pureimage = require("pureimage");
16
+ const path = require("path");
17
+ const {
18
+ loadImageWithAxios,
19
+ encodeToBuffer,
20
+ roundRect,
21
+ wrapText,
22
+ registerFontIfNeeded,
23
+ isValidHexColor,
24
+ DEFAULT_FONT_FAMILY,
25
+ applyTextShadow,
26
+ clearShadow,
27
+ createLinearGradient,
28
+ hexToRgba
29
+ } = require("../utils");
30
+
31
+ /**
32
+ * @class WhatsAppStatus
33
+ * @classdesc Gera um banner no estilo de status do WhatsApp.
34
+ * @example const statusCard = new WhatsAppStatus()
35
+ * .setText("Bom dia a todos!")
36
+ * .setBackground("image", "background.jpg")
37
+ * .setTextColor("#FFFFFF")
38
+ * .build();
39
+ */
40
+ module.exports = class WhatsAppStatus {
41
+ constructor(options) {
42
+ // Dados Principais
43
+ this.text = "Digite seu status aqui";
44
+ this.author = null;
45
+ this.authorAvatar = null;
46
+ this.timestamp = new Date().toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });
47
+
48
+ // Personalização Visual
49
+ this.font = { name: options?.font?.name ?? DEFAULT_FONT_FAMILY, path: options?.font?.path };
50
+ this.background = { type: "color", value: "#128C7E" }; // Cor padrão do WhatsApp
51
+ this.textColor = "#FFFFFF";
52
+ this.textAlign = "center";
53
+ this.textSize = 32;
54
+ this.overlayOpacity = 0.3;
55
+ this.useTextShadow = true;
56
+ this.useEmoji = false;
57
+ this.emoji = "❤️";
58
+
59
+ // Configurações de Layout
60
+ this.cardWidth = 720;
61
+ this.cardHeight = 1280;
62
+ this.cornerRadius = 0; // Status do WhatsApp não tem cantos arredondados
63
+ this.padding = 40;
64
+ }
65
+
66
+ // --- Setters para Dados Principais ---
67
+ /**
68
+ * Define o texto do status
69
+ * @param {string} text - Texto do status
70
+ * @returns {WhatsAppStatus} - Instância atual para encadeamento
71
+ */
72
+ setText(text) {
73
+ if (!text || typeof text !== "string") throw new Error("O texto do status deve ser uma string não vazia.");
74
+ this.text = text;
75
+ return this;
76
+ }
77
+
78
+ /**
79
+ * Define o autor do status
80
+ * @param {string} name - Nome do autor
81
+ * @returns {WhatsAppStatus} - Instância atual para encadeamento
82
+ */
83
+ setAuthor(name) {
84
+ if (!name || typeof name !== "string") throw new Error("O nome do autor deve ser uma string não vazia.");
85
+ this.author = name;
86
+ return this;
87
+ }
88
+
89
+ /**
90
+ * Define o avatar do autor
91
+ * @param {string|Buffer|Object} image - URL, Buffer ou caminho da imagem do avatar
92
+ * @returns {WhatsAppStatus} - Instância atual para encadeamento
93
+ */
94
+ setAuthorAvatar(image) {
95
+ if (!image) throw new Error("A fonte da imagem do avatar não pode estar vazia.");
96
+ this.authorAvatar = image;
97
+ return this;
98
+ }
99
+
100
+ /**
101
+ * Define o timestamp do status
102
+ * @param {string} time - Hora do status (ex: "14:30")
103
+ * @returns {WhatsAppStatus} - Instância atual para encadeamento
104
+ */
105
+ setTimestamp(time) {
106
+ if (!time || typeof time !== "string") throw new Error("O timestamp deve ser uma string não vazia.");
107
+ this.timestamp = time;
108
+ return this;
109
+ }
110
+
111
+ // --- Setters para Personalização Visual ---
112
+ /**
113
+ * Define o plano de fundo do status
114
+ * @param {string} type - Tipo de plano de fundo ('color' ou 'image')
115
+ * @param {string} value - Valor do plano de fundo (cor hexadecimal ou URL/caminho da imagem)
116
+ * @returns {WhatsAppStatus} - Instância atual para encadeamento
117
+ */
118
+ setBackground(type, value) {
119
+ const types = ["color", "image"];
120
+ if (!type || !types.includes(type.toLowerCase())) throw new Error("O tipo de plano de fundo deve ser 'color' ou 'image'.");
121
+ if (!value) throw new Error("O valor do plano de fundo não pode estar vazio.");
122
+ if (type.toLowerCase() === "color" && !isValidHexColor(value)) throw new Error("Cor de plano de fundo inválida. Use o formato hexadecimal.");
123
+ this.background = { type: type.toLowerCase(), value };
124
+ return this;
125
+ }
126
+
127
+ /**
128
+ * Define a cor do texto
129
+ * @param {string} color - Cor hexadecimal
130
+ * @returns {WhatsAppStatus} - Instância atual para encadeamento
131
+ */
132
+ setTextColor(color) {
133
+ if (!color || !isValidHexColor(color)) throw new Error("Cor de texto inválida. Use o formato hexadecimal.");
134
+ this.textColor = color;
135
+ return this;
136
+ }
137
+
138
+ /**
139
+ * Define o alinhamento do texto
140
+ * @param {string} align - Alinhamento do texto ('left', 'center', 'right')
141
+ * @returns {WhatsAppStatus} - Instância atual para encadeamento
142
+ */
143
+ setTextAlign(align) {
144
+ const validAlignments = ["left", "center", "right"];
145
+ if (!align || !validAlignments.includes(align.toLowerCase())) {
146
+ throw new Error(`Alinhamento de texto inválido. Use um dos seguintes: ${validAlignments.join(", ")}`);
147
+ }
148
+
149
+ this.textAlign = align.toLowerCase();
150
+ return this;
151
+ }
152
+
153
+ /**
154
+ * Define o tamanho do texto
155
+ * @param {number} size - Tamanho do texto em pixels
156
+ * @returns {WhatsAppStatus} - Instância atual para encadeamento
157
+ */
158
+ setTextSize(size) {
159
+ if (typeof size !== "number" || size < 16 || size > 72) {
160
+ throw new Error("O tamanho do texto deve estar entre 16 e 72 pixels.");
161
+ }
162
+
163
+ this.textSize = size;
164
+ return this;
165
+ }
166
+
167
+ /**
168
+ * Define a opacidade da sobreposição
169
+ * @param {number} opacity - Valor de opacidade (0-1)
170
+ * @returns {WhatsAppStatus} - Instância atual para encadeamento
171
+ */
172
+ setOverlayOpacity(opacity) {
173
+ if (typeof opacity !== "number" || opacity < 0 || opacity > 1) throw new Error("A opacidade da sobreposição deve estar entre 0 e 1.");
174
+ this.overlayOpacity = opacity;
175
+ return this;
176
+ }
177
+
178
+ /**
179
+ * Ativa ou desativa a sombra de texto
180
+ * @param {boolean} enabled - Se a sombra de texto deve ser ativada
181
+ * @returns {WhatsAppStatus} - Instância atual para encadeamento
182
+ */
183
+ enableTextShadow(enabled = true) {
184
+ this.useTextShadow = enabled;
185
+ return this;
186
+ }
187
+
188
+ /**
189
+ * Ativa ou desativa o emoji flutuante
190
+ * @param {boolean} enabled - Se o emoji deve ser ativado
191
+ * @param {string} emoji - Emoji a ser usado
192
+ * @returns {WhatsAppStatus} - Instância atual para encadeamento
193
+ */
194
+ enableEmoji(enabled = true, emoji = "❤️") {
195
+ this.useEmoji = enabled;
196
+
197
+ if (emoji && typeof emoji === "string") {
198
+ this.emoji = emoji;
199
+ }
200
+
201
+ return this;
202
+ }
203
+
204
+ /**
205
+ * Define as dimensões do card
206
+ * @param {number} width - Largura do card em pixels
207
+ * @param {number} height - Altura do card em pixels
208
+ * @returns {WhatsAppStatus} - Instância atual para encadeamento
209
+ */
210
+ setCardDimensions(width, height) {
211
+ if (typeof width !== "number" || width < 400 || width > 1080) {
212
+ throw new Error("A largura do card deve estar entre 400 e 1080 pixels.");
213
+ }
214
+
215
+ if (typeof height !== "number" || height < 600 || height > 1920) {
216
+ throw new Error("A altura do card deve estar entre 600 e 1920 pixels.");
217
+ }
218
+
219
+ this.cardWidth = width;
220
+ this.cardHeight = height;
221
+
222
+ return this;
223
+ }
224
+
225
+ // --- Método de Construção ---
226
+ /**
227
+ * Constrói o banner e retorna um buffer de imagem
228
+ * @returns {Promise<Buffer>} - Buffer contendo a imagem do banner
229
+ */
230
+ async build() {
231
+ // --- Registro de Fonte ---
232
+ const registeredFontName = await registerFontIfNeeded(this.font);
233
+
234
+ // --- Configuração do Canvas ---
235
+ const cardWidth = this.cardWidth;
236
+ const cardHeight = this.cardHeight;
237
+ const padding = this.padding;
238
+
239
+ const canvas = pureimage.make(cardWidth, cardHeight);
240
+ const ctx = canvas.getContext("2d");
241
+
242
+ // --- Desenha Plano de Fundo ---
243
+ if (this.background.type === "color") {
244
+ // Plano de fundo de cor sólida
245
+ ctx.fillStyle = this.background.value;
246
+ ctx.fillRect(0, 0, cardWidth, cardHeight);
247
+ } else {
248
+ // Plano de fundo de imagem
249
+ try {
250
+ const img = await loadImageWithAxios(this.background.value);
251
+ const aspect = img.width / img.height;
252
+ let drawWidth = cardWidth;
253
+ let drawHeight = cardWidth / aspect;
254
+
255
+ // Ajusta as dimensões para cobrir todo o card
256
+ if (drawHeight < cardHeight) {
257
+ drawHeight = cardHeight;
258
+ drawWidth = cardHeight * aspect;
259
+ }
260
+
261
+ const offsetX = (cardWidth - drawWidth) / 2;
262
+ const offsetY = (cardHeight - drawHeight) / 2;
263
+
264
+ ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
265
+
266
+ // Aplica sobreposição para melhorar legibilidade do texto
267
+ ctx.globalAlpha = this.overlayOpacity;
268
+ ctx.fillStyle = "#000000";
269
+ ctx.fillRect(0, 0, cardWidth, cardHeight);
270
+ ctx.globalAlpha = 1;
271
+ } catch (e) {
272
+ console.error("Falha ao desenhar imagem de plano de fundo:", e.message);
273
+ ctx.fillStyle = "#128C7E"; // Cor padrão do WhatsApp
274
+ ctx.fillRect(0, 0, cardWidth, cardHeight);
275
+ }
276
+ }
277
+
278
+ // --- Desenha Emoji Flutuante (se ativado) ---
279
+ if (this.useEmoji) {
280
+ const emojiSize = Math.min(cardWidth, cardHeight) * 0.4;
281
+ ctx.globalAlpha = 0.2;
282
+ ctx.font = `${emojiSize}px Arial`;
283
+ ctx.textAlign = "center";
284
+ ctx.textBaseline = "middle";
285
+ ctx.fillText(this.emoji, cardWidth / 2, cardHeight / 2);
286
+ wrapText(ctx, this.emoji, cardWidth / 2, cardHeight / 2, cardWidth - (padding * 2), this.textSize * 1.2, registeredFontName);
287
+ ctx.globalAlpha = 1;
288
+ }
289
+
290
+ // --- Desenha Texto Principal ---
291
+ ctx.fillStyle = this.textColor;
292
+ ctx.font = `bold ${this.textSize}px ${registeredFontName}-Bold`;
293
+ ctx.textAlign = this.textAlign;
294
+ ctx.textBaseline = "middle";
295
+
296
+ // Aplica sombra de texto se ativada
297
+ if (this.useTextShadow) {
298
+ applyTextShadow(ctx, "rgba(0, 0, 0, 0.5)", 3, 2, 2);
299
+ }
300
+
301
+ // Calcula a posição Y do texto com base no alinhamento
302
+ const textY = cardHeight / 2;
303
+
304
+ // Calcula a posição X do texto com base no alinhamento
305
+ let textX;
306
+ switch (this.textAlign) {
307
+ case "left":
308
+ textX = padding;
309
+ break;
310
+ case "right":
311
+ textX = cardWidth - padding;
312
+ break;
313
+ case "center":
314
+ default:
315
+ textX = cardWidth / 2;
316
+ break;
317
+ }
318
+
319
+ // Desenha o texto com quebra de linha
320
+ wrapText(ctx, this.text, textX, textY, cardWidth - (padding * 2), this.textSize * 1.2, registeredFontName);
321
+
322
+ // Remove sombra
323
+ if (this.useTextShadow) {
324
+ clearShadow(ctx);
325
+ }
326
+
327
+ // --- Desenha Informações do Autor (se fornecidas) ---
328
+ if (this.author || this.authorAvatar) {
329
+ const authorAreaHeight = 60;
330
+ const authorAreaY = cardHeight - authorAreaHeight - padding;
331
+ const avatarSize = 40;
332
+
333
+ // Desenha o fundo semi-transparente para a área do autor
334
+ ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
335
+ roundRect(ctx, padding, authorAreaY, cardWidth - (padding * 2), authorAreaHeight, 10, true, false);
336
+
337
+ let authorX = padding + 10;
338
+
339
+ // Desenha o avatar do autor (se fornecido)
340
+ if (this.authorAvatar) {
341
+ try {
342
+ ctx.save();
343
+ ctx.beginPath();
344
+ ctx.arc(authorX + avatarSize / 2, authorAreaY + authorAreaHeight / 2, avatarSize / 2, 0, Math.PI * 2);
345
+ ctx.closePath();
346
+ ctx.clip();
347
+
348
+ const avatarImg = await loadImageWithAxios(this.authorAvatar);
349
+ ctx.drawImage(avatarImg, authorX, authorAreaY + (authorAreaHeight - avatarSize) / 2, avatarSize, avatarSize);
350
+
351
+ ctx.restore();
352
+
353
+ authorX += avatarSize + 10;
354
+ } catch (e) {
355
+ console.error("Falha ao desenhar avatar do autor:", e.message);
356
+ authorX = padding + 10;
357
+ }
358
+ }
359
+
360
+ // Desenha o nome do autor (se fornecido)
361
+ if (this.author) {
362
+ ctx.fillStyle = "#FFFFFF";
363
+ ctx.font = `medium 18px ${registeredFontName}-Medium`;
364
+ ctx.textAlign = "left";
365
+ ctx.textBaseline = "middle";
366
+ ctx.fillText(this.author, authorX, authorAreaY + authorAreaHeight / 2 - 5);
367
+ wrapText(ctx, this.author, authorX, authorAreaY + authorAreaHeight / 2 - 5, cardWidth - (padding * 2), this.textSize * 1.2, registeredFontName);
368
+
369
+ // Desenha o timestamp
370
+ ctx.fillStyle = "rgba(255, 255, 255, 0.7)";
371
+ ctx.font = `regular 14px ${registeredFontName}-Regular`;
372
+ ctx.fillText(this.timestamp, authorX, authorAreaY + authorAreaHeight / 2 + 15);
373
+ wrapText(ctx, this.timestamp, authorX, authorAreaY + authorAreaHeight / 2 + 15, cardWidth - (padding * 2), this.textSize * 1.2, registeredFontName);
374
+ }
375
+ }
376
+
377
+ // --- Codifica e Retorna Buffer ---
378
+ try {
379
+ return await encodeToBuffer(canvas);
380
+ } catch (err) {
381
+ console.error("Falha ao codificar o card de Status do WhatsApp:", err);
382
+ throw new Error("Não foi possível gerar o buffer de imagem do card de Status do WhatsApp.");
383
+ }
384
+ }
385
+ };
386
+