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 +3 -1
- package/dist/index.cjs +26 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -41
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +44 -41
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +26 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -8
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
|
[](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 ??
|
|
1120
|
+
const fontSize = (config.fontSize ?? 22) * scale;
|
|
1121
1121
|
const position = config.position ?? "bottom-right";
|
|
1122
|
-
const
|
|
1123
|
-
const
|
|
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 =
|
|
1126
|
-
const gap =
|
|
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
|
-
|
|
1134
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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") ? `
|
|
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 ? "
|
|
2490
|
-
return isEs ? "
|
|
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 ?? "
|
|
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;
|