Merge pull request #104 from R0m1k3/claude/analyze-codebase-01XdqnCRHEvL9VWeHDNGVPkA

Analyze and understand application codebase
This commit is contained in:
LogiFlow authored and GitHub committed 2025-11-29 11:38:41 +01:00
commit c21562e2b0
1 file changed
+68 -12
+68 -12
View File
@@ -19,6 +19,11 @@ export class EmojiRenderer {
*/
private parseTextWithEmojis(text: string): EmojiSegment[] {
const emojis = parse(text);
if (emojis.length > 0) {
console.log(`📝 Texte avec ${emojis.length} emoji(s) détecté(s)`);
}
if (emojis.length === 0) {
return [{ type: 'text', content: text }];
}
@@ -57,17 +62,67 @@ export class EmojiRenderer {
}
/**
* Download and cache emoji image
* Convert default Twemoji URL to jsDelivr CDN (more reliable)
* Example: https://twemoji.maxcdn.com/v/latest/72x72/1f600.png
* Becomes: https://cdn.jsdelivr.net/gh/twitter/twemoji@latest/assets/72x72/1f600.png
*/
private convertToReliableCDN(url: string): string {
// Extract the emoji filename (e.g., "1f600.png")
const match = url.match(/\/([a-f0-9\-]+\.png)$/i);
if (!match) return url;
const filename = match[1];
// Use jsDelivr CDN which is more reliable
return `https://cdn.jsdelivr.net/gh/twitter/twemoji@latest/assets/72x72/${filename}`;
}
/**
* Download and cache emoji image with retry logic
*/
private async getEmojiImage(url: string): Promise<Buffer> {
if (this.emojiCache.has(url)) {
return this.emojiCache.get(url)!;
// Use reliable CDN URL
const reliableUrl = this.convertToReliableCDN(url);
if (this.emojiCache.has(reliableUrl)) {
return this.emojiCache.get(reliableUrl)!;
}
const response = await fetch(url);
const buffer = Buffer.from(await response.arrayBuffer());
this.emojiCache.set(url, buffer);
return buffer;
console.log('🔍 Téléchargement emoji depuis:', reliableUrl);
// Retry logic: try up to 3 times
let lastError: Error | null = null;
for (let attempt = 1; attempt <= 3; attempt++) {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000); // 10 second timeout
const response = await fetch(reliableUrl, {
signal: controller.signal
});
clearTimeout(timeout);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
console.log('✅ Emoji téléchargé avec succès');
const buffer = Buffer.from(await response.arrayBuffer());
this.emojiCache.set(reliableUrl, buffer);
return buffer;
} catch (error) {
lastError = error as Error;
console.warn(`⚠️ Tentative ${attempt}/3 échouée:`, error instanceof Error ? error.message : error);
if (attempt < 3) {
// Wait before retry (exponential backoff: 500ms, 1000ms)
await new Promise(resolve => setTimeout(resolve, attempt * 500));
}
}
}
console.error(`❌ Échec téléchargement emoji après 3 tentatives:`, lastError);
throw lastError || new Error('Failed to fetch emoji');
}
/**
@@ -110,15 +165,16 @@ export class EmojiRenderer {
try {
const emojiBuffer = await this.getEmojiImage(segment.url);
const emojiImage = await loadImage(emojiBuffer);
// Draw emoji slightly above baseline to align with text
// Draw emoji centered vertically with text
// When textBaseline is 'middle', y represents the vertical center
const emojiSize = fontSize;
const emojiY = y - fontSize * 0.8; // Adjust vertical position
const emojiY = y - (emojiSize / 2); // Center the emoji around y
ctx.drawImage(emojiImage, currentX, emojiY, emojiSize, emojiSize);
currentX += emojiSize;
} catch (error) {
console.error('Error loading emoji:', error);
console.error(`❌ Impossible de charger l'emoji "${segment.content}":`, error instanceof Error ? error.message : error);
// Fallback: draw text representation
ctx.fillText(segment.content, currentX, y);
currentX += ctx.measureText(segment.content).width;