build-in-public-bot
Version:
AI-powered CLI bot for automating build-in-public tweets with code screenshots
107 lines • 4.15 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EmojiRenderer = void 0;
const canvas_1 = require("canvas");
const axios_1 = __importDefault(require("axios"));
const logger_1 = require("../utils/logger");
class EmojiRenderer {
static instance;
emojiCache = new Map();
emojiRegex = /(\p{Emoji_Presentation}|\p{Emoji}\uFE0F)/gu;
constructor() { }
static getInstance() {
if (!EmojiRenderer.instance) {
EmojiRenderer.instance = new EmojiRenderer();
}
return EmojiRenderer.instance;
}
async renderTextWithEmojis(ctx, text, x, y, fontSize, theme) {
const emojis = this.findEmojis(text);
let currentX = x;
if (emojis.length === 0) {
ctx.fillStyle = theme?.text || '#ffffff';
ctx.fillText(text, currentX, y);
return ctx.measureText(text).width;
}
let lastIndex = 0;
for (const emoji of emojis) {
if (emoji.indices[0] > lastIndex) {
const beforeText = text.substring(lastIndex, emoji.indices[0]);
ctx.fillStyle = theme?.text || '#ffffff';
ctx.fillText(beforeText, currentX, y);
currentX += ctx.measureText(beforeText).width;
}
try {
const emojiImage = await this.loadEmojiImage(emoji.codePoint);
const emojiSize = fontSize * 0.8;
const emojiY = y + (fontSize * 0.1);
ctx.drawImage(emojiImage, currentX, emojiY, emojiSize, emojiSize);
currentX += emojiSize;
const nextEmoji = emojis.find(e => e.indices[0] === emoji.indices[1]);
if (nextEmoji) {
currentX += fontSize * 0.2;
}
}
catch (error) {
logger_1.logger.warn(`Failed to load emoji ${emoji.char}`);
ctx.fillStyle = theme?.text || '#ffffff';
ctx.fillText(emoji.char, currentX, y);
currentX += ctx.measureText(emoji.char).width;
}
lastIndex = emoji.indices[1];
}
if (lastIndex < text.length) {
const afterText = text.substring(lastIndex);
ctx.fillStyle = theme?.text || '#ffffff';
ctx.fillText(afterText, currentX, y);
currentX += ctx.measureText(afterText).width;
}
return currentX - x;
}
findEmojis(text) {
const matches = [];
let match;
while ((match = this.emojiRegex.exec(text)) !== null) {
const char = match[0];
const codePoint = this.getEmojiCodePoint(char);
matches.push({
char,
codePoint,
indices: [match.index, match.index + char.length]
});
}
this.emojiRegex.lastIndex = 0;
return matches;
}
getEmojiCodePoint(emoji) {
const codePoints = [...emoji].map(char => char.codePointAt(0)?.toString(16).toLowerCase().padStart(4, '0')).filter(Boolean);
return codePoints.join('-');
}
async loadEmojiImage(codePoint) {
if (this.emojiCache.has(codePoint)) {
return this.emojiCache.get(codePoint);
}
try {
const url = `https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/${codePoint}.png`;
const response = await axios_1.default.get(url, {
responseType: 'arraybuffer',
timeout: 5000
});
const image = await (0, canvas_1.loadImage)(Buffer.from(response.data));
this.emojiCache.set(codePoint, image);
return image;
}
catch (error) {
logger_1.logger.warn(`Failed to load emoji from CDN: ${codePoint}`);
throw error;
}
}
clearCache() {
this.emojiCache.clear();
}
}
exports.EmojiRenderer = EmojiRenderer;
//# sourceMappingURL=emoji-renderer.js.map
;