edtech / apps /whatsapp-worker /src /visuals.ts
CognxSafeTrack
feat: implement adaptive pedagogy, visuals, and remediation (WOW Phase 1)
6c294cb
import sharp from 'sharp';
import path from 'path';
/**
* Wraps text into multiple lines for SVG.
*/
function wrapText(text: string, maxCharsPerLine: number): string[] {
const words = text.split(' ');
const lines: string[] = [];
let currentLine = '';
words.forEach(word => {
if ((currentLine + word).length > maxCharsPerLine) {
lines.push(currentLine.trim());
currentLine = word + ' ';
} else {
currentLine += word + ' ';
}
});
lines.push(currentLine.trim());
return lines;
}
/**
* Generates a personalized business pitch card with text overlay.
*/
export async function generatePitchCard(text: string): Promise<Buffer> {
const templatePath = path.resolve(__dirname, '../assets/templates/pitch_card.png');
const lines = wrapText(text.toUpperCase(), 25);
const lineHeight = 60;
const startY = 540 - (lines.length * lineHeight) / 2;
const svgText = lines.map((line, i) =>
`<text x="540" y="${startY + (i * lineHeight)}" class="text">${line}</text>`
).join('');
const svgOverlay = Buffer.from(`
<svg width="1080" height="1080">
<style>
.title {
fill: #d4af37;
font-size: 56px;
font-family: sans-serif;
font-weight: 900;
text-anchor: middle;
text-transform: uppercase;
letter-spacing: 3px;
}
.label {
fill: #ffffff;
font-size: 28px;
font-family: sans-serif;
font-weight: 600;
text-anchor: middle;
opacity: 0.8;
}
.text {
fill: #ffffff;
font-size: 48px;
font-family: sans-serif;
font-weight: 800;
text-anchor: middle;
letter-spacing: 1px;
}
</style>
<text x="540" y="250" class="title">MA CARTE BUSINESS</text>
<text x="540" y="440" class="label">MON PROJET :</text>
${svgText}
<text x="540" y="850" class="label">XAMLÉ - 2026</text>
</svg>`);
return sharp(templatePath)
.composite([{ input: svgOverlay, top: 0, left: 0 }])
.png()
.toBuffer();
}