const fs = require('fs'); const path = require('path'); const { createCanvas } = require('@napi-rs/canvas'); const SIZE = 600; const outDir = __dirname; function roundedRect(ctx, x, y, w, h, r) { ctx.beginPath(); ctx.moveTo(x + r, y); ctx.arcTo(x + w, y, x + w, y + h, r); ctx.arcTo(x + w, y + h, x, y + h, r); ctx.arcTo(x, y + h, x, y, r); ctx.arcTo(x, y, x + w, y, r); ctx.closePath(); } function drawSteam(ctx, x, y, color) { ctx.strokeStyle = color; ctx.lineWidth = 10; ctx.lineCap = 'round'; ctx.beginPath(); ctx.moveTo(x, y + 36); ctx.bezierCurveTo(x - 14, y + 10, x + 20, y, x + 6, y - 30); ctx.stroke(); } function drawRice(ctx, cx, cy, colorA, colorB) { // Rice mound const grad = ctx.createLinearGradient(cx - 100, cy - 30, cx + 100, cy + 20); grad.addColorStop(0, colorA); grad.addColorStop(1, colorB); ctx.fillStyle = grad; ctx.beginPath(); ctx.ellipse(cx, cy, 128, 56, 0, 0, Math.PI * 2); ctx.fill(); // Rice grains highlights (subtle) ctx.fillStyle = 'rgba(255,255,255,0.55)'; for (let i = 0; i < 14; i++) { const x = cx - 95 + i * 14; const y = cy - 6 + ((i % 2) * 6 - 3); ctx.beginPath(); ctx.ellipse(x, y, 4, 2.6, -0.3, 0, Math.PI * 2); ctx.fill(); } } function drawLogo(theme) { const canvas = createCanvas(SIZE, SIZE); const ctx = canvas.getContext('2d'); // Background gradient const bg = ctx.createLinearGradient(0, 0, SIZE, SIZE); bg.addColorStop(0, theme.bgFrom); bg.addColorStop(1, theme.bgTo); ctx.fillStyle = bg; ctx.fillRect(0, 0, SIZE, SIZE); // Main card ctx.save(); ctx.shadowColor = theme.shadow; ctx.shadowBlur = 28; ctx.shadowOffsetY = 10; roundedRect(ctx, 74, 74, 452, 452, 122); ctx.fillStyle = theme.card; ctx.fill(); ctx.restore(); // Bowl body const bowlGrad = ctx.createLinearGradient(180, 280, 420, 430); bowlGrad.addColorStop(0, theme.bowlTop); bowlGrad.addColorStop(1, theme.bowlBottom); ctx.fillStyle = bowlGrad; roundedRect(ctx, 168, 302, 264, 144, 64); ctx.fill(); // Bowl lip outer ctx.fillStyle = theme.lipOuter; ctx.beginPath(); ctx.ellipse(300, 300, 154, 44, 0, 0, Math.PI * 2); ctx.fill(); // Bowl lip inner ctx.fillStyle = theme.lipInner; ctx.beginPath(); ctx.ellipse(300, 304, 104, 26, 0, 0, Math.PI * 2); ctx.fill(); // Rice drawRice(ctx, 300, 278, theme.riceA, theme.riceB); // Chopsticks (to imply meal) ctx.strokeStyle = theme.chop; ctx.lineWidth = 12; ctx.lineCap = 'round'; ctx.beginPath(); ctx.moveTo(380, 178); ctx.lineTo(492, 290); ctx.stroke(); ctx.beginPath(); ctx.moveTo(416, 158); ctx.lineTo(528, 270); ctx.stroke(); // Steam drawSteam(ctx, 240, 196, theme.steam); drawSteam(ctx, 300, 172, theme.steam); drawSteam(ctx, 360, 196, theme.steam); const output = path.join(outDir, theme.filename); fs.writeFileSync(output, canvas.toBuffer('image/png')); return output; } const light = drawLogo({ filename: 'lunch-pick-logo-light-600-v3.png', bgFrom: '#F6FAFF', bgTo: '#EAF3FF', card: 'rgba(255,255,255,0.96)', shadow: 'rgba(16,78,147,0.18)', bowlTop: '#31B4F4', bowlBottom: '#0A85E8', lipOuter: '#DDE9F8', lipInner: '#8EA6C5', riceA: '#FFFFFF', riceB: '#EFF6FF', chop: '#1E4F8A', steam: '#5EA8EE', }); const dark = drawLogo({ filename: 'lunch-pick-logo-dark-600-v3.png', bgFrom: '#07142A', bgTo: '#0E2243', card: 'rgba(17,39,72,0.96)', shadow: 'rgba(0,0,0,0.46)', bowlTop: '#66CBFF', bowlBottom: '#1A9EF4', lipOuter: '#DDE6F2', lipInner: '#8CA2BC', riceA: '#FFFFFF', riceB: '#F2F7FF', chop: '#E4F0FF', steam: '#B8DCFF', }); console.log(light); console.log(dark);