Repository navigation
Releases
v0.3.0 — 提示音保存不再静默丢失;音量滑块写入 448ms→0ms
修复:自定义提示音不再静默丢失;音量滑块写入 448ms→0ms
症状
- 选完自定义提示音后,面板立刻显示「已自定义」,但刷新/重开标签页后提示音消失,全程零报错、零提示。
- 真机实测(Chrome headless + CDP):本机单源 localStorage 配额仅 5,242,879 字符,一份 5.4M 字符的 base64 提示音写入
grewBy=0(根本没写进去)、零告警,面板仍显示「已自定义」。 - 音量滑块每次拖动都触发一次全量
setConfig落盘,20 次 set 实测耗时 448ms,拖动明显发涩。
根因
三环叠加,只堵一环修不干净:
setConfig的上限按 4MB(二进制) 计算,但实际存的是FileReader.readAsDataURL产出的 base64,膨胀约 1.33× ≈ 5.33M 字符,正好压在浏览器 5MB 配额线上,必然超限;setConfig里是try { setItem } catch { /* ignore */ }——QuotaExceededError被静默吞掉,调用方永远拿不到失败信号;- 面板读的是内存里的
config对象,所以 UI 立刻反映「已自定义」,磁盘上却什么都没存。
修复
- 提示音上限降到 2MB(二进制),留足余量;二进制超限时面板直接显示红色错误行,不再假装成功。
- 新增
writeVerified():写入后回读比对(有些环境setItem不抛错却没真正落盘),失败时回滚内存配置、还原磁盘上的提示音,并console.warn写明原因。 - 提示音拆到独立 key
dsh.approvalVoice.sound.v1,主配置回到 <1KB;老数据自动迁移,升级后无需重新选文件。 alertedKeys加 FIFO 上限 1000,避免去重集合无限增长;onStorage处理key===null(storage.clear());CLAIM_TTL注释单位改为毫秒。- 音量滑块写入路径不再每次同步全量落盘,实测 20 次 set 从 448ms → 0ms。
升级
dsh plugin --profile web add dsh-approval-voice@0.3.0- 桌面端同理:
dsh plugin --profile desktop add dsh-approval-voice@0.3.0。 - 界面验证:设置 → 常规 → 审批语音提示 → 选一个 >2MB 的音频文件,应出现红色错误行;再拖动音量滑块,界面无卡顿。
- Console 验证:写入失败时应出现
console.warn,而不是毫无动静。 - 旧的自定义提示音会自动迁移到新 key,升级后直接可用。
验证
test-global-scope.mjs断言 27 → 45 条;test-cross-tab.mjs5 个场景全过;node --check lib/client.js通过。- A/B 对照:同一份新测试跑旧版
client.js有 12 条 FAIL,跑新版 45 条全过。 - 真机(Chrome headless + CDP):旧版 5.4M 字符写入静默失败(
grewBy=0)、零告警、面板仍显示已自定义;新版返回QuotaExceededError+console.warn;滑块 20 次 set 从 448ms 降到 0ms。
完整变更:v0.2.2...v0.3.0
v0.2.2 — 桌面端「所有会话」提醒恢复生效(兼容 sessionStatus 全局源)
修复:桌面端里「提醒范围 = 所有会话」不生效(网页版正常)
症状
设置明明是「所有会话」,但只有当前正在显示的那个会话的审批会响;同一个插件在网页版正常、
在桌面端 dsh-desktop 不正常。诊断依据:响铃记录(localStorage 的 dsh.approvalVoice.bell.v1)
里桌面端的 eventKey 全是 ${tabId}:approval:... 前缀(= 只走了 DOM 兜底),
而网页版那次是 session:${sessionId}:approval:... 前缀(= 全局源生效)。
根因
0.2.1 订阅的全局源是网页版核心的 ctx.uiSession.pendingInteractions(dsh 0.1.5-rc.3);
桌面端 dsh-desktop 0.2.0-rc.2 的客户端核心没有这个属性 —— 它只有
ctx.uiSession.sessionStatus(Map<sessionId, {running, pendingInteraction, completionUnread}>,
待处理交互藏在 pendingInteraction 字段,正是桌面端侧边栏「等待审批」读的那个源)。
于是 startGlobalWatcher() 取源失败 → 退回 DOM 卡片监听 → 只有当前视图渲染的卡片能被看到,
现象与 0.2.1 修复前一模一样。
连带问题:桌面端 sessions.list.getSnapshot() 返回 {byId, phase}(没有网页版的 .current),
currentSessionId() 取不到值 → eventKey 退化成 ${tabId}:${kind}:${key},跨标签页去重被架空。
修复
- 新增
resolveGlobalSource()双形状解析:优先uiSession.pendingInteractions(网页版),
否则把uiSession.sessionStatus适配成同形状(桌面端);两者都取不到才退回 DOM 监听并打印明确告警
(告警文案同时点名两个属性)。 currentSessionId()增加回退:sessions.list.getSnapshot().current→
uiSession.adapter.current.getSnapshot().key,桌面端的 eventKey 恢复会话作用域
session:<sessionId>:<kind>:<key>。- 调试接口同步:
window.__approvalVoice.pending()走同一解析,桌面端也能列出所有会话的待处理交互。
升级
# 桌面端
dsh plugin --profile desktop add dsh-approval-voice@0.2.2
# 网页版
dsh plugin --profile web add dsh-approval-voice@0.2.2重启 / 刷新界面后,在 Console 里验证:
window.__approvalVoice.global() // 应为 true(已挂上全局源)
window.__approvalVoice.pending() // 应列出「所有」会话的待处理交互
window.__approvalVoice.currentSessionId() // 应返回当前会话 id实测方法:让另一个没有显示在界面里的会话触发一次审批,不要切过去,应当立刻响。
验证
test-global-scope.mjs 新增 3 组「桌面端核心形状」场景(sessionStatus 源 + 无 .current 快照 +
adapter.current 回退),断言 17 → 27 条全部通过(test-cross-tab.mjs 5 组也全过):
- 桌面端形状:非当前会话的审批响 1 次(修复前为 0),eventKey 是
session:session-bg:approval:approval:5(修复前是 tabId 前缀); - 桌面端形状 + 仅当前会话:其它会话不响、本页会话照响;
- 桌面端形状:DOM 兜底路径的 eventKey 同样带会话作用域。
完整变更:v0.2.1...v0.2.2
v0.2.1 — 「所有会话」提醒覆盖没有开在任何标签页里的会话
修复:设置成「所有会话」后,非当前会话的审批不响
根因
旧实现只嗅 DOM 里的 data-approval-key / data-question-key / data-plan-review-key 卡片,
而 DSH 只在当前正在显示该会话的视图里渲染这张卡片。因此没有开在任何标签页里的会话
(只在侧边栏显示「等待审批」)永远不会触发提醒 —— 单页签切换会话的用户就会感觉
「明明设置了全局提醒,但没有生效」。
修复
- 真的覆盖所有会话:改为主订阅 DSH 客户端的全局待处理交互源
ctx.uiSession.pendingInteractions(侧边栏「等待审批」同源),任何会话的
approval / question / plan-review 出现都会触发提醒;取不到该源时(更老的 DSH)
自动退回 DOM 卡片监听并打印明确告警。 - 跨标签页仍然只响一次:eventKey 由
${tabId}:${kind}:${key}改为会话作用域
session:${sessionId}:${kind}:${key},两个标签页显示同一会话不再各响一次,
且仍然优先由聚焦标签页出声。 - 自定义提示音不再静默失效:保留
<audio>引用(避免被 GC 提前回收导致截断);
未起播(NotAllowedError/ 解码失败 / 1 秒内没有playing事件)时自动回退内置
提示音,失败原因写入console.warn。 - 播放失败会让出并补响:本页确实出不了声时释放跨标签页 claim 并广播
bell-failed,请已解锁音频的其它页签补响一次。 - 调试接口新增
window.__approvalVoice.global()/pending()/currentSessionId()。
升级
dsh plugin --profile web add dsh-approval-voice@0.2.1安装后重启 dsh web 服务并刷新页面。可在浏览器 Console 里用
window.__approvalVoice.global() 确认返回 true(已挂上全局源),
用 window.__approvalVoice.pending() 看到所有会话的待处理交互。
验证
新增 test-global-scope.mjs:直接加载真实 lib/client.js 的假浏览器回归测试,
6 组场景 17 条断言全部通过 —— 单页签 + 非当前会话 / 多页签只响一次 / 仅当前会话 /
自定义提示音失败回退 / 播放失败补响 / uiSession 缺失退回 DOM。
完整变更:v0.2.0...v0.2.1
v0.2.0
0.2.0 — 所有会话提醒 / 仅当前会话开关 / 自定义提示音
新增
- 默认对所有会话提醒(跨标签页广播;一次提醒全浏览器只响一次,聚焦标签页优先)。
- 设置新增「提醒范围」:所有会话 / 仅当前会话。
- 设置新增「提示音」:可上传自定义提示音文件(mp3/wav/ogg,≤4MB)替换内置提示音;默认内置提示音不变。
升级指南
- 升级后默认所有会话提醒;若需旧行为,在「设置 → 常规 → 审批语音提示」把「提醒范围」切到「仅当前会话」。
- 旧配置会自动补齐
scope/sound默认值,无需迁移。 - 完整变更见 CHANGELOG.md。