Chuyển đến nội dung chính

dsh-icon-custom

Đã xác minh

@cowwo/dsh-icon-custom · v0.19.0 · MIT · Giao diện web

自定义浏览器标签页图标:在设置页上传 SVG/PNG/ICO,立即生效并持久保存,自动适配安卓/iOS/平板 PWA 图标,显示未读红点与进行中黄点(工作区数字/会话行/左上角/标签页,数字可点开清单、点条目定位到侧栏),并可把未读数显示为系统应用图标角标(Windows 任务栏 / macOS Dock,含通知权限申请)。· Custom browser tab favicon: upload SVG/PNG/ICO from the settings page, applies instantly a

Cài đặt

dsh plugin add @cowwo/dsh-icon-custom

Xác nhận layer đã áp bằng dsh --profile default --dump-config — xem hướng dẫn cài plugin.

Mã nguồn

Thẻ

Readme

dsh-icon-custom

自定义 DSH 浏览器标签页图标(favicon):在设置页上传 SVG / PNG / ICO / JPEG,立即生效并持久保存,刷新或重启后都保留。从 0.7.0 起,PWA / Logo 路径会自动派生各平台的标准图标尺寸,同一张上传图即可适配安卓、iOS 和平板。

纯 JS 依赖(jimp),无原生二进制,在 pnpm 供应链政策下也能干净安装。

安装

dsh plugin --profile web add dsh-icon-custom

带 scope 的包名:@cowwo/dsh-icon-custom。

使用

  1. 打开设置 → icon管理。
  2. 点击上传图标,选择 SVG、PNG 或 ICO(最大 10 MB)。
  3. 当前标签页图标立即替换;文件会保存到 $DSH_HOME/custom-favicon/。
  4. 点击恢复默认清除自定义图标,回到平台默认图标。

PWA 安装图标

勾选同时替换 PWA 安装图标(勾选状态按图标记录保存),网页「安装为应用 / 添加到主屏幕」后,桌面和启动器上的图标也会换成你上传的这张。插件会把 /manifest.webmanifest 的 icons 项改写为自动派生的标准 PNG 集合:192×192 any、512×512 any,以及一个 512×512 maskable 变体(不透明背景),让安卓启动器能干净裁剪;在 iPhone/iPad 上还会把 <link rel="apple-touch-icon"> 指向派生的 180×180 不透明 PNG(iOS 不支持 SVG 触屏图标,也不接受透明图)。

SVG 会在浏览器里先栅格化成 512×512 PNG 再上传,ICO 则由后端提取内嵌 PNG,所以任何格式都能生成可用的跨平台图标。若派生失败则回退为原样透传上传图。推荐上传规范的 512×512 PNG,效果最佳。

该选项默认不勾选:不勾选时行为与之前完全一致,平台 manifest 原样透传。浏览器按自己的节奏重新检查 manifest,已经安装的应用通常要等重新安装(或浏览器下一次 manifest 更新读到新的 src)才会换图标。

同时替换 PWA 安装图标选项

把网页「安装为应用」时,浏览器弹出的安装对话框也会显示你的图标:

PWA 安装对话框

页面 Logo 图标

勾选同时替换页面 Logo 图标,网页左上角品牌 logo 里的鲸鱼图标就会换成你上传的这张——deepseek HARNESS 文字不受影响。选项关闭时显示官方鲸鱼;改动立即生效,刷新/重启都保留。

说明:

  • 该选项默认不勾选。
  • 上传后随时可以勾选/取消,不用重新上传文件。
  • 只影响侧边栏的品牌图标(deepseek HARNESS 旁边那只);PWA 安装图标是上面的另一个选项。

未读红点

有会话发生了事情、而你还没处理时,标签页图标和左上角 Logo 上会出现红底数字角标。实时推送,不需要刷新。

什么算一件事(设置页里逐项可勾选,默认除"我自己停止"外全部打开):

情况 什么时候消失
有会话在等你(审批 / 提问 / 计划待审) 你回答完
有会话干完了一轮 你打开那个会话
有会话报错 / 卡住 / 崩断 / 输出超限 你打开那个会话

规则:

  • 数字 = 几个会话在等你(不是几件事);同一个会话只占一格。
  • 只有子代理会话不计。什么算"你在看"(0.15.2 起):那个会话必须真的显示在主区域的对话里、浏览器标签页在前台(切到别的标签页或最小化不算)、且屏幕上就是它——残留的"最近打开过"不再算数。这两种"不算"都只是暂停计时,不会因此清掉红点;真正算"离开"的只有一件事:去别的会话(那时它的快照被丢弃,你下次进来待够 N 秒才算看过)。你正开着的那个会话也算;要清掉它,切走再切回来即可(你人已经在里面时又结束的那一轮不会自动消,见下一条)。
  • 已读记在宿主($DSH_HOME/custom-favicon/unread-seen.json):进过 = 处理过,刷新/重启不会"复活",任何一台设备上看过,所有设备都不再提醒(开着的页面最多约一分钟收敛,刷新则立刻)。浏览器自己也留一份,但只当缓存兼离线队列——所以清站点数据、换浏览器、无痕窗口都不会把"已读"丢回去。装插件之前的历史结束也不算。
  • 可以要求"待够才算已读":设置页填一个秒数(默认 0)。填 0 = 进去就算已读(红点立即消失);填 N = 你要在那个会话里连续待满 N 秒它才算看过,中途切走就清零重数,刷新页面也重新数。上限 600 秒。 计时只认"你进入会话时已经存在的那次结束":你人已经在会话里的时候它又跑完一轮,那一轮不算数——红点会一直亮着,等你切走再进来才消。这样"我正看着的会话跑完了"这件事不会被几秒钟后自动抹掉(0.11.1 起;此前是"停留满 N 秒就把最新那次结束也标成已读",红点只闪一下)。
  • 数据全部来自官方:turn/end 事件经官方 session projection 折叠,加上官方的"等待交互"注册表——不依赖任何其他插件。
  • 设置页的数据来源可以切到"手动测试",手填数字试看效果。
  • 工作区和会话行上的红点(实验):侧栏每个工作区那一行的文件夹图标右上角一个小红点(收起的工作区里有多少未读,就看它),每个有未读的会话在标题右边跟一个小红点——一眼看出"事情在哪个工作区的哪个会话"。定位走 DSH 自己的 data-row-key(工作区、会话都按 id,不靠标题文字):标题重复不会串行,折叠起来的会话不单独画点——它的行没有渲染,由工作区那一行的点和「待处理」清单代表它。这一项是贴着页面结构做的——DSH 大版本升级后可能失效,失效时只会变成"不显示",不影响红点数字、清单和跳转;设置页里有一个开关可以随时关掉它。
  • 想知道具体是哪个会话? 侧栏「工作区」标题的右上角有一个红底数字(没有待处理时是灰色 0,位置不变);点开是待处理会话清单,按工作区分组(工作区名 · 会话名 · 在等你/刚结束),点其中一条直接跳到那个会话,并在侧栏定位到它(自动展开所在工作区、滚到那一行、闪一下;见下面的「定位」一节)。

进行中黄点

有会话正在跑时会四处出现黄色标记:侧栏「工作区」标题后面是黄色数字(此刻有几个在跑),工作区文件夹图标上是黄点,会话行上也是黄点,左上角 Logo 和标签页图标的右下角是同一个数字。实时推送,不需要刷新。

  • 和未读红点怎么排:数字是上下排——红数字在右上、黄数字在右下(标题后面、左上角、标签页三处都一样);点是左右排——红点在左、黄点在右(工作区文件夹图标与会话行都一样)。文件夹上的红点在黄点出现时会向左让出一个点的宽度,黄点消失后退回原处。两个功能各有各的开关——关掉黄点,红点一个都不少。
  • 黄点就是官方的「进行中」:会话有回合正在跑、而且没有停下来等你——你停下等审批 / 回答 / 计划待审的时候,官方那一行显示的是琥珀色标记,黄点也就不亮。它和官方行首那颗灰色转圈同进同出,差别只是颜色:官方做得安静,这里做得看得见。
  • 事实全部来自官方:会话列表里每个会话的 running(宿主算好、实时推给浏览器),插件不落盘、不推断。所以刷新页面立刻就是对的,进程重启也不会留下一个永远亮着的黄点。
  • 子代理不算:子代理会话根本不在侧栏里显示,它的运行不会让黄点亮起来(与红点同一条规矩,也和官方那一行一致)。
  • 黄数字点得开:点它就弹出进行中的会话清单,按工作区分组(工作区名 · 会话名 · 正在跑 · 会话短号),点其中一条直接跳到那个会话,并在侧栏定位到它(同红字清单:展开工作区 + 滚到那一行 + 闪一下,设置里可关)。它和红数字的「待处理」清单共用同一块浮层,开一个另一个自动关——两个数字在同一行,同时开就是两层叠在一起。清单里没有"跑了多久":官方只给 running 一个布尔,没有"开跑时刻",插件不编造;它也不碰已读水位线——进行中是活的事实,不是待办队列,所以没有"全部标记已读"这一项。没有会话在跑时黄数字本来就不显示,也就没有可点的入口。
  • 数字超过 99 显示 99+,和红点角标同一条规矩;两颗数字徽标一样大:同时出现时它们一起缩到能容下的尺寸、上下排开不重叠(16px 标签页与 24px logo 上两个数字本来就挤,画成一大一小或叠在一起都会读不出来),只有一颗时保持原来的大小(也就是说"红点大小"那三档只在单独出现时生效,两颗同框时三档一样)。
  • 开关在设置 → icon管理 → 进行中黄点,默认打开。

为什么红在上、黄在下,而不是干脆把官方那颗灰转圈染黄?见 docs/adr/0010-running-mark-is-a-second-projection.md。

定位(点清单里的会话之后)

点「待处理」或「进行中」清单里的任意一条(见上面两节),除了打开那个会话,插件还会在侧栏把它找出来:展开它所在的工作区、滚到那一行、并闪一下(约 1 秒),免得你切过去之后不知道它在哪。

  • 官方只开了半个口子:侧栏自己有一套"展开 + 滚动",但它只服务于侧栏自己的搜索框,而且那套状态是它内部的,没有对插件开放的接口(openSession 只管切换主画面)。所以这一项是模拟点击官方的工作区行与「显示更多」按钮做到的。
  • 因此它贴产品结构:DSH 大版本升级后可能失效。失效的表现只有一个——点进去照常打开会话,只是侧栏不动。它有自己的开关(设置 → icon管理 → 未读红点 那一节里),默认开,随时可关。
  • 展开是官方自己记住的:那个工作区的展开状态由 DSH 存在浏览器里,不会自己收回去;不想留着就手动点一下收起。官方自己的搜索跳转也是这个行为。
  • 它不会做过头:只展开、不收起;目标会话被折叠得很深、一次放不出来时,最多点几次「显示更多」就停手(会话照样已经打开了)。

应用图标角标(系统图标上的红点)

把网页安装为应用后,同一个未读数字也会显示在系统图标上。设置在设置 → icon管理 → 未读红点里的「在系统应用图标上显示红点」,默认打开;数字与页内红点严格同值(同一份计算,不额外截断),所以两个地方永远一致。

⚠️ Windows 上它长什么样——取决于你用什么浏览器装:

  • Chrome 装的:Chrome 自己画一张覆盖图标贴在任务栏按钮上——深灰圆 + 白色数字,位置在图标右下角。不是红色圆点,也不是我们图标的一部分。数字超过 99 显示 99+。
  • Edge 装的:Edge 走 Windows 系统徽章通道,外观是系统样式。官方文档说支持数字和空角标,但本机在 Windows 11 上实测(2026-10-01)控制台直接 navigator.setAppBadge(7) 也只出现一个系统蓝点。插件已加 5 秒低频看门狗,会持续重设正向数字,但最终画成数字还是点由 Edge/Windows 决定;想要稳定数字,可用 Chrome 安装。

两种情况下都:只在应用窗口开着的时候存在——关掉窗口角标就消失(这不是插件坏了),窗口全关之后也不会再更新数字(没有推送服务,页面不在就没有代码在跑)。

平台支持情况(截至 0.18.0;每条都有出处,见 ADR 0005 / 0009):

平台 角标 状态
Windows Chrome 81+ 深灰圆 + 白色数字(Chrome 自己画的覆盖图标,右下角) 官方文档示例图 + 源码印证;本机待实测
Windows Edge 81+ 走 Windows 系统徽章通道;官方文档支持数字,但本机实测数字被画成点(待 Edge/Windows 侧修复) 本机实测异常(2026-10-01)
macOS Chrome / Edge 81+ Dock 角标(红底数字) 源码印证(MDN:Chrome 自 81 起支持 Windows 与 macOS;badge_manager_delegate_mac.cc → app shim → NSApp.dockTile.badgeLabel);未在真机实测。不需要通知权限,但只对正在运行的已安装应用有效
macOS Safari 17+(「添加到程序坞」,需 macOS Sonoma 14+) Dock 角标(红底数字) Apple 官方文档支持;必须先授予通知权限才会画——设置页那一行「通知权限」就是为它准备的
iOS / iPadOS Safari 16.4+(加到主屏) 主屏图标上的数字 官方支持;同样必须先授予通知权限(可以用设置页的「允许通知」按钮申请)
Android Chrome / Edge —— 系统不支持:Chromium 在安卓上把角标实现编译掉了;启动器上的圆点只能由通知产生(本插件不发通知)
鸿蒙 —— 无任何公开实现证据,预计不支持(鸿蒙的原生角标只能用 ArkTS 设)
Firefox / Linux Chromium —— 不支持 / 没有系统 API,调用静默无效

关于「通知权限」(0.17.0 起):设置页在角标开关下面多了一行本机读数(已授予 / 未授予 / 已被拒绝 / 本机浏览器没有通知能力)和一个「允许通知」按钮——按钮只在"还没问过"时出现(已授予没什么可问,已被拒绝时浏览器也不会再弹,那一行会直接告诉你去系统设置里改)。它不是插件的开关,而是 Apple 的规则:Safari 把角标权限和通知权限绑在一起,程序坞 / 主屏网页应用没被允许通知,setAppBadge() 就会被丢掉。Chrome / Edge 不需要这一步;申请来的权限只用于画角标,本插件不会发通知。权限在页面外被改动(系统设置里打开/关闭、别的标签页弹过窗)时这一行会自动跟上;刚变成"已授予"的那一刻会立刻把当前数字重设一遍——此前那些调用在没权限时就已经丢了。

设置页里那行「当前环境」只报本机此刻的事实(是不是安全上下文、有没有这个 API、是不是已安装的应用),它不承诺角标一定会出现——因为探测到 API ≠ 会显示(安卓、Linux 上就是如此;WebKit 还多一道通知权限)。是否真正显示由系统决定。

顺便说明两个容易被当成 bug 的现象:

  • 在浏览器标签页里填「手动测试」数字,任务栏没有角标 —— 这是对的:角标只属于安装后的应用窗口,普通标签页调用是静默无操作。
  • 局域网 http://192.168.x.x 访问时没有角标 —— Badging API 要求安全上下文,这时设置页会提示"请用 localhost 或 https 访问"。

设计取舍(为什么不做通知发送、不做 service worker、为什么不按平台门禁)记在 docs/adr/0005-app-icon-badge.md;通知权限这一行(为什么单独探测、为什么不拿它门禁角标调用)记在 docs/adr/0009-notification-permission.md。

支持的格式

  • SVG — 按内容(<svg)识别,并拦截 <script> / 事件属性 / javascript: 注入。上传前会在浏览器里栅格化成 PNG。
  • PNG — 按魔数识别(容忍部分优化器产生的前导字节),并校验 IHDR → … → IEND 结构。
  • ICO / CUR — CUR 当作 ICO 接受。后端会提取内嵌的 PNG;仅含 BMP 帧的 ICO 无法栅格化,会回退为原样透传。
  • JPEG — 直接接受,当作位图基准。

上传的原始文件原样保存,不重新编码,favicon 的透明背景和画质都不损失;派生的 PWA / Logo PNG 集合是从它单独生成的。

持久化

图标按用户保存在 $DSH_HOME/custom-favicon/。恢复默认只清除当前生效标记,已保存的文件会保留,为将来的图标库 UI 做准备。

同一个目录里还有三份 JSON:unread.json(哪些结束原因算未读)、unread-seen.json(已读水位线,{ version, seen: { 会话id → endAt } },上限 400 条、写入是 temp+rename)与 running.json(进行中黄点的开关,就一个布尔)。三份都是可丢的:删掉 unread.json 回到默认规则,删掉 unread-seen.json 则每台浏览器按"上次在线时间"重新起基线(不会把历史红点全部点亮),删掉 running.json 回到"显示黄点"。这里不存"进行中"本身——谁在跑永远是官方宿主的即时事实。

许可证

MIT