UNPKG

build-in-public-bot

Version:

AI-powered CLI bot for automating build-in-public tweets with code screenshots

107 lines 4.15 kB
"use strict"; 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