Skip to main content

小程序图标规范

为小程序配置入口和界面图标

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-reactBlocks),不会报错,但体验上不如有图标专业。

二、界面图标(IconRef

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

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

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

"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 声明图标包命名空间(只声明有这么个包,不放实际图形):

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

2. activate() 里注册实际 SVG:

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-casepackIdiconId 建议和 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)保持一致