From dc9c744df1aaddedc8c7e789ddf104fb0db0098c Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Sun, 13 Sep 2026 08:57:12 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=9B=BE=E6=A0=87=E7=94=9F=E6=88=90?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E5=85=A5=E5=BA=93=EF=BC=88Chromium=20SVG=20?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=20+=20PIL=20=E7=BC=A9=E6=94=BE=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- png/gen_icons.js | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 png/gen_icons.js diff --git a/png/gen_icons.js b/png/gen_icons.js new file mode 100644 index 0000000..80c8694 --- /dev/null +++ b/png/gen_icons.js @@ -0,0 +1,66 @@ +// 生成 PWA 图标:复用 index.html 内联 favicon 的同一套图形语言(靛蓝圆角方块 + 时钟指针)。 +// 用 Chromium 渲染 SVG 截图(omitBackground 保住透明圆角),再用 PIL 缩放出各尺寸。 +// 一次性工具:图标改版时重跑 png/gen_icons.js 即可。 +const puppeteer = require('/root/.openclaw/workspace/web_tasks/node_modules/puppeteer'); +const fs = require('fs'); +const path = require('path'); + +const OUT = path.join(__dirname, '..', 'icons'); + +// any:圆角方块,四角透明(浏览器/桌面场景显示) +const svgAny = (s) => ` + + + +`; + +// maskable:满幅无圆角(由系统裁切),图形缩进 80% 安全区 +const svgMaskable = (s) => ` + + + + + +`; + +// apple-touch-icon:满幅无圆角(iOS 自行加圆角) +const svgApple = (s) => ` + + + +`; + +(async () => { + const browser = await puppeteer.launch({ + executablePath: '/usr/bin/chromium', headless: 'new', + args: ['--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage'] + }); + const page = await browser.newPage(); + + async function shoot(svg, width, height, file) { + await page.setViewport({ width, height }); + await page.setContent(`${svg}`); + await page.screenshot({ path: file, omitBackground: true, clip: { x: 0, y: 0, width, height } }); + } + + await shoot(svgAny(512), 512, 512, path.join(OUT, 'icon-512.png')); + await shoot(svgMaskable(512), 512, 512, path.join(OUT, 'icon-maskable-512.png')); + await shoot(svgApple(180), 180, 180, path.join(OUT, 'apple-touch-icon.png')); + await browser.close(); + + // 高分辨率原图就位,小尺寸用 PIL Lanczos 缩(比再截一轮更快更稳) + const { execSync } = require('child_process'); + execSync(`python3 - <<'PY' +from PIL import Image +import os +OUT = ${JSON.stringify(OUT)} +Image.open(os.path.join(OUT, 'icon-512.png')).resize((192,192), Image.LANCZOS).save(os.path.join(OUT, 'icon-192.png'), optimize=True) +Image.open(os.path.join(OUT, 'icon-maskable-512.png')).resize((192,192), Image.LANCZOS).save(os.path.join(OUT, 'icon-maskable-192.png'), optimize=True) +print('icons done') +PY`, { stdio: 'inherit' }); + + for (const f of fs.readdirSync(OUT)) { + const st = fs.statSync(path.join(OUT, f)); + console.log(f, st.size, 'bytes'); + } +})().catch(e => { console.error('FAILED:', e.message); process.exit(1); });