kira-arts 1.4.0 → 1.4.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.
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  A TypeScript library for generating Discord-style visual cards — profiles, welcome/leave events, level-ups, achievements, leaderboards, compatibility "ship" cards, now-playing music cards, and giveaways — rendered natively for speed and zero runtime dependencies on a browser or headless Chromium.
4
4
 
5
+ <img src="https://i.imgur.com/AMk4jWr.png" alt="Kira Arts" width="100%">
6
+
5
7
  **📚 Full documentation, live examples, and a Playground: [documentation](https://guide.worddevs.dev/docs/kira-arts)**
6
8
 
7
9
  [![npm version](https://img.shields.io/npm/v/kira-arts.svg)](https://www.npmjs.com/package/kira-arts)
@@ -69,7 +71,7 @@ client.on("messageCreate", async (message) => {
69
71
  badgesFrame: true,
70
72
  });
71
73
 
72
- await message.reply({ files: [toAttachment(buffer, "profile", "png")] });
74
+ await message.reply({ files: [toAttachment(buffer, { name: "profile", extension: "png" })] });
73
75
  });
74
76
 
75
77
  client.login(process.env.TOKEN);
package/dist/index.cjs CHANGED
@@ -1117,21 +1117,31 @@ async function applyWatermarkToCanvas(canvas) {
1117
1117
  const margin = config.margin ?? 16;
1118
1118
  const scale = config.scale && config.scale > 0 ? config.scale : 1;
1119
1119
  const opacity = config.opacity != null ? Math.min(1, Math.max(0, config.opacity)) : .6;
1120
- const fontSize = (config.fontSize ?? 13) * scale;
1120
+ const fontSize = (config.fontSize ?? 22) * scale;
1121
1121
  const position = config.position ?? "bottom-right";
1122
- const isRight = position === "bottom-right" || position === "top-right";
1123
- const isBottom = position === "bottom-right" || position === "bottom-left";
1122
+ const isLeft = position === "top-left" || position === "bottom-left" || position === "center-left";
1123
+ const isRight = position === "top-right" || position === "bottom-right" || position === "center-right";
1124
+ const isTop = position === "top-left" || position === "top-right";
1125
+ const isBottom = position === "bottom-left" || position === "bottom-right";
1124
1126
  const logo = config.imageUrl ? await resolveWatermarkImage(config.imageUrl) : void 0;
1125
- const logoSize = 20 * scale;
1126
- const gap = 8 * scale;
1127
+ const logoSize = (config.size && config.size > 0 ? config.size : 56) * scale;
1128
+ const gap = 12 * scale;
1127
1129
  let contentWidth = 0;
1128
1130
  if (logo) contentWidth += logoSize;
1129
1131
  if (config.text) {
1130
1132
  ctx.font = `${fontSize}px sans-serif`;
1131
1133
  contentWidth += (logo ? gap : 0) + ctx.measureText(config.text).width;
1132
1134
  }
1133
- const startX = isRight ? width - margin - contentWidth : margin;
1134
- const centerY = isBottom ? height - margin - logoSize / 2 : margin + logoSize / 2;
1135
+ let startX;
1136
+ if (isRight) startX = width - margin - contentWidth;
1137
+ else if (isLeft) startX = margin;
1138
+ else startX = (width - contentWidth) / 2;
1139
+ let centerY;
1140
+ if (isBottom) centerY = height - margin - logoSize / 2;
1141
+ else if (isTop) centerY = margin + logoSize / 2;
1142
+ else centerY = height / 2;
1143
+ startX += config.offsetX ?? 0;
1144
+ centerY += config.offsetY ?? 0;
1135
1145
  ctx.save();
1136
1146
  ctx.globalAlpha = opacity;
1137
1147
  let cursorX = startX;
@@ -2395,7 +2405,7 @@ async function resolveStaticImage(bytes, fallbackUrl) {
2395
2405
  }
2396
2406
  return (0, _napi_rs_canvas.loadImage)(fallbackUrl).catch(() => void 0);
2397
2407
  }
2398
- async function renderAnimatedMemberEvent(avatarUrl, username, layout, width, height) {
2408
+ async function renderAnimatedMemberEvent(avatarUrl, username, layout, width, height, output) {
2399
2409
  const avatarSource = await fetchImageSource(avatarUrl).catch(() => null);
2400
2410
  const animatedAvatar = avatarSource?.animated ?? null;
2401
2411
  const avatarImage = animatedAvatar ? void 0 : await resolveStaticImage(avatarSource?.bytes, avatarUrl);
@@ -2404,7 +2414,7 @@ async function renderAnimatedMemberEvent(avatarUrl, username, layout, width, hei
2404
2414
  const staticBackgroundImage = !animatedBackground && layout.customBackground ? await resolveStaticImage(backgroundSource?.bytes, layout.customBackground) : void 0;
2405
2415
  const wantsConfetti = layout.confetti === true;
2406
2416
  const frameCount = resolveGifFrameCount(layout.gifSeconds);
2407
- if (!animatedBackground && !animatedAvatar && !wantsConfetti) return renderStaticMemberEventGif(avatarUrl, username, layout, width, height, avatarImage, staticBackgroundImage);
2417
+ if (!animatedBackground && !animatedAvatar && !wantsConfetti) return renderStaticMemberEventImage(avatarUrl, username, layout, width, height, avatarImage, staticBackgroundImage, output);
2408
2418
  const particles = wantsConfetti ? generateConfetti(width, height, layout.accentColor) : [];
2409
2419
  const frames = [];
2410
2420
  if (animatedBackground || animatedAvatar) {
@@ -2451,7 +2461,7 @@ async function renderAnimatedMemberEvent(avatarUrl, username, layout, width, hei
2451
2461
  }
2452
2462
  return encodeGif(frames, GIF_FRAME_DELAY_MS);
2453
2463
  }
2454
- async function renderStaticMemberEventGif(avatarUrl, username, layout, width, height, avatarImage, backgroundImage) {
2464
+ async function renderStaticMemberEventImage(avatarUrl, username, layout, width, height, avatarImage, backgroundImage, output) {
2455
2465
  const canvas = (0, _napi_rs_canvas.createCanvas)(width, height);
2456
2466
  const ctx = canvas.getContext("2d");
2457
2467
  fillOpaqueBase(ctx, width, height);
@@ -2460,16 +2470,11 @@ async function renderStaticMemberEventGif(avatarUrl, username, layout, width, he
2460
2470
  backgroundImage
2461
2471
  });
2462
2472
  await applyWatermarkToCanvas(canvas);
2463
- const imageData = ctx.getImageData(0, 0, width, height);
2464
- return encodeGif([{
2465
- data: imageData.data,
2466
- width,
2467
- height
2468
- }], GIF_FRAME_DELAY_MS);
2473
+ return encodeCanvas(canvas, output);
2469
2474
  }
2470
2475
  async function genMemberEventPng(avatarUrl, username, layout, output) {
2471
2476
  const { width, height } = getCardDimensions$5(layout);
2472
- if (layout.animated) return renderAnimatedMemberEvent(avatarUrl, username, layout, width, height);
2477
+ if (layout.animated) return renderAnimatedMemberEvent(avatarUrl, username, layout, width, height, output);
2473
2478
  const { canvas, ctx } = createMemberEventCanvas(width, height);
2474
2479
  await drawMemberEventCard(ctx, width, height, avatarUrl, username, layout);
2475
2480
  return encodeCanvas(canvas, output);
@@ -2482,12 +2487,12 @@ function formatMessage(template, username, guildName, memberCount) {
2482
2487
  return template.replace(/\{username\}/g, username).replace(/\{user\}/g, username).replace(/\{server\}/g, guildName).replace(/\{memberCount\}/g, memberCount != null ? String(memberCount) : "");
2483
2488
  }
2484
2489
  function formatMemberCount(memberCount, localDateType) {
2485
- return localDateType.startsWith("es") ? `Member #${memberCount}` : `Member #${memberCount}`;
2490
+ return localDateType.startsWith("es") ? `Miembro #${memberCount}` : `Member #${memberCount}`;
2486
2491
  }
2487
2492
  function formatKindLabel(kind, localDateType) {
2488
2493
  const isEs = localDateType.startsWith("es");
2489
- if (kind === "welcome") return isEs ? "NEW MEMBER" : "NEW MEMBER";
2490
- return isEs ? "MEMBER LEFT" : "MEMBER LEFT";
2494
+ if (kind === "welcome") return isEs ? "NUEVO MIEMBRO" : "NEW MEMBER";
2495
+ return isEs ? "MIEMBRO SALIÓ" : "MEMBER LEFT";
2491
2496
  }
2492
2497
  async function renderMemberEventCard(userId, guildName, defaultMessage, options = {}, kind = "welcome") {
2493
2498
  if (!userId || typeof userId !== "string") throw new KiraError("A valid userId is required", "VALIDATION");
@@ -2506,7 +2511,7 @@ async function renderMemberEventCard(userId, guildName, defaultMessage, options
2506
2511
  roleColor: data.decoration.roleColor,
2507
2512
  fallback: palette?.borderColor ?? [accentColor]
2508
2513
  });
2509
- const localDateType = options.localDateType ?? "es";
2514
+ const localDateType = options.localDateType ?? "en";
2510
2515
  const rawMessage = options.message === "" ? void 0 : options.message ?? defaultMessage;
2511
2516
  const message = rawMessage ? normalizeDisplayText(formatMessage(rawMessage, username, normalizeDisplayText(guildName), options.memberCount)) : void 0;
2512
2517
  const secondaryMessage = options.secondaryMessage ? normalizeDisplayText(formatMessage(options.secondaryMessage, username, normalizeDisplayText(guildName), options.memberCount)) : void 0;