Raw
1 #!/usr/bin/env node
2 /**
3 * Renders Play Store marketing graphics from HTML templates using Puppeteer.
4 * Output: assets/aso/playstore/{01-chat,02-settings}-1080x1920.png + feature-graphic-1024x500.png
5 *
6 * Usage: node scripts/generate-playstore-graphics.mjs
7 */
8
9 import puppeteer from 'puppeteer';
10 import { fileURLToPath } from 'url';
11 import { dirname, join, resolve } from 'path';
12 import { mkdirSync } from 'fs';
13
14 const __dirname = dirname(fileURLToPath(import.meta.url));
15 const repoRoot = resolve(__dirname, '..');
16 const templateDir = join(__dirname, 'playstore-templates');
17 const outDir = join(repoRoot, 'assets', 'aso', 'playstore');
18
19 mkdirSync(outDir, { recursive: true });
20
21 const targets = [
22 {
23 template: '01-chat.html',
24 output: '01-chat-1080x1920.png',
25 width: 1080,
26 height: 1920,
27 },
28 {
29 template: '02-settings.html',
30 output: '02-settings-1080x1920.png',
31 width: 1080,
32 height: 1920,
33 },
34 {
35 template: 'feature-graphic.html',
36 output: 'feature-graphic-1024x500.png',
37 width: 1024,
38 height: 500,
39 },
40 ];
41
42 console.log('Launching browser…');
43 const browser = await puppeteer.launch({
44 headless: true,
45 args: ['--no-sandbox', '--disable-setuid-sandbox'],
46 });
47
48 for (const { template, output, width, height } of targets) {
49 const page = await browser.newPage();
50 await page.setViewport({ width, height, deviceScaleFactor: 1 });
51
52 const url = `file://${join(templateDir, template)}`;
53 await page.goto(url, { waitUntil: 'networkidle0', timeout: 30_000 });
54
55 // Wait a tick for any final layout reflow after font load
56 await new Promise(r => setTimeout(r, 500));
57
58 const outputPath = join(outDir, output);
59 await page.screenshot({ path: outputPath, fullPage: false, type: 'png' });
60
61 console.log(`✓ ${output}`);
62 await page.close();
63 }
64
65 await browser.close();
66 console.log(`\nDone — files in assets/aso/playstore/`);