---
title: "小程序图标规范 · Finch Agent"
description: "为小程序配置入口和界面图标"
source: https://finchwork.app/zh/docs/minitools-icons
---

# 小程序图标规范

Finch 里的"图标"其实是两套独立系统，分别对应两个不同的展示位置。写 Mini Tool 时容易把它们混在一起，这里先分清楚：

|  | 入口图标（`icon.png`） | 界面图标（`IconRef`） |
| --- | --- | --- |
| 出现在哪 | 工具箱列表 / 详情页 / 社区推荐卡片 | Composer 工具栏按钮、按钮下拉菜单项 |
| 代表什么 | 这个 Mini Tool 本身 | 某个具体交互点（一个按钮、一个菜单项） |
| 格式 | 一张 PNG 文件 | 一个字符串引用 |
| 谁提供 | 包根目录放一个文件 | manifest 声明 + 代码按需注册 SVG |

## 一、入口图标（`icon.png`）

### 规格

-   文件名必须精确是 **`icon.png`**（大小写敏感），放在包根目录，和 `package.json` 同级。
-   格式必须是 PNG。
-   社区目录收录强制要求**正方形，128×128 至 300×300 像素（含边界）**；本地私装虽不做尺寸校验，但为了在不同展示位置（40px 小图标 / 64px 卡片图）都清晰，建议统一按这个规格出图。
-   背景建议透明或纯色；避免整张图铺满渐变或照片级内容——图标最终会被缩到 40px 左右展示，细节会糊掉。

### 两条读取路径

-   **已安装**：Finch 主进程直接读磁盘上的 `<extension-dir>/icon.png`，通过内部的 `finch-ext-icon://` 协议提供给渲染层。只认包根目录下这一个文件，不会去子目录找，也不接受其他文件名。
-   **未安装（社区目录里展示）**：Finch 直接拼 `https://unpkg.com/<npm包名>@<version>/icon.png` 去读——**这意味着 `icon.png` 必须真的被打进了发布到 npm 的 tarball**。发布前用 `npm pack --dry-run` 确认它在文件清单里；只在你的 git 仓库里有、没跟着 `npm publish` 一起发出去，社区卡片会显示成默认的方块占位图。
-   没有图标时的兜底是一个中性的方块符号（`lucide-react` 的 `Blocks`），不会报错，但体验上不如有图标专业。

## 二、界面图标（`IconRef`）

Composer 按钮和它的下拉菜单项都通过一个字符串引用图标（对齐 VS Code 的 `ThemeIcon` 思路），而不是直接塞组件或图片。这个字符串叫 `IconRef`，有三种写法：

| 写法 | 含义 |
| --- | --- |
| `"settings"` 或 `"Settings"` | Finch 内置 Lucide 图标名，kebab-case 或 PascalCase 都能解析 |
| `"lucide:settings"` | 同上，显式前缀写法，推荐 |
| `"ext:<packId>/<iconId>"` | 引用你自己在运行时注册的 SVG 图标包里的一个图标 |

manifest 里声明按钮槽位时用它：

```json
"contributes": {
  "composerActions": [
    { "id": "my-btn", "icon": "GitBranch", "tooltip": "..." }
  ]
}
```

代码里也可以在 `getIcon()` / 菜单项 `iconName` 里动态返回一个 `IconRef`，覆盖 manifest 里的静态声明。

### 内置 Lucide 集是一个固定白名单

不是"整个 Lucide 图标库都能用"——Finch 维护了一份中央注册表（`BUILTIN_ICONS`），只有登记在案的名字才能被解析，未登记的名字会被当成纯文本（比如你写了个不存在的图标名，界面上会直接显示这串字符，而不是报错或显示某个 Lucide 图标）。目前登记的图标以线性 UI 常用场景为主，例如：

```
settings · star · sparkles · zap · timer · filter · list
git-branch · git-commit-horizontal · clipboard · clipboard-check · clipboard-list
file · file-text · message-circle · log-in · puzzle · check
toggle-left · toggle-right · wand-sparkles · zoom-in · zoom-out
folder · hash · rocket · shield · cloud · calendar · users · bookmark …
```

这份清单会持续增补，**不要凭记忆或猜测使用列表之外的名字**——集成前先用 Finch 跑一下你的按钮，确认图标能正常显示，而不是退化成文字。如果你需要的语义在内置集里确实没有，走下面的运行时图标包。

### 内置集没有你要的图标：注册运行时 SVG 图标包

两步，和 Composer 按钮的"manifest 静态声明 + 代码动态绑定"是同一套模式：

**1\. manifest 声明图标包命名空间**（只声明有这么个包，不放实际图形）：

```json
"contributes": {
  "iconPacks": [{ "id": "my-icons", "label": "My Icons" }]
}
```

**2\. `activate()` 里注册实际 SVG：**

```ts
ctx.subscriptions.push(
  ctx.icons.register('my-icons', {
    rocket: { svg: '<svg viewBox="0 0 24 24">...</svg>' },
  }),
);
```

注册后引用为 `ext:my-icons/rocket`；如果是在自己扩展内部引用自己的图标包，也可以省略包名简写成 `ext:rocket`。

### 设计 SVG 时的几条硬约束

出于安全考虑，Finch 只会把图标当作"一张纯矢量图"来处理，不支持脚本、动画、外部图片/链接这些动态内容——不符合的部分会在显示时被自动忽略，而不是报错提示你。所以画图标时按下面几条来，能保证一次注册就正常显示：

-   **只用基础矢量图形**：路径、圆、矩形、多边形这类静态形状即可，不要包含 `<script>`、`<image>`、动画标签，也不要引用任何外部图片或链接（包括 `http(s)://` 地址）。
-   **不要写死颜色**：正常填色/描边即可，Finch 会自动让图标颜色跟随界面主题（深色/浅色模式切换时自动适配），所以不用、也不需要自己处理配色。
-   **画布用 `24×24`**：不用关心具体像素单位，图标最终会按当前文字大小自动缩放，只要保证内容按 24×24 的比例构图即可。
-   **文件保持小巧**：一个线性小图标正常几 KB 以内，不会有问题。

### 实际渲染尺寸

Composer 工具栏按钮图标和下拉菜单项图标目前都以 **14px、`strokeWidth: 1.8`** 渲染，和 Finch 内置的 Lucide 图标混排在同一行。自定义 SVG 图标包的图形如果想和内置图标视觉统一，建议：

-   用 24×24 画布，图形主体占大约 18–20px 的可视区域（参考 Lucide 默认留白）。
-   描边粗细对齐 Lucide 的 `strokeWidth: 1.8–2`，太粗或太细在 14px 下观感会和其他图标不一致。
-   尽量用描边风格（`fill="none"` + `stroke="currentColor"`），大面积色块图标缩到 14px 容易糊成一团。

## 三、最佳实践

-   **优先用内置 Lucide 集，不要为了"好看"每个 Mini Tool 都自建一套图标包**——内置集已经覆盖了大部分 Composer 场景（设置、过滤、文件、Git、开关……），只有语义确实找不到匹配项时才注册运行时图标包。
-   **`icon.png` 追求"40px 也能一眼认出"**，不要塞复杂渐变、文字或照片；单色/双色的简洁图形效果最好。
-   **不要在 SVG 里写死颜色**，除非你的图标本来就需要多色语义（比如状态灯），否则交给 `currentColor` 继承主题色。
-   **图标包按需注册，不要预注册一堆用不到的图标**——`ctx.icons.register()` 的图标最终都会被消毒并缓存，用多少注册多少即可。
-   **发布前用 `npm pack --dry-run` 确认 `icon.png` 真的进了 tarball**，这是社区目录展示图标出问题最常见的原因（本地开发时文件在，发布清单里漏了）。
-   **命名走 kebab-case**：`packId`、`iconId` 建议和 Mini Tool 自己的 `id` 风格保持一致，方便排查和复用。

## 检查清单

```
[ ] icon.png 在包根目录，文件名精确匹配（大小写敏感）
[ ] icon.png 是 PNG，正方形，128×128–300×300 像素
[ ] npm pack --dry-run 确认 icon.png 在发布清单里
[ ] Composer 按钮 / 菜单图标名先在内置 Lucide 白名单里核对，避免用到未登记的名字导致显示成文字
[ ] 自定义 SVG 图标包按 24×24 画布、currentColor、无外部引用的约束设计
[ ] 自定义 SVG 图标视觉粗细与 Lucide（strokeWidth ~1.8–2）保持一致
```
