chore(icons): unify derived logo assets

This commit is contained in:
linshen committed 2026-03-25 20:41:17 +08:00
1 parent 0be61fe9e2
commit 605d7770ca
8 files changed
+92 -312

No files matched your search

+92 -112
View File
@@ -1,142 +1,122 @@
# Electron 应用图标更新指南
# 项目图标资产指南
本文档说明如何为 Electron 应用更新图标文件。
本文档说明 Prompt Optimizer 当前的图标资产结构、唯一源文件,以及更新图标时应同步处理的目标文件。
## 📁 当前图标结构
## 唯一源文件
```
packages/desktop/icons/
├── app-icon.ico # Windows 图标 (多分辨率)
├── app-icon.icns # macOS 图标 (Apple 标准格式)
├── app-icon.png # Linux 备用图标
├── 16x16.png # 小图标 (托盘、工具栏)
├── 32x32.png # 标准桌面图标
├── 48x48.png # 大图标视图
├── 64x64.png # 高 DPI 小图标
├── 128x128.png # 中等 DPI 图标
├── 256x256.png # 高 DPI 大图标
├── 512x512.png # Retina 显示器
└── 1024x1024.png # 超高清显示器
```
当前图标体系的唯一源文件是:
## 🛠️ 使用工具
- `images/logo/1024-1024.svg`
**electron-icon-builder** - 专门为 Electron 应用设计的图标生成工具
对应的位图参考文件是:
### 安装
```bash
npm install -g electron-icon-builder
```
- `images/logo/1024-1024.png`
## 🔄 图标更新流程
约定:
### 准备工作
1. 准备新的源图片 (推荐 1024x1024 PNG 格式)
2. 确保图片质量高,背景透明
- 所有对外发布使用的图标与展示 Logo,都应从上述 SVG 导出
- 不再使用历史位图作为导出入口
- 不再在目标目录中维护独立的“第二份 SVG 源文件”
### Windows 系统
```powershell
# 删除旧图标目录并重新生成
rmdir /s /q packages\desktop\icons
electron-icon-builder --input=images\logo\v2.png --output=packages\desktop --flatten
ren packages\desktop\icons\icon.ico app-icon.ico
ren packages\desktop\icons\icon.icns app-icon.icns
copy images\logo\v2.png packages\desktop\icons\app-icon.png
```
## 当前产物分组
### Linux/macOS 系统
```bash
# 删除旧图标目录并重新生成
rm -rf packages/desktop/icons
electron-icon-builder --input=images/logo/v2.png --output=packages/desktop --flatten
mv packages/desktop/icons/icon.ico packages/desktop/icons/app-icon.ico
mv packages/desktop/icons/icon.icns packages/desktop/icons/app-icon.icns
cp images/logo/v2.png packages/desktop/icons/app-icon.png
```
### 1. 桌面应用图标
## 📋 配置说明
目录:`packages/desktop/icons/`
### package.json 配置
```json
{
"build": {
"win": { "icon": "icons/app-icon.ico" }, // Windows: 专用 ICO 文件
"mac": { "icon": "icons/app-icon.icns" }, // macOS: 专用 ICNS 文件
"linux": { "icon": "icons/" } // Linux: 目录模式,自动选择
}
}
```
主要文件:
### 应用内图标配置
main.js 会根据平台自动选择合适的图标:
- Windows: 使用 app-icon.ico
- macOS: 使用 app-icon.icns
- Linux: 优先使用 512x512.png 或 256x256.png
- `app-icon.ico`
- `app-icon.icns`
- `app-icon.png`
- `16x16.png`
- `24x24.png`
- `32x32.png`
- `48x48.png`
- `64x64.png`
- `128x128.png`
- `256x256.png`
- `512x512.png`
- `1024x1024.png`
## ⚠️ 注意事项
说明:
### 源文件要求
- **格式**: PNG
- **尺寸**: 1024x1024 像素 (推荐)
- **质量**: 高清无损
- **背景**: 透明
- Windows 使用 `app-icon.ico`
- macOS 使用 `app-icon.icns`
- Linux 优先使用 `512x512.png` / `256x256.png`
- 这些文件是已提交的发布产物,不应被视为新的设计源文件
### 注意事项
- ✅ 使用高质量源图片 (1024x1024 PNG)
- ✅ 确保背景透明
- ✅ 删除重建比复制更可靠
### 2. Chrome 扩展图标
## 🧪 测试构建
目录:`packages/extension/public/icons/`
更新图标后,测试各平台构建:
主要文件:
```bash
# 测试 Windows 构建
cd packages/desktop
pnpm run build
- `icon16.png`
- `icon48.png`
- `icon128.png`
# 或测试跨平台构建
pnpm run build:cross-platform
```
额外文件:
## 📝 一键更新命令
- `packages/extension/public/favicon.ico`
假设您有新的图标文件 `images/logo/v2.png`:
说明:
### Windows 一键更新
```powershell
rmdir /s /q packages\desktop\icons && electron-icon-builder --input=images\logo\v2.png --output=packages\desktop --flatten && ren packages\desktop\icons\icon.ico app-icon.ico && ren packages\desktop\icons\icon.icns app-icon.icns && copy images\logo\v2.png packages\desktop\icons\app-icon.png
```
- `manifest.json` 当前引用的就是这三张 PNG
- 扩展图标应与桌面端对应尺寸 PNG 保持一致
### Linux/macOS 一键更新
```bash
rm -rf packages/desktop/icons && electron-icon-builder --input=images/logo/v2.png --output=packages/desktop --flatten && mv packages/desktop/icons/icon.ico packages/desktop/icons/app-icon.ico && mv packages/desktop/icons/icon.icns packages/desktop/icons/app-icon.icns && cp images/logo/v2.png packages/desktop/icons/app-icon.png
```
### 3. Web / 文档 / 展示 Logo
---
主要文件:
**最后更新**: 2026-03-01
**生成工具**: electron-icon-builder
**当前源文件**: images/logo/1024-1024.svg(保留同名 PNG 作为位图参考)
**SVG 转 PNG(圆角透明蒙版,推荐作为跨平台统一输入)**:
- `packages/web/public/favicon.ico`
- `packages/ui/src/assets/logo.png`
- `mkdocs/docs/assets/images/logo.png`
- `mkdocs/docs/assets/images/favicon.png`
- `site/public/images/logo.png`
```bash
magick images/logo/1024-1024.svg -alpha set -colorspace sRGB -strip -resize 1024x1024! images/logo/_tmp-icon-square.png
magick images/logo/_tmp-icon-square.png \
\( -size 1024x1024 xc:none -fill white -draw "roundrectangle 0,0 1024,1024 224,224" \) \
-alpha off -compose CopyOpacity -composite images/logo/1024-1024.png
rm -f images/logo/_tmp-icon-square.png
```
说明:
macOS Dock 图标建议使用圆角(避免直角黑底在 Dock 上显得突兀):
- 这些文件面向站点、文档和应用头部展示
- 它们不是设计源文件,而是从统一源图导出的展示资产
```bash
# 生成圆角临时 PNG,然后用 electron-icon-builder 产出 icns
magick images/logo/1024-1024.png \
\( -size 1024x1024 xc:none -fill white -draw "roundrectangle 0,0 1024,1024 224,224" \) \
-alpha off -compose CopyOpacity -composite images/logo/_mac-icon-src.png
## 历史遗留
electron-icon-builder --input=images/logo/_mac-icon-src.png --output=images/logo/_icon-build-mac-tmp --flatten
cp images/logo/_icon-build-mac-tmp/icons/icon.icns packages/desktop/icons/app-icon.icns
rm -rf images/logo/_icon-build-mac-tmp images/logo/_mac-icon-src.png
```
以下旧文件已不再作为当前图标流程的一部分:
- `images/logo/v2.png`
- `images/logo/v3.png`
如果需要回溯历史设计,请从 Git 历史中查看;不要再把它们重新引入当前导出流程。
## 更新规则
当图标需要更新时,遵循以下规则:
1. 只修改 `images/logo/1024-1024.svg`
2. 从该 SVG 导出 `images/logo/1024-1024.png`
3. 同步更新桌面应用图标产物
4. 同步更新扩展图标产物
5. 同步更新 Web / UI / 文档 / 站点展示 Logo
6. 提交时将这些导出结果一并纳入版本控制
## 最低同步范围
若只做一次常规品牌更新,至少需要同步这些文件:
- `images/logo/1024-1024.svg`
- `images/logo/1024-1024.png`
- `packages/desktop/icons/*`
- `packages/extension/public/icons/*`
- `packages/extension/public/favicon.ico`
- `packages/web/public/favicon.ico`
- `packages/ui/src/assets/logo.png`
- `mkdocs/docs/assets/images/logo.png`
- `mkdocs/docs/assets/images/favicon.png`
- `site/public/images/logo.png`
## 备注
- 当前仓库中的桌面端图标、扩展图标和 Web favicon 已完成统一
- 展示类 Logo 也应继续跟随这套统一源文件更新
- 若后续引入自动化导出脚本,应继续保持 `images/logo/1024-1024.svg` 为唯一输入
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 187 KiB

-200
View File
@@ -1,200 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024" fill="none">
<defs>
<radialGradient id="bg-gradient" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(512 512) rotate(90) scale(512)">
<stop offset="0" stop-color="#2E2F4A"/>
<stop offset="0.45" stop-color="#262147"/>
<stop offset="0.78" stop-color="#1A1634"/>
<stop offset="1" stop-color="#0D0A1F"/>
</radialGradient>
<!-- Subtle top-center atmosphere to match the reference's brighter "sky". -->
<radialGradient id="top-spot" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(512 190) rotate(90) scale(520 380)">
<stop offset="0" stop-color="#7A2BD8" stop-opacity="0.24"/>
<stop offset="0.6" stop-color="#2C1C5E" stop-opacity="0.1"/>
<stop offset="1" stop-color="#0A1020" stop-opacity="0"/>
</radialGradient>
<radialGradient id="inner-plate" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(512 492) rotate(90) scale(470)">
<stop offset="0" stop-color="#552BFF" stop-opacity="0.18"/>
<stop offset="0.45" stop-color="#2A164A" stop-opacity="0.08"/>
<stop offset="0.78" stop-color="#0E0A22" stop-opacity="0.04"/>
<stop offset="1" stop-color="#0C1324" stop-opacity="0"/>
</radialGradient>
<radialGradient id="disk-darken" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(512 512) rotate(90) scale(390)">
<stop offset="0" stop-color="#07081A" stop-opacity="0.62"/>
<stop offset="0.6" stop-color="#07081A" stop-opacity="0.62"/>
<stop offset="0.78" stop-color="#07081A" stop-opacity="0.46"/>
<stop offset="0.9" stop-color="#07081A" stop-opacity="0.22"/>
<stop offset="1" stop-color="#07081A" stop-opacity="0"/>
</radialGradient>
<filter id="soft-glow" x="-50%" y="-50%" width="200%" height="200%" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="22"/>
</filter>
<filter id="mid-glow" x="-50%" y="-50%" width="200%" height="200%" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="14"/>
</filter>
<filter id="wide-glow" x="-70%" y="-70%" width="240%" height="240%" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="52"/>
</filter>
<filter id="far-glow" x="-90%" y="-90%" width="280%" height="280%" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="72"/>
</filter>
<filter id="inner-bloom" x="-70%" y="-70%" width="240%" height="240%" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="10"/>
</filter>
<!-- Symbol glow is intentionally tighter than ring glow to avoid filling the disk. -->
<filter id="sym-edge" x="-40%" y="-40%" width="180%" height="180%" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="6"/>
</filter>
<filter id="sym-soft" x="-50%" y="-50%" width="200%" height="200%" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="14"/>
</filter>
<filter id="sym-wide" x="-70%" y="-70%" width="240%" height="240%" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="28"/>
</filter>
<filter id="sym-far" x="-90%" y="-90%" width="280%" height="280%" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="42"/>
</filter>
<linearGradient id="cutout-glow-fill" x1="440" y1="520" x2="616" y2="520" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#9221FF"/>
<stop offset="0.6" stop-color="#5B17FF"/>
<stop offset="1" stop-color="#2A00FF"/>
</linearGradient>
<filter id="edge-glow" x="-40%" y="-40%" width="180%" height="180%" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="10"/>
</filter>
<!-- Mask out deep interior so ring glow doesn't fill the disk. -->
<linearGradient id="halo-bias" x1="0" y1="0" x2="0" y2="1024" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#FFFFFF" stop-opacity="1"/>
<stop offset="0.55" stop-color="#FFFFFF" stop-opacity="1"/>
<stop offset="1" stop-color="#FFFFFF" stop-opacity="0.35"/>
</linearGradient>
<linearGradient id="halo-bias-soft" x1="0" y1="0" x2="0" y2="1024" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#FFFFFF" stop-opacity="1"/>
<stop offset="0.55" stop-color="#FFFFFF" stop-opacity="1"/>
<stop offset="1" stop-color="#FFFFFF" stop-opacity="0.78"/>
</linearGradient>
<!-- Ring core: white inner rim -> saturated purple outer rim. -->
<radialGradient id="ring-core-base" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(512 512) rotate(90) scale(410)">
<stop offset="0" stop-color="#FFFFFF"/>
<stop offset="0.955" stop-color="#FFFFFF"/>
<stop offset="0.97" stop-color="#DAA3FF"/>
<stop offset="0.99" stop-color="#A528FF"/>
<stop offset="1" stop-color="#A528FF"/>
</radialGradient>
<!-- Top-only specular highlight for the ring. -->
<linearGradient id="ring-highlight-mask-grad" x1="0" y1="0" x2="0" y2="1024" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#FFFFFF"/>
<stop offset="0.55" stop-color="#FFFFFF"/>
<stop offset="0.9" stop-color="#000000"/>
<stop offset="1" stop-color="#000000"/>
</linearGradient>
<mask id="ring-highlight-mask" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse" x="0" y="0" width="1024" height="1024">
<rect x="0" y="0" width="1024" height="1024" fill="url(#ring-highlight-mask-grad)"/>
</mask>
<!-- Apply the top highlight only to the inner half of the ring stroke. -->
<clipPath id="ring-inner-clip">
<circle cx="512" cy="512" r="394"/>
</clipPath>
<!--
Luminance mask feather: black=masked out, white=fully visible.
Using a grayscale ramp (not opacity-only) avoids a hard cutoff ring.
-->
<radialGradient id="ring-mask-feather" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(512 512) rotate(90) scale(360)">
<stop offset="0" stop-color="#000000"/>
<stop offset="0.9" stop-color="#000000"/>
<stop offset="0.93" stop-color="#1A1A1A"/>
<stop offset="0.96" stop-color="#6A6A6A"/>
<stop offset="0.985" stop-color="#D0D0D0"/>
<stop offset="1" stop-color="#FFFFFF"/>
</radialGradient>
<mask id="ring-glow-mask" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse" x="0" y="0" width="1024" height="1024">
<rect x="0" y="0" width="1024" height="1024" fill="#FFFFFF"/>
<circle cx="512" cy="512" r="360" fill="url(#ring-mask-feather)"/>
</mask>
<mask id="ring-glow-mask-biased" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse" x="0" y="0" width="1024" height="1024">
<rect x="0" y="0" width="1024" height="1024" fill="url(#halo-bias)"/>
<circle cx="512" cy="512" r="360" fill="url(#ring-mask-feather)"/>
</mask>
<mask id="ring-glow-mask-biased-soft" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse" x="0" y="0" width="1024" height="1024">
<rect x="0" y="0" width="1024" height="1024" fill="url(#halo-bias-soft)"/>
<circle cx="512" cy="512" r="360" fill="url(#ring-mask-feather)"/>
</mask>
<!-- Keep the "inner bloom" strictly inside the ring with a soft edge. -->
<radialGradient id="inside-ring-feather" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(512 512) rotate(90) scale(404)">
<stop offset="0" stop-color="#FFFFFF" stop-opacity="1"/>
<stop offset="0.985" stop-color="#FFFFFF" stop-opacity="1"/>
<stop offset="1" stop-color="#FFFFFF" stop-opacity="0"/>
</radialGradient>
<!-- Bottom-only boost layer for inner bloom. -->
<linearGradient id="inner-bloom-bottom-boost" x1="0" y1="0" x2="0" y2="1024" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#9221FF" stop-opacity="0"/>
<stop offset="0.65" stop-color="#9221FF" stop-opacity="0"/>
<stop offset="0.85" stop-color="#9221FF" stop-opacity="0.7"/>
<stop offset="1" stop-color="#9221FF" stop-opacity="1"/>
</linearGradient>
<mask id="inside-ring-mask" maskUnits="userSpaceOnUse" maskContentUnits="userSpaceOnUse" x="0" y="0" width="1024" height="1024">
<rect x="0" y="0" width="1024" height="1024" fill="#000000"/>
<circle cx="512" cy="512" r="404" fill="url(#inside-ring-feather)"/>
</mask>
<filter id="soft-core" x="-20%" y="-20%" width="140%" height="140%" color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation="1.2"/>
</filter>
</defs>
<rect width="1024" height="1024" fill="url(#bg-gradient)"/>
<ellipse cx="512" cy="190" rx="640" ry="440" fill="url(#top-spot)"/>
<circle cx="512" cy="512" r="390" fill="url(#inner-plate)"/>
<circle cx="512" cy="512" r="390" fill="url(#disk-darken)"/>
<circle cx="512" cy="512" r="370" stroke="#9221FF" stroke-width="18" opacity="0.92" filter="url(#inner-bloom)" mask="url(#inside-ring-mask)"/>
<circle cx="512" cy="512" r="370" stroke="url(#inner-bloom-bottom-boost)" stroke-width="18" opacity="0.55" filter="url(#inner-bloom)" mask="url(#inside-ring-mask)"/>
<circle cx="512" cy="512" r="396" stroke="#9221FF" stroke-width="48" opacity="0.82" filter="url(#far-glow)" mask="url(#ring-glow-mask-biased)"/>
<circle cx="512" cy="512" r="396" stroke="#9221FF" stroke-width="52" opacity="1" filter="url(#wide-glow)" mask="url(#ring-glow-mask-biased)"/>
<circle cx="512" cy="512" r="396" stroke="#9221FF" stroke-width="36" opacity="0.9" filter="url(#soft-glow)" mask="url(#ring-glow-mask)"/>
<circle cx="512" cy="512" r="396" stroke="#9221FF" stroke-width="34" opacity="0.85" filter="url(#mid-glow)" mask="url(#ring-glow-mask)"/>
<circle cx="512" cy="512" r="396" stroke="#9221FF" stroke-width="30" opacity="0.45" filter="url(#edge-glow)" mask="url(#ring-glow-mask)"/>
<circle cx="512" cy="512" r="394" stroke="url(#ring-core-base)" stroke-width="31" opacity="0.32" filter="url(#soft-core)"/>
<circle cx="512" cy="512" r="394" stroke="url(#ring-core-base)" stroke-width="31"/>
<circle cx="512" cy="512" r="394" stroke="#FFFFFF" stroke-width="31" opacity="0.24" filter="url(#soft-core)" mask="url(#ring-highlight-mask)" clip-path="url(#ring-inner-clip)"/>
<g transform="translate(512 512) scale(0.98) translate(-512 -512)">
<!-- Soft fill inside the cutout to match reference's inner glow. -->
<path d="M440 449 L512 520 L584 449 L616 481 L616 503 L512 597 L408 503 L408 481 Z" fill="url(#cutout-glow-fill)" opacity="0.38" filter="url(#sym-soft)"/>
<path d="M440 376 L512 448 L584 376 L665 457 L665 525 L512 678 L359 525 L359 457 Z M440 449 L512 520 L584 449 L616 481 L616 503 L512 597 L408 503 L408 481 Z" fill="#9221FF" fill-rule="evenodd" opacity="0.14" filter="url(#sym-far)"/>
<path d="M440 376 L512 448 L584 376 L665 457 L665 525 L512 678 L359 525 L359 457 Z M440 449 L512 520 L584 449 L616 481 L616 503 L512 597 L408 503 L408 481 Z" fill="#9221FF" fill-rule="evenodd" opacity="0.36" filter="url(#sym-wide)"/>
<path d="M440 376 L512 448 L584 376 L665 457 L665 525 L512 678 L359 525 L359 457 Z M440 449 L512 520 L584 449 L616 481 L616 503 L512 597 L408 503 L408 481 Z" fill="#9221FF" fill-rule="evenodd" opacity="0.64" filter="url(#sym-soft)"/>
<path d="M440 376 L512 448 L584 376 L665 457 L665 525 L512 678 L359 525 L359 457 Z M440 449 L512 520 L584 449 L616 481 L616 503 L512 597 L408 503 L408 481 Z" fill="#9221FF" fill-rule="evenodd" opacity="0.84" filter="url(#sym-edge)"/>
<path d="M440 376 L512 448 L584 376 L665 457 L665 525 L512 678 L359 525 L359 457 Z M440 449 L512 520 L584 449 L616 481 L616 503 L512 597 L408 503 L408 481 Z" fill="#FFFFFF" fill-rule="evenodd" opacity="0.34" filter="url(#soft-core)"/>
<path d="M440 376 L512 448 L584 376 L665 457 L665 525 L512 678 L359 525 L359 457 Z M440 449 L512 520 L584 449 L616 481 L616 503 L512 597 L408 503 L408 481 Z" fill="#FFFFFF" fill-rule="evenodd"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

After

Width:  |  Height:  |  Size: 187 KiB