@cowwo/dsh-icon-custom 0.16.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,6 +43,21 @@ Notes:
43
43
  - It can be toggled at any time after upload — no re-upload needed.
44
44
  - This only affects the sidebar brand mark (the one next to `deepseek HARNESS`); PWA/installed-app icons are a separate option above.
45
45
 
46
+ ## Running marks (yellow)
47
+
48
+ While a session is **running**, a yellow mark appears in four places: after the sidebar's **"工作区" label** a yellow number (how many sessions are running right now), a yellow dot on each **workspace folder icon**, a yellow dot on each **session row**, and the same number on the **top-left logo** and in the **bottom-right corner of the tab icon**. Live, no reload needed.
49
+
50
+ - **How it sits next to the unread red dot**: numbers stack — red top-right, yellow bottom-right (after the label, on the top-left logo, and on the tab icon alike); dots sit side by side — **red on the left, yellow on the right** (on the workspace folder icon and on the session row alike). On the folder icon the red dot steps one dot to the left while the yellow is there, and steps back the moment it is gone. Each feature has its own switch — turning the yellow off leaves every red dot in place.
51
+ - **The yellow mark IS the official "running"**: the session has a turn in flight and is not parked waiting for you — while it waits, the official row shows the amber "waiting for approval / waiting for answer / plan review" and the yellow goes dark. It tracks the official row's **grey spinner** exactly; the only difference is colour — the official one is deliberately quiet, this one is meant to be seen.
52
+ - **Every fact is official**: each session's `running` from the session list (computed by the host, pushed live). The plugin stores nothing and infers nothing, so a reload is immediately right and a **process restart never leaves a yellow mark stuck on**.
53
+ - **Sub-agents never count**: sub-agent sessions are not rendered in the sidebar at all (the same rule the red dot and the official row follow).
54
+ - **The yellow number is clickable**: it opens the list of **sessions running right now**, grouped by workspace (workspace · session · turn in flight · short id), and clicking a row **jumps to that session** and **locates it in the sidebar** (expands its workspace, scrolls to the row, flashes it once — see the "Locating" section below). It shares ONE floating panel with the red count's "needing you" list — opening either closes the other, since two panels under the same header row would stack. The list shows **no "running for how long"**: the official host only gives `running` as a boolean, with no started-at fact, and this plugin does not invent one. It also **never touches the seen watermark** — running is a live fact, not a queue, so there is no "mark all read". With nothing running the yellow number is not shown at all, so there is no entry point to click.
55
+ - Above 99 it shows `99+`, the same rule as the red count; **the two numbers are always the same size** — when both appear they shrink together to the largest pair that fits, stacked and never overlapping (at a 16px tab icon or a 24px logo two digits are already tight: a big one beside a small one, or two overlapping, would make both unreadable), and a number shown alone keeps the normal size (so the small/medium/large setting applies only while a number is on its own — with both shown, all three sizes draw the same).
56
+ - **Clicking a row also locates it in the sidebar**: the session opens AND, in the browsing list, its workspace expands, the row scrolls into view and flashes once (~1s) so you can see where it went. The official sidebar has this exact flow, but only for **its own search box** — the state behind it is private and no service exposes it (`openSession` only switches the main view) — so this drives the shipped workspace row and "show more" button **the way a click would**. It therefore reads the page structure: a DSH upgrade can break it, and the only symptom is a sidebar that does not move (the session still opens). It has its own switch in **Settings → icon管理**, on by default. Note that the expanded state is remembered by the official sidebar itself, so it will not collapse again on its own, and this feature only ever expands — never collapses — a workspace.
57
+ - The switch is in **Settings → icon管理 → Running marks** and is **on by default**.
58
+
59
+ > Why is the red count top-right and the yellow dot bottom-right instead of simply recolouring the official grey spinner? See [`docs/adr/0010-running-mark-is-a-second-projection.md`](./docs/adr/0010-running-mark-is-a-second-projection.md).
60
+
46
61
  ## App icon badge (the red dot on the system icon)
47
62
 
48
63
  Once the page is **installed as an app**, the same unread number is also shown on the **system icon**. The switch lives in **Settings → icon管理 → unread badge** ("Badge the system app icon") and is **on by default**; the number is **exactly the page number** (one computation, no extra clamping), so the two can never disagree.
@@ -53,27 +68,29 @@ Once the page is **installed as an app**, the same unread number is also shown o
53
68
  >
54
69
  > Either way it exists **only while the app window is open**: close the window and it is gone (that is not a bug), and nothing can refresh it afterwards (there is no push server, so with no page running there is no code running).
55
70
 
56
- Platform support as of 0.11.1:
71
+ Platform support as of 0.18.0 (every row has a source; see ADR 0005 / 0009):
57
72
 
58
73
  | Platform | Badge | Status |
59
74
  |---|---|---|
60
75
  | Windows **Chrome** 81+ | Dark circle + **white number** (Chrome's own overlay icon, lower-right) | Confirmed by Chrome's docs screenshot + Chromium source; **not yet measured on this machine** |
61
76
  | Windows **Edge** 81+ | Goes through the Windows badge channel; Microsoft's docs support numbers, but this machine measured the number being drawn as a dot | **Measured anomaly** (2026-10-01) |
62
- | macOS Chrome / Edge 81+ | Dock badge | Unverified (the code is ready; same single call) |
63
- | macOS Safari 17+ ("Add to Dock") | Dock badge | Unverified |
64
- | iOS / iPadOS Safari 16.4+ (Add to Home Screen) | Number on the home-screen icon | Unverified; and it appears **only after notifications have been granted** — this plugin does not request that permission yet, so no badge on iOS for now |
77
+ | macOS **Chrome / Edge** 81+ | Dock badge (red, numeric) | Confirmed in source (MDN: Chrome supports Windows and macOS since 81; `badge_manager_delegate_mac.cc` → app shim → `NSApp.dockTile.badgeLabel`); **not measured on a Mac here**. Needs **no** notification permission, but only works for the **running installed app** |
78
+ | macOS **Safari 17+** ("Add to Dock", macOS Sonoma 14+) | Dock badge (red, numeric) | Supported per Apple's own documentation; it **only appears after notifications are granted** — that is what the permission row in settings is for |
79
+ | iOS / iPadOS Safari 16.4+ (Add to Home Screen) | Number on the home-screen icon | Supported; also **requires notifications to be granted first** (the "Allow notifications" button does that) |
65
80
  | Android Chrome / Edge | — | **Not supported by the system**: Chromium compiles the badge implementation out on Android, and a launcher dot can only come from a **notification** (this plugin posts none) |
66
81
  | HarmonyOS | — | No public evidence of any implementation; **expected unsupported** (native badges there are ArkTS-only) |
67
82
  | Firefox / Linux Chromium | — | No API / no OS API — calls resolve and do nothing |
68
83
 
69
- The "Current environment" line in the settings page reports **local facts only** (secure context? API present? installed as an app?). It never promises a badge will appear, because **detecting the API is not delivery** (that is exactly the Android and Linux case, and iOS adds a permission on top). Whether it really shows is up to the system.
84
+ **About the "notifications permission" row (since 0.17.0)**: below the badge switch the settings page now shows one local reading (**granted / not granted / denied / no Notification API in this browser**) plus an "Allow notifications" button that appears only while the permission is still undecided (nothing left to ask once granted; once denied the browser refuses to prompt again, so the row says where to change it instead). It is **not a plugin switch** — it is Apple's rule: Safari ties the badge permission to the notifications permission, and a Dock or home-screen web app that has not been allowed to notify simply has its `setAppBadge()` calls dropped. **Chrome / Edge need none of this**, and the permission is only ever used to draw the badge — the plugin sends no notifications. If the permission moves outside the page (changed in System Settings, or prompted by another tab) the row follows; the moment it becomes *granted* the current number is re-asserted at once, because every earlier call was dropped while there was no permission.
85
+
86
+ The "Current environment" line in the settings page reports **local facts only** (secure context? API present? installed as an app?). It never promises a badge will appear, because **detecting the API is not delivery** (that is exactly the Android and Linux case, and WebKit adds a permission on top). Whether it really shows is up to the system.
70
87
 
71
88
  Two behaviours that look like bugs but are not:
72
89
 
73
90
  - **A "manual test" number typed in a normal browser tab produces no taskbar badge** — correct: the badge belongs to the **installed app window**; a plain tab's call is silently ignored.
74
91
  - **No badge when the page is reached over LAN `http://192.168.x.x`** — the Badging API requires a secure context; the settings page then says to use localhost or https.
75
92
 
76
- The reasoning (why no notifications, why no service worker, why no platform gating) is recorded in [`docs/adr/0005-app-icon-badge.md`](./docs/adr/0005-app-icon-badge.md).
93
+ The reasoning (why no notifications are sent, why no service worker, why no platform gating) is recorded in [`docs/adr/0005-app-icon-badge.md`](./docs/adr/0005-app-icon-badge.md); the notifications-permission row (why it is probed separately, why it never gates the badge call) is in [`docs/adr/0009-notification-permission.md`](./docs/adr/0009-notification-permission.md).
77
94
 
78
95
  ## Format support
79
96
 
@@ -88,7 +105,7 @@ The uploaded file is stored verbatim (never re‑encoded) so the favicon keeps i
88
105
 
89
106
  Icons are stored per‑user under `$DSH_HOME/custom-favicon/`. "Reset" only clears the active marker; stored files are kept for a future icon‑library UI.
90
107
 
91
- That directory also holds two JSON files: `unread.json` (which turn-end reasons count as unread) and `unread-seen.json` (the seen watermark, `{ version, seen: { sessionId → endAt } }`, capped at 400 entries and written temp-then-rename). Both are **disposable**: deleting the first restores the default rule, deleting the second makes each browser re-baseline at "the last time it was open" (which does not resurrect old dots). The watermark is host-owned on purpose — looked at on any device means read on every device — while each browser keeps its own copy as a cache and offline queue.
108
+ That directory also holds three JSON files: `unread.json` (which turn-end reasons count as unread), `unread-seen.json` (the seen watermark, `{ version, seen: { sessionId → endAt } }`, capped at 400 entries and written temp-then-rename) and `running.json` (the running-mark switch — one boolean). All three are **disposable**: deleting the first restores the default rule, the second makes each browser re-baseline at "the last time it was open" (which does not resurrect old dots), and the third restores "show the yellow marks". **None of them stores what is running** — that is always the official host's live fact. The watermark is host-owned on purpose — looked at on any device means read on every device — while each browser keeps its own copy as a cache and offline queue.
92
109
 
93
110
  ## License
94
111
 
package/README.zh.md CHANGED
@@ -65,7 +65,30 @@ SVG 会在浏览器里先栅格化成 512×512 PNG 再上传,ICO 则由后端提
65
65
  - 数据全部来自官方:`turn/end` 事件经官方 session projection 折叠,加上官方的"等待交互"注册表——**不依赖任何其他插件**。
66
66
  - 设置页的**数据来源**可以切到"手动测试",手填数字试看效果。
67
67
  - **工作区和会话行上的红点(实验)**:侧栏每个**工作区**那一行的文件夹图标右上角一个小红点(收起的工作区里有多少未读,就看它),每个**有未读的会话**在标题右边跟一个小红点——一眼看出"事情在哪个工作区的哪个会话"。定位走 DSH 自己的 `data-row-key`(工作区、会话都按 **id**,不靠标题文字):标题重复不会串行,**折叠起来的会话不单独画点**——它的行没有渲染,由工作区那一行的点和「待处理」清单代表它。这一项是**贴着页面结构**做的——DSH 大版本升级后**可能失效**,失效时只会变成"不显示",**不影响**红点数字、清单和跳转;设置页里有一个开关可以随时关掉它。
68
- - **想知道具体是哪个会话?** 侧栏「工作区」标题的**右上角**有一个**红底数字**(没有待处理时是灰色 0,位置不变);点开是待处理会话清单,**按工作区分组**(工作区名 · 会话名 · 在等你/刚结束),点其中一条**直接跳到那个会话**。
68
+ - **想知道具体是哪个会话?** 侧栏「工作区」标题的**右上角**有一个**红底数字**(没有待处理时是灰色 0,位置不变);点开是待处理会话清单,**按工作区分组**(工作区名 · 会话名 · 在等你/刚结束),点其中一条**直接跳到那个会话**,并**在侧栏定位到它**(自动展开所在工作区、滚到那一行、闪一下;见下面的「定位」一节)。
69
+
70
+ ## 进行中黄点
71
+
72
+ 有会话**正在跑**时会四处出现黄色标记:**侧栏「工作区」标题后面**是黄色数字(此刻有几个在跑),**工作区文件夹图标上**是黄点,**会话行**上也是黄点,**左上角 Logo** 和**标签页图标的右下角**是同一个数字。实时推送,不需要刷新。
73
+
74
+ - **和未读红点怎么排**:数字是上下排——红数字在右上、黄数字在右下(标题后面、左上角、标签页三处都一样);点是左右排——**红点在左、黄点在右**(工作区文件夹图标与会话行都一样)。文件夹上的红点在黄点出现时会向左让出一个点的宽度,黄点消失后退回原处。两个功能各有各的开关——关掉黄点,红点一个都不少。
75
+ - **黄点就是官方的「进行中」**:会话有回合正在跑、而且没有停下来等你——你停下等审批 / 回答 / 计划待审的时候,官方那一行显示的是琥珀色标记,黄点也就不亮。它和官方行首那颗**灰色转圈**同进同出,差别只是颜色:官方做得安静,这里做得看得见。
76
+ - **事实全部来自官方**:会话列表里每个会话的 `running`(宿主算好、实时推给浏览器),插件不落盘、不推断。所以刷新页面立刻就是对的,**进程重启也不会留下一个永远亮着的黄点**。
77
+ - **子代理不算**:子代理会话根本不在侧栏里显示,它的运行不会让黄点亮起来(与红点同一条规矩,也和官方那一行一致)。
78
+ - **黄数字点得开**:点它就弹出**进行中的会话**清单,按工作区分组(工作区名 · 会话名 · 正在跑 · 会话短号),点其中一条**直接跳到那个会话**,并**在侧栏定位到它**(同红字清单:展开工作区 + 滚到那一行 + 闪一下,设置里可关)。它和红数字的「待处理」清单**共用同一块浮层**,开一个另一个自动关——两个数字在同一行,同时开就是两层叠在一起。清单里**没有"跑了多久"**:官方只给 `running` 一个布尔,没有"开跑时刻",插件不编造;它也**不碰已读水位线**——进行中是活的事实,不是待办队列,所以没有"全部标记已读"这一项。没有会话在跑时黄数字本来就不显示,也就没有可点的入口。
79
+ - 数字超过 99 显示 `99+`,和红点角标同一条规矩;**两颗数字徽标一样大**:同时出现时它们一起缩到能容下的尺寸、上下排开不重叠(16px 标签页与 24px logo 上两个数字本来就挤,画成一大一小或叠在一起都会读不出来),只有一颗时保持原来的大小(也就是说"红点大小"那三档只在单独出现时生效,两颗同框时三档一样)。
80
+ - 开关在**设置 → icon管理 → 进行中黄点**,**默认打开**。
81
+
82
+ > 为什么红在上、黄在下,而不是干脆把官方那颗灰转圈染黄?见 [`docs/adr/0010-running-mark-is-a-second-projection.md`](./docs/adr/0010-running-mark-is-a-second-projection.md)。
83
+
84
+ ## 定位(点清单里的会话之后)
85
+
86
+ 点「待处理」或「进行中」清单里的任意一条(见上面两节),除了**打开那个会话**,插件还会在侧栏**把它找出来**:展开它所在的工作区、滚到那一行、并**闪一下**(约 1 秒),免得你切过去之后不知道它在哪。
87
+
88
+ - **官方只开了半个口子**:侧栏自己有一套"展开 + 滚动",但它只服务于侧栏自己的**搜索框**,而且那套状态是它内部的,没有对插件开放的接口(`openSession` 只管切换主画面)。所以这一项是**模拟点击**官方的工作区行与「显示更多」按钮做到的。
89
+ - **因此它贴产品结构**:DSH 大版本升级后可能失效。失效的表现只有一个——**点进去照常打开会话,只是侧栏不动**。它有自己的开关(设置 → icon管理 → 未读红点 那一节里),**默认开**,随时可关。
90
+ - **展开是官方自己记住的**:那个工作区的展开状态由 DSH 存在浏览器里,**不会自己收回去**;不想留着就手动点一下收起。官方自己的搜索跳转也是这个行为。
91
+ - **它不会做过头**:只展开、不收起;目标会话被折叠得很深、一次放不出来时,最多点几次「显示更多」就停手(会话照样已经打开了)。
69
92
 
70
93
  ## 应用图标角标(系统图标上的红点)
71
94
 
@@ -77,27 +100,29 @@ SVG 会在浏览器里先栅格化成 512×512 PNG 再上传,ICO 则由后端提
77
100
  >
78
101
  > 两种情况下都:**只在应用窗口开着的时候存在**——关掉窗口角标就消失(这不是插件坏了),窗口全关之后也不会再更新数字(没有推送服务,页面不在就没有代码在跑)。
79
102
 
80
- 平台支持情况(截至 0.11.1):
103
+ 平台支持情况(截至 0.18.0;每条都有出处,见 ADR 0005 / 0009):
81
104
 
82
105
  | 平台 | 角标 | 状态 |
83
106
  |---|---|---|
84
107
  | Windows **Chrome** 81+ | 深灰圆 + **白色数字**(Chrome 自己画的覆盖图标,右下角) | 官方文档示例图 + 源码印证;**本机待实测** |
85
108
  | Windows **Edge** 81+ | 走 Windows 系统徽章通道;官方文档支持数字,但本机实测数字被画成点(待 Edge/Windows 侧修复) | **本机实测异常**(2026-10-01) |
86
- | macOS Chrome / Edge 81+ | Dock 角标 | 未验证(代码已就绪,同一行不会额外做事) |
87
- | macOS Safari 17+(「添加到程序坞」) | Dock 角标 | 未验证 |
88
- | iOS / iPadOS Safari 16.4+(加到主屏) | 主屏图标上的数字 | 未验证;且**必须先授予通知权限**才会显示——本插件目前不申请通知权限,所以 iOS 上暂时不会出现 |
109
+ | macOS **Chrome / Edge** 81+ | Dock 角标(红底数字) | 源码印证(MDN:Chrome 自 81 起支持 Windows 与 macOS;`badge_manager_delegate_mac.cc` → app shim → `NSApp.dockTile.badgeLabel`);**未在真机实测**。**不需要通知权限**,但只对**正在运行的已安装应用**有效 |
110
+ | macOS **Safari 17+**(「添加到程序坞」,需 macOS Sonoma 14+) | Dock 角标(红底数字) | Apple 官方文档支持;**必须先授予通知权限**才会画——设置页那一行「通知权限」就是为它准备的 |
111
+ | iOS / iPadOS Safari 16.4+(加到主屏) | 主屏图标上的数字 | 官方支持;同样**必须先授予通知权限**(可以用设置页的「允许通知」按钮申请) |
89
112
  | Android Chrome / Edge | —— | **系统不支持**:Chromium 在安卓上把角标实现编译掉了;启动器上的圆点只能由**通知**产生(本插件不发通知) |
90
113
  | 鸿蒙 | —— | 无任何公开实现证据,**预计不支持**(鸿蒙的原生角标只能用 ArkTS 设) |
91
114
  | Firefox / Linux Chromium | —— | 不支持 / 没有系统 API,调用静默无效 |
92
115
 
93
- 设置页里那行「当前环境」只报**本机此刻的事实**(是不是安全上下文、有没有这个 API、是不是已安装的应用),它**不**承诺角标一定会出现——因为**探测到 API ≠ 会显示**(安卓、Linux 上就是如此;iOS 还多一道通知权限)。是否真正显示由系统决定。
116
+ **关于「通知权限」(0.17.0 起)**:设置页在角标开关下面多了一行本机读数(**已授予 / 未授予 / 已被拒绝 / 本机浏览器没有通知能力**)和一个「允许通知」按钮——按钮只在"还没问过"时出现(已授予没什么可问,已被拒绝时浏览器也不会再弹,那一行会直接告诉你去系统设置里改)。它**不是插件的开关**,而是 Apple 的规则:Safari 把角标权限和通知权限绑在一起,程序坞 / 主屏网页应用没被允许通知,`setAppBadge()` 就会被丢掉。**Chrome / Edge 不需要这一步**;申请来的权限只用于画角标,本插件不会发通知。权限在页面外被改动(系统设置里打开/关闭、别的标签页弹过窗)时这一行会自动跟上;刚变成"已授予"的那一刻会立刻把当前数字重设一遍——此前那些调用在没权限时就已经丢了。
117
+
118
+ 设置页里那行「当前环境」只报**本机此刻的事实**(是不是安全上下文、有没有这个 API、是不是已安装的应用),它**不**承诺角标一定会出现——因为**探测到 API ≠ 会显示**(安卓、Linux 上就是如此;WebKit 还多一道通知权限)。是否真正显示由系统决定。
94
119
 
95
120
  顺便说明两个容易被当成 bug 的现象:
96
121
 
97
122
  - **在浏览器标签页里填「手动测试」数字,任务栏没有角标** —— 这是对的:角标只属于**安装后的应用窗口**,普通标签页调用是静默无操作。
98
123
  - **局域网 `http://192.168.x.x` 访问时没有角标** —— Badging API 要求安全上下文,这时设置页会提示"请用 localhost 或 https 访问"。
99
124
 
100
- 设计取舍(为什么不做通知、不做 service worker、为什么不按平台门禁)记在 [`docs/adr/0005-app-icon-badge.md`](./docs/adr/0005-app-icon-badge.md)。
125
+ 设计取舍(为什么不做通知发送、不做 service worker、为什么不按平台门禁)记在 [`docs/adr/0005-app-icon-badge.md`](./docs/adr/0005-app-icon-badge.md);通知权限这一行(为什么单独探测、为什么不拿它门禁角标调用)记在 [`docs/adr/0009-notification-permission.md`](./docs/adr/0009-notification-permission.md)。
101
126
 
102
127
  ## 支持的格式
103
128
 
@@ -112,7 +137,7 @@ SVG 会在浏览器里先栅格化成 512×512 PNG 再上传,ICO 则由后端提
112
137
 
113
138
  图标按用户保存在 `$DSH_HOME/custom-favicon/`。**恢复默认**只清除当前生效标记,已保存的文件会保留,为将来的图标库 UI 做准备。
114
139
 
115
- 同一个目录里还有两份 JSON:`unread.json`(哪些结束原因算未读)与 `unread-seen.json`(已读水位线,`{ version, seen: { 会话id → endAt } }`,上限 400 条、写入是 temp+rename)。两份都是**可丢的**:删掉 `unread.json` 回到默认规则,删掉 `unread-seen.json` 则每台浏览器按"上次在线时间"重新起基线(不会把历史红点全部点亮)。
140
+ 同一个目录里还有三份 JSON:`unread.json`(哪些结束原因算未读)、`unread-seen.json`(已读水位线,`{ version, seen: { 会话id → endAt } }`,上限 400 条、写入是 temp+rename)与 `running.json`(进行中黄点的开关,就一个布尔)。三份都是**可丢的**:删掉 `unread.json` 回到默认规则,删掉 `unread-seen.json` 则每台浏览器按"上次在线时间"重新起基线(不会把历史红点全部点亮),删掉 `running.json` 回到"显示黄点"。**这里不存"进行中"本身**——谁在跑永远是官方宿主的即时事实。
116
141
 
117
142
  ## 许可证
118
143