@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,445 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Módulo de Banner de Boas-vindas/Saída
5
+ *
6
+ * Este módulo gera banners de boas-vindas ou despedida com avatar centralizado
7
+ * e mensagem personalizada.
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 WelcomeLeave
33
+ * @classdesc Gera um banner de boas-vindas ou despedida visualmente refinado com avatar central.
34
+ * @example const welcomeCard = new WelcomeLeave()
35
+ * .setAvatar("avatar.png")
36
+ * .setTitle("Bem-vindo!")
37
+ * .setUsername("NovoUsuário")
38
+ * .setMessage("Ficamos felizes em ter você aqui!")
39
+ * .build();
40
+ */
41
+ module.exports = class WelcomeLeave {
42
+ constructor(options) {
43
+ // Dados Principais
44
+ this.avatar = null;
45
+ this.title = "Bem-vindo!";
46
+ this.username = "Usuário";
47
+ this.message = "Texto da mensagem aqui.";
48
+
49
+ // Personalização Visual
50
+ this.font = { name: options?.font?.name ?? DEFAULT_FONT_FAMILY, path: options?.font?.path };
51
+ this.background = { type: "color", value: "#2C2F33" };
52
+ this.titleColor = "#FFFFFF";
53
+ this.usernameColor = "#FFFFFF";
54
+ this.messageColor = "#B9BBBE";
55
+ this.avatarBorderColor = null;
56
+ this.cardBorderColor = null;
57
+ this.overlayOpacity = 0.3;
58
+
59
+ // Novas opções de personalização
60
+ this.useTextShadow = false;
61
+ this.useGradient = false;
62
+ this.gradientColors = { start: "#3498db", end: "#8e44ad" };
63
+ this.gradientDirection = "vertical";
64
+ this.cornerRadius = 0; // Raio dos cantos arredondados
65
+ this.avatarBorderWidth = 4; // Largura da borda do avatar
66
+ }
67
+
68
+ // --- Setters para Dados Principais ---
69
+ /**
70
+ * Define a imagem do avatar
71
+ * @param {string|Buffer|Object} image - URL, Buffer ou caminho da imagem do avatar
72
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
73
+ */
74
+ setAvatar(image) {
75
+ if (!image) throw new Error("A fonte da imagem do avatar não pode estar vazia.");
76
+ this.avatar = image;
77
+ return this;
78
+ }
79
+
80
+ /**
81
+ * Define o título do banner
82
+ * @param {string} text - Texto do título
83
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
84
+ */
85
+ setTitle(text) {
86
+ if (!text || typeof text !== "string") throw new Error("O texto do título deve ser uma string não vazia.");
87
+ this.title = text;
88
+ return this;
89
+ }
90
+
91
+ /**
92
+ * Define o nome de usuário
93
+ * @param {string} name - Nome do usuário
94
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
95
+ */
96
+ setUsername(name) {
97
+ if (!name || typeof name !== "string") throw new Error("O nome de usuário deve ser uma string não vazia.");
98
+ this.username = name;
99
+ return this;
100
+ }
101
+
102
+ /**
103
+ * Define o texto da mensagem
104
+ * @param {string} text - Texto da mensagem
105
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
106
+ */
107
+ setMessage(text) {
108
+ if (!text || typeof text !== "string") throw new Error("O texto da mensagem deve ser uma string não vazia.");
109
+ this.message = text;
110
+ return this;
111
+ }
112
+
113
+ // --- Setters para Personalização Visual ---
114
+ /**
115
+ * Define o plano de fundo do banner
116
+ * @param {string} type - Tipo de plano de fundo ('color' ou 'image')
117
+ * @param {string} value - Valor do plano de fundo (cor hexadecimal ou URL/caminho da imagem)
118
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
119
+ */
120
+ setBackground(type, value) {
121
+ const types = ["color", "image"];
122
+ if (!type || !types.includes(type.toLowerCase())) throw new Error("O tipo de plano de fundo deve ser 'color' ou 'image'.");
123
+ if (!value) throw new Error("O valor do plano de fundo não pode estar vazio.");
124
+ if (type.toLowerCase() === "color" && !isValidHexColor(value)) throw new Error("Cor de plano de fundo inválida. Use o formato hexadecimal.");
125
+ this.background = { type: type.toLowerCase(), value };
126
+ return this;
127
+ }
128
+
129
+ /**
130
+ * Define a cor do título
131
+ * @param {string} color - Cor hexadecimal
132
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
133
+ */
134
+ setTitleColor(color) {
135
+ if (!color || !isValidHexColor(color)) throw new Error("Cor de título inválida. Use o formato hexadecimal.");
136
+ this.titleColor = color;
137
+ return this;
138
+ }
139
+
140
+ /**
141
+ * Define a cor do nome de usuário
142
+ * @param {string} color - Cor hexadecimal
143
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
144
+ */
145
+ setUsernameColor(color) {
146
+ if (!color || !isValidHexColor(color)) throw new Error("Cor de nome de usuário inválida. Use o formato hexadecimal.");
147
+ this.usernameColor = color;
148
+ return this;
149
+ }
150
+
151
+ /**
152
+ * Define a cor da mensagem
153
+ * @param {string} color - Cor hexadecimal
154
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
155
+ */
156
+ setMessageColor(color) {
157
+ if (!color || !isValidHexColor(color)) throw new Error("Cor de mensagem inválida. Use o formato hexadecimal.");
158
+ this.messageColor = color;
159
+ return this;
160
+ }
161
+
162
+ /**
163
+ * Define a cor da borda do avatar
164
+ * @param {string} color - Cor hexadecimal
165
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
166
+ */
167
+ setAvatarBorderColor(color) {
168
+ if (color && !isValidHexColor(color)) throw new Error("Cor de borda do avatar inválida. Use o formato hexadecimal.");
169
+ this.avatarBorderColor = color;
170
+ return this;
171
+ }
172
+
173
+ /**
174
+ * Define a cor da borda do card
175
+ * @param {string} color - Cor hexadecimal
176
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
177
+ */
178
+ setCardBorderColor(color) {
179
+ if (color && !isValidHexColor(color)) throw new Error("Cor de borda do card inválida. Use o formato hexadecimal.");
180
+ this.cardBorderColor = color;
181
+ return this;
182
+ }
183
+
184
+ /**
185
+ * Define a opacidade da sobreposição
186
+ * @param {number} opacity - Valor de opacidade (0-1)
187
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
188
+ */
189
+ setOverlayOpacity(opacity) {
190
+ if (typeof opacity !== "number" || opacity < 0 || opacity > 1) throw new Error("A opacidade da sobreposição deve estar entre 0 e 1.");
191
+ this.overlayOpacity = opacity;
192
+ return this;
193
+ }
194
+
195
+ /**
196
+ * Ativa ou desativa a sombra de texto
197
+ * @param {boolean} enabled - Se a sombra de texto deve ser ativada
198
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
199
+ */
200
+ enableTextShadow(enabled = true) {
201
+ this.useTextShadow = enabled;
202
+ return this;
203
+ }
204
+
205
+ /**
206
+ * Ativa ou desativa o gradiente de fundo
207
+ * @param {boolean} enabled - Se o gradiente deve ser ativado
208
+ * @param {string} startColor - Cor inicial do gradiente (hexadecimal)
209
+ * @param {string} endColor - Cor final do gradiente (hexadecimal)
210
+ * @param {string} direction - Direção do gradiente ('vertical', 'horizontal', 'diagonal')
211
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
212
+ */
213
+ enableGradient(enabled = true, startColor = "#3498db", endColor = "#8e44ad", direction = "vertical") {
214
+ this.useGradient = enabled;
215
+
216
+ if (startColor && isValidHexColor(startColor)) {
217
+ this.gradientColors.start = startColor;
218
+ }
219
+
220
+ if (endColor && isValidHexColor(endColor)) {
221
+ this.gradientColors.end = endColor;
222
+ }
223
+
224
+ const validDirections = ["vertical", "horizontal", "diagonal"];
225
+ if (direction && validDirections.includes(direction.toLowerCase())) {
226
+ this.gradientDirection = direction.toLowerCase();
227
+ }
228
+
229
+ return this;
230
+ }
231
+
232
+ /**
233
+ * Define o raio dos cantos arredondados
234
+ * @param {number} radius - Raio dos cantos em pixels
235
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
236
+ */
237
+ setCornerRadius(radius) {
238
+ if (typeof radius !== "number" || radius < 0) throw new Error("O raio dos cantos deve ser um número não negativo.");
239
+ this.cornerRadius = radius;
240
+ return this;
241
+ }
242
+
243
+ /**
244
+ * Define a largura da borda do avatar
245
+ * @param {number} width - Largura da borda em pixels
246
+ * @returns {WelcomeLeave} - Instância atual para encadeamento
247
+ */
248
+ setAvatarBorderWidth(width) {
249
+ if (typeof width !== "number" || width < 0) throw new Error("A largura da borda do avatar deve ser um número não negativo.");
250
+ this.avatarBorderWidth = width;
251
+ return this;
252
+ }
253
+
254
+ // --- Método de Construção ---
255
+ /**
256
+ * Constrói o banner e retorna um buffer de imagem
257
+ * @returns {Promise<Buffer>} - Buffer contendo a imagem do banner
258
+ */
259
+ async build() {
260
+ if (!this.avatar) throw new Error("A imagem do avatar deve ser definida usando setAvatar().");
261
+
262
+ // --- Registro de Fonte ---
263
+ const registeredFontName = await registerFontIfNeeded(this.font);
264
+
265
+ // --- Configuração do Canvas ---
266
+ const cardWidth = 700;
267
+ const cardHeight = 350;
268
+ const avatarSize = 128;
269
+ const borderRadius = this.cornerRadius;
270
+ const padding = 30;
271
+
272
+ const canvas = pureimage.make(cardWidth, cardHeight);
273
+ const ctx = canvas.getContext("2d");
274
+
275
+ // --- Desenha Plano de Fundo e Sobreposição ---
276
+ ctx.save();
277
+ roundRect(ctx, 0, 0, cardWidth, cardHeight, borderRadius, false, false);
278
+ ctx.clip();
279
+ ctx.globalAlpha = 1;
280
+
281
+ if (this.useGradient && this.background.type === "color") {
282
+ // Aplica gradiente como plano de fundo
283
+ const gradient = createLinearGradient(
284
+ ctx,
285
+ 0,
286
+ 0,
287
+ cardWidth,
288
+ cardHeight,
289
+ this.gradientColors.start,
290
+ this.gradientColors.end,
291
+ this.gradientDirection
292
+ );
293
+ ctx.fillStyle = gradient;
294
+ ctx.fillRect(0, 0, cardWidth, cardHeight);
295
+ } else if (this.background.type === "color") {
296
+ // Plano de fundo de cor sólida
297
+ ctx.fillStyle = this.background.value;
298
+ ctx.fillRect(0, 0, cardWidth, cardHeight);
299
+ } else {
300
+ // Plano de fundo de imagem
301
+ try {
302
+ const img = await loadImageWithAxios(this.background.value);
303
+ const aspect = img.width / img.height;
304
+ let drawWidth = cardWidth;
305
+ let drawHeight = cardWidth / aspect;
306
+
307
+ // Ajusta as dimensões para cobrir todo o card
308
+ if (drawHeight < cardHeight) {
309
+ drawHeight = cardHeight;
310
+ drawWidth = cardHeight * aspect;
311
+ }
312
+
313
+ const offsetX = (cardWidth - drawWidth) / 2;
314
+ const offsetY = (cardHeight - drawHeight) / 2;
315
+
316
+ ctx.drawImage(img, offsetX, offsetY, drawWidth, drawHeight);
317
+ } catch (e) {
318
+ console.error("Falha ao desenhar imagem de plano de fundo:", e.message);
319
+ ctx.fillStyle = "#2C2F33";
320
+ ctx.fillRect(0, 0, cardWidth, cardHeight);
321
+ }
322
+ }
323
+
324
+ // Aplica sobreposição para melhorar legibilidade do texto
325
+ if (this.overlayOpacity > 0 && this.background.type === "image") {
326
+ ctx.globalAlpha = this.overlayOpacity;
327
+ ctx.fillStyle = "#000000";
328
+ ctx.fillRect(0, 0, cardWidth, cardHeight);
329
+ }
330
+
331
+ ctx.restore();
332
+ ctx.globalAlpha = 1;
333
+
334
+ // --- Desenha Borda do Card (Opcional) ---
335
+ if (this.cardBorderColor) {
336
+ ctx.strokeStyle = this.cardBorderColor;
337
+ ctx.lineWidth = 5;
338
+ roundRect(ctx, ctx.lineWidth / 2, ctx.lineWidth / 2, cardWidth - ctx.lineWidth, cardHeight - ctx.lineWidth, borderRadius - ctx.lineWidth / 2, false, true);
339
+ }
340
+
341
+ // --- Desenha Avatar ---
342
+ const avatarX = cardWidth / 2 - avatarSize / 2;
343
+ const avatarY = padding + 20;
344
+
345
+ ctx.save();
346
+ ctx.beginPath();
347
+ ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, Math.PI * 2);
348
+ ctx.closePath();
349
+ ctx.clip();
350
+
351
+ try {
352
+ const avatarImg = await loadImageWithAxios(this.avatar);
353
+ ctx.drawImage(avatarImg, avatarX, avatarY, avatarSize, avatarSize);
354
+ } catch (e) {
355
+ console.error("Falha ao desenhar imagem do avatar:", e.message);
356
+ ctx.fillStyle = "#555";
357
+ ctx.fillRect(avatarX, avatarY, avatarSize, avatarSize);
358
+ ctx.fillStyle = "#FFF";
359
+ ctx.font = `bold 30px ${registeredFontName}-Bold`;
360
+ ctx.textAlign = "center";
361
+ ctx.textBaseline = "middle";
362
+ ctx.fillText("?", avatarX + avatarSize / 2, avatarY + avatarSize / 2);
363
+ }
364
+
365
+ ctx.restore();
366
+
367
+ // --- Desenha Borda do Avatar (Opcional) ---
368
+ if (this.avatarBorderColor) {
369
+ ctx.strokeStyle = this.avatarBorderColor;
370
+ ctx.lineWidth = this.avatarBorderWidth;
371
+ ctx.beginPath();
372
+ ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2 + ctx.lineWidth / 2, 0, Math.PI * 2);
373
+ ctx.stroke();
374
+ ctx.closePath();
375
+ }
376
+
377
+ // --- Desenha Texto --- (Centralizado abaixo do avatar)
378
+ const textYStart = avatarY + avatarSize + 30;
379
+ const textMaxWidth = cardWidth - 2 * padding;
380
+
381
+ // Título
382
+ ctx.fillStyle = this.titleColor;
383
+ ctx.font = `bold 32px ${registeredFontName}-Bold`;
384
+ ctx.textAlign = "center";
385
+ ctx.textBaseline = "top";
386
+
387
+ // Aplica sombra de texto se ativada
388
+ if (this.useTextShadow) {
389
+ applyTextShadow(ctx);
390
+ }
391
+
392
+ const titleText = this.title.length > 30 ? this.title.slice(0, 27) + "..." : this.title;
393
+ wrapText(ctx, titleText, cardWidth / 2, textYStart, textMaxWidth - 40, 28, registeredFontName)
394
+
395
+ // Remove sombra para o próximo texto
396
+ if (this.useTextShadow) {
397
+ clearShadow(ctx);
398
+ }
399
+
400
+ // Nome de Usuário (Abaixo do Título)
401
+ ctx.fillStyle = this.usernameColor;
402
+ ctx.font = `medium 28px ${registeredFontName}-Medium`;
403
+ ctx.textAlign = "center";
404
+ const usernameY = textYStart + 40;
405
+
406
+ // Aplica sombra de texto se ativada
407
+ if (this.useTextShadow) {
408
+ applyTextShadow(ctx);
409
+ }
410
+
411
+ const usernameText = this.username.length > 35 ? this.username.slice(0, 32) + "..." : this.username;
412
+
413
+ // Remove sombra para o próximo texto
414
+ if (this.useTextShadow) {
415
+ clearShadow(ctx);
416
+ }
417
+
418
+ // Mensagem (Abaixo do Nome de Usuário)
419
+ ctx.fillStyle = this.messageColor;
420
+ ctx.font = `regular 22px ${registeredFontName}-Regular`;
421
+ ctx.textAlign = "center";
422
+ const messageY = usernameY + 35;
423
+
424
+ // Aplica sombra de texto se ativada
425
+ if (this.useTextShadow) {
426
+ applyTextShadow(ctx, "#000000", 2, 1, 1);
427
+ }
428
+
429
+ wrapText(ctx, this.message, cardWidth / 2, messageY, textMaxWidth - 40, 28, registeredFontName);
430
+
431
+ // Remove sombra
432
+ if (this.useTextShadow) {
433
+ clearShadow(ctx);
434
+ }
435
+
436
+ // --- Codifica e Retorna Buffer ---
437
+ try {
438
+ return await encodeToBuffer(canvas);
439
+ } catch (err) {
440
+ console.error("Falha ao codificar o card de Boas-vindas/Saída:", err);
441
+ throw new Error("Não foi possível gerar o buffer de imagem do card de Boas-vindas/Saída.");
442
+ }
443
+ }
444
+ };
445
+