#!/usr/bin/env node // 生成托盘深浅色单色图标 (W-THE-01): // src-tauri/icons/tray-light.png 深色字形 —— 浅色任务栏用 // src-tauri/icons/tray-dark.png 白色字形 —— 深色任务栏用 // // 做法: 不能只用 alpha 通道做剪影 —— 源图标是「深色圆角方块 + 白色 i」, // 整个方块都是不透明的, 只取 alpha 会得到一块纯色方块, i 字母细节全丢。 // 这里按**亮度**映射, 保留字母结构: // 深色字形: alpha = (255-L)/255 → 方块保留深色, 白色 i 镂空透出任务栏 // 浅色字形: alpha = max(L,40)/255 → i 白色高亮, 方块留一点淡淡衬底 // 产物已提交进仓库, 仅图标变更时需要重跑: node scripts/gen-tray-icons.mjs import sharp from 'sharp' import { fileURLToPath } from 'node:url' import path from 'node:path' const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') const src = path.join(root, 'src-tauri', 'icons', '128x128.png') const outDir = path.join(root, 'src-tauri', 'icons') const targets = [ { file: 'tray-light.png', rgb: [31, 31, 31], mode: 'dark-glyph' }, // 深色字形 (浅色任务栏) { file: 'tray-dark.png', rgb: [250, 250, 250], mode: 'light-glyph' }, // 白色字形 (深色任务栏) ] for (const t of targets) { const { data, info } = await sharp(src) .resize(32, 32, { fit: 'contain', background: { r: 0, g: 0, b: 0, alpha: 0 } }) .ensureAlpha() .raw() .toBuffer({ resolveWithObject: true }) const out = Buffer.alloc(info.width * info.height * 4) for (let i = 0; i < info.width * info.height; i++) { const r = data[i * 4] const g = data[i * 4 + 1] const b = data[i * 4 + 2] const a = data[i * 4 + 3] const lumi = 0.2126 * r + 0.7152 * g + 0.0722 * b const alpha = t.mode === 'dark-glyph' ? (a * (255 - lumi)) / 255 : (a * Math.max(lumi, 40)) / 255 out[i * 4] = t.rgb[0] out[i * 4 + 1] = t.rgb[1] out[i * 4 + 2] = t.rgb[2] out[i * 4 + 3] = Math.round(alpha) } await sharp(out, { raw: { width: info.width, height: info.height, channels: 4 } }) .png() .toFile(path.join(outDir, t.file)) console.log('generated', t.file) }