Skip to content

Releases

v1.9.1 视频打磨完成

Choose a tag to compare

@ysl289 ysl289 released this 01 Nov 04:57

docs: 视频打磨完成 —— 补齐动画层次与音画对位

体检发现两个遗留问题,都修了。

修的问题

1. S2 痛点的动画层次太少

体检测出 S2 只有 2 个动画操作,其他场景 4–24 个。原因是它只有打字机,
元素没有进出场,看起来比别的场景「薄」。

补了 4 个层次:

  • 终端窗口从下方升起(位移 34px + 缩放 0.965→1)
  • 计时器数字每次跳动做一次缩放脉冲(1.0→1.14)
  • 「等待审批」徽章出现后,终端边框开始呼吸(黄光 28→54px)
  • 整窗阴影随呼吸同步变化

现在 S2 有 5 个动画操作,7 个场景全部达标。

2. 音效比画面动作早了 1.1 秒

这是更要紧的问题。原来的音效时间是「估的」,不是从动画代码里算的:

动作 画面实际时刻 原音效时刻 误差
S2 审批徽章出现 9.52s 9.90s +0.38s
S3 裂缝 14.96s 14.85s −0.11s
S3 答案砸下 15.68s 16.70s +1.02s
S4 光点到达手机 26.48s 25.35s −1.13s
S5 卡片弹入 29.90s 29.50s −0.40s

S4 那个尤其明显:通知音先响了一秒,光点才飞到手机——因果感全毁了。

现在全部按动画代码里的 p 值反算:

  • 公式:场景起始时间 + p值 × 场景时长
  • 例:S4 光点在 p=0.72 到达,场景 20s 起、9s 长 → 20 + 0.72×9 = 26.48s

修正后 5 个音效的峰值都比背景高 5.4–11.1dB,误差归零。

验收

检查 结果
画面完整性 9/9 场景通过,内容占比 39.3%–48.2%
字幕烧入 7/7 通过
字幕位置 86% 高度,不遮挡主体
音频电平 mean −26.3dB,max −8.5dB
音效对位 5 个关键点峰值高于背景 5.4–11.1dB
动画层次 7/7 场景达标(S2 从 2 提升到 5)
视觉焦点连续性 25 个采样点全部有明确内容

成品

docs/demo-v3.mp4 — 50.5 秒,4.34 MB,1280×720 @ 30fps

v1.9.0 新视频:视觉重做 + 声音设计

Choose a tag to compare

@ysl289 ysl289 released this 01 Nov 03:46

这一版做了什么

新视频(v4,视觉重做 + 声音设计)

用户要求「画面也要打磨」。这一版把视觉整个重做了,并补上了声音设计。

项 上一版 这一版
手机 圆角矩形 完整 iPhone 模型:灵动岛、状态栏(信号/电池)、锁屏时间、壁纸渐变、iOS 风格通知卡
因果表达 两个框 + 一条线 光点从电脑沿贝塞尔弧线飞到手机,到达时手机亮起、通知弹出
光 无 背景光斑、屏幕自发光、手机环境光晕、关键元素辉光
景深 单层 三层:背景光斑(模糊)/ 主体 / 暗角
文字 淡入 打字机逐字 + 光标闪烁
转场 硬切 扫描线扫过(只用在 4 个关键转场)

声音设计:从「一段正弦波垫底」升级为完整设计。

  • 配乐:A 小调 → F → C 大调 → A 小调的四段和声进行,配合「压抑 → 转折 → 明亮 → 收」
  • 5 个音效,每个都有明确存在理由(不堆砌):打字声、通知音、冲击声、警示音、转场扫过
  • 砍掉了 5 个语义重叠的(powerdown / rip / pop / click / chord)—— 多了显廉价

量化验收

检查 结果
画面完整性 9/9 场景通过,内容占比 39.3%–48.2%(上一版 8.6–32.6%)
字幕烧入 7/7 通过
字幕位置 86% 高度,不遮挡主体
音频电平 mean -26.3dB,max -9.0dB
音效对位 5 个关键音效峰值高于背景 4.0–8.7dB
光点轨迹 (6,127) → 弧线 → (240,136)

修的两个 bug

  1. #progress 元素漏写 —— JS 引用但 HTML 没有,导致 renderFrame 抛 null 错,所有动画都不执行。加了 id 存在性自检,35 个引用全部验证。
  2. 配乐时长不匹配 —— pad.wav 49.5s 而视频 50.5s,amix duration=first 把混音截断,成片少 1 秒。

安装

命令行:在 DSH 对话里说「帮我装 dsh-alert」

从 GitHub:

cd ~/.dsh/profiles/desktop
npm install github:ysl289/dsh-alert
# 在 package.json 的 dsh.profile.bundles 里加 "dsh-alert"

下载包:见下方 Assets

装完必须重启 DSH(Desktop 重启应用;Web 重启 dsh web 再刷新浏览器)。


附件说明

文件 内容
dsh-alert-1.9.0.tgz npm 包,26 个文件,包含全部代码
dsh-alert-1.9.0-source.zip 完整源码,含测试(422 项)与文档

注意:GitHub Release 页面只显示压缩包,不显示单个代码文件。
要浏览源码请去 仓库文件树。

v1.8.1 新视频:摸鱼叙事版

Choose a tag to compare

@ysl289 ysl289 released this 30 Oct 15:29

新视频

49.5 秒,第一人称摸鱼叙事。开场就是你说的那句:

AI 在电脑上跑任务
你在客厅摸鱼刷手机
它跑完了 —— 你不知道

第三句出现时,画面右上角的电脑屏幕会自己熄灭 —— 不用旁白,一眼看懂「错过了」。

全片结构

时间 场景
0–6s 钩子:摸鱼场景 + 屏幕熄灭
6–14s 痛点:终端卡在「等你审批」,计时器 0→28 分钟
14–20s 转折:「每跑一次你要回来瞄一眼」→ 一道裂缝 →「那就让它自己叫你」
20–28s 它是什么:左边电脑四个触发点,右边手机通知,中间发光连线
28–35.5s 七个通道:ntfy 高亮推荐,微信标灰写「有门槛」
35.5–41.5s 四道防打扰闸门
41.5–49.5s 结尾:仓库地址 + $ 帮我装 dsh-alert 命令条 + 「去摸鱼吧,有事它叫你」

按审美 skill 做的取舍

读了 taste 和 video-editing 两个 skill,落实了几条:

  • 一镜一强调色:蓝=正确/成功,红=出错/焦虑,全片红只出现 3 次
  • 硬切不溶解:场景之间直接切
  • 裁掉 UI chrome:还原界面时只留内容区
  • 不让画面死黑:3.5% 不透明度网格底纹

量化验收

不只看「命令跑通」,逐项验:

检查 结果
每个场景有画面 8/8 通过(内容占比 8.6%–32.6%)
字幕真的烧进去 7/7 通过(底部亮像素 +1655 ~ +2772)
字幕不挡内容 字幕在 85% 高度,主体在 24–45%
音频不是静音 -26.5 dB
SRT 格式 19 条,序号/时间/重叠全过

第一版 S3 转折那场只有 11/64 网格填充,太空。加了「问题句 → 发光裂缝 → 答案 → 下划线 → 四个标签」五层结构,并把这场的时长从 4 秒扩到 6 秒。


视频文件:docs/demo-v2.mp4(1.90 MB)· 字幕源 docs/demo-v2.srt

完整说明见 README

v1.8.0 完整说明 + 字段补全 + 铃铛图标

Choose a tag to compare

@ysl289 ysl289 released this 30 Oct 14:59

装法

插件市场:DSH → 设置 → 插件 → 搜 dsh-alert → 安装 → 重启 DSH

命令行:在 DSH 对话里说「帮我装 dsh-alert」

手动:下载下面的 dsh-alert-1.8.0.tgz,然后

cd ~/.dsh/profiles/desktop
npm install /path/to/dsh-alert-1.8.0.tgz
# 在 package.json 的 dsh.profile.bundles 里加 "dsh-alert"

装完必须重启 DSH(Desktop 重启应用;Web 重启 dsh web 再刷新浏览器)。


这个版本做了什么

1. 字段说明全部补齐

原来 20 个字段只有 placeholder,没告诉用户「去哪儿拿」。现在缺 help 的字段:0 个。

重点补的都是实际踩过的坑:

  • QQ openid:写明不是 QQ 号,来自 C2C_MESSAGE_CREATE 事件的 author.user_openid
  • 企业微信:CorpID / Secret / AgentId 分别写清在管理后台哪一层
  • Bark:只取斜杠后那段
  • Server酱:SCT 与 sctp 两代 key 的区别
  • webhook:按地址开头选平台

2. 设置侧边栏齿轮换铃铛

先说清为什么大家都是齿轮 —— 挖 app.asar 确认,宿主代码里是这样:

/** Nav glyph by section id; unknown ids fall back to the settings gear. */
function navIcon(id) {
  if (id === 'account') return IconUser...
  if (id === 'models') return IconData...
  ...
  return IconSettingsOutlineMedium;   // ← 所有第三方插件都落到这里
}

register() 里根本没有 icon 字段,插件侧改不了,不是我们没写对。

所以做了受控 DOM 补丁:按标题文字认出自己那一项,把 svg 换成铃铛。
按文字而不是按 class,因为宿主 class 是 hash 过的(AnXs9G_navCell),跟着写必挂。

安全边界:找不到就什么都不做、只认文字完全相等那项、确认是 svg 才换、
打过标记不重复处理、dispose 时清理观察器。

真实浏览器验证:只有「手机提醒」换成铃铛,账户和模型保持齿轮。

3. README 重写

从 391 行散装改成 564 行、31 个章节,按读者动线排:
它解决什么 → 特性 → 触发时机 → 防打扰 → 通道对比 → 安装 → 快速开始
→ 各通道配置 → 设置项 → 联动 → 设计 → FAQ → 排错 → 开发 → 隐私。

通道按真实动手成本排序并标注限额,企业微信明确写清门槛,
排错表覆盖实测到的全部错误码。


附件

文件 用途
dsh-alert-1.8.0.tgz npm 包(81 KB,26 个文件),手动安装用
dsh-alert-1.8.0-source.zip 完整源码(2.4 MB,70 个文件),含测试与文档

测试

422 项断言,三平台(ubuntu/windows/macos)× 两版本(node 20/22)CI 全绿。

新增 10 条导航图标测试,其中一条专门挡实现错误:
把 ['rect', 6, 2, ...] 当成属性对象遍历了(ICON_PATHS 是位置参数数组)。


完整说明见 README

v1.7.3 全通道真打审计

Choose a tag to compare

@ysl289 ysl289 released this 30 Oct 13:30

fix(channels): 全通道真打审计,修 4 处问题

按「用无效凭据打真实接口,看服务端怎么答」的办法把所有通道过了一遍 ——
这就是上次抓出 Server酱 522 端点 bug 的办法。

审计结果:端点全部正确

通道 真实响应 判定
ntfy HTTP 200,消息真的发出去了 ✅
Bark 400 failed to get device token ✅ 端点对,仅 key 无效
Server酱 400 [AUTH]错误的Key ✅
Webhook/钉钉 200 errcode 300005 token is not exist ✅
企业微信 40013 invalid corpid(gettoken 接口通) ✅
QQ 10004 机器人不存在(getAppAccessToken 接口通) ✅

两段式通道(企业微信 / QQ)的 prepare → build 路径也验证了,
token 换取接口可达,错误如实带出。

修了 4 处真问题

1. ntfy 默认优先级太低(最要紧的一条)

原来默认 default = priority 3。按 ntfy 的语义,3 只进通知栏、不弹窗。
这插件的全部意义就是「别错过」,结果装好之后通知悄悄躺在通知栏里 —— 等于白装。

改成 high(4,弹窗置顶)。实测服务端记录 priority: 4。

2. Bark 的排查规则误伤其他通道

规则写的是 /Bark.*code 400|failed to get device|key/i ——
里面那个裸的 key 会把 Server酱 的 [AUTH]错误的Key
和钉钉的 keywords not in content 一起匹配进来。
用户遇到 Server酱 的 key 错,看到的是「Bark Key 不对」,被指错方向。

改成只认 Bark 特有说法:failed to get device token|Bark code \d+。

3. /errcode 400/ 吃掉 40013

400 是 40013 的前缀,所以 CorpID 错误会同时显示
「CorpID 不对」和「消息格式对不上」两条,自相矛盾。加了 \b 边界。

4. check() 丢了服务端的错误原因

Bark 和 Server酱 在非 2xx 时只返回 HTTP 400,
把 body 里的 failed to get device token / [AUTH]错误的Key 丢了。
排查规则是靠文本匹配的,丢了 message 就永远命中不了。
现在会拼上 HTTP 400: <服务端原因>。

顺带补齐的排查规则

  • QQ 10004 机器人不存在 → AppID 不对(原来没规则)
  • 我们自己输出的「无效收件人:xxx」→ 并进 81013 那条
  • 通用 HTTP 5xx → 对方服务器出错,不是你配错
  • HTTP 413 → 消息太长,关掉「带上 AI 回复」或调短摘要

测试(402 → 412)

新增 test/troubleshoot.test.mjs 10 条,用实测到的真实错误文本逐条验证:

  • 每条错误至少命中一条规则
  • 每条错误命中语义正确的那条
  • Server酱 的 [AUTH] 错误不能误判成 Bark
  • 钉钉关键词错误不能误判成 Bark
  • errcode 40013 不能误判成格式错误
  • 真正的格式错误仍能命中格式规则
  • 每条规则都必须有 fix(只给标题不给做法等于没解决)

这些断言直接把这次发现的三个误匹配钉死,以后改规则会立刻挂。

v1.7.2 修 IM 认不到服务(真根因)

Choose a tag to compare

@ysl289 ysl289 released this 30 Oct 13:11

fix(im): 装好了也认不到 —— Cordis 必须 inject 才能读兄弟插件的服务

用户反馈「我都装好了根本选择不了啊,检测也屡屡失败」。

真正的根因

不是用户没装好。dsh-im 5.7.0 确实装在 profile 里(bundles 第 22 项),
被禁用也没有。问题是我的代码从来读不到它的服务。

Cordis 里兄弟插件的服务不能直接读:

ctx.dshIm        // ❌ throws "cannot get property \"dshIm\" without inject"
ctx.inject(['dshIm'], scoped => {
  scoped.dshIm   // ✅ 只有这条路
})

我原来用 try/catch 包住直接访问,异常被吞掉、返回 undefined,
于是 im.installed 永远是 false —— 界面一直让用户去装,点「发送测试」必然失败。

这个约束我用一个隔离实验确认过:root 上 provide 服务、两个插件是兄弟 fiber,
B 直接读会抛,C 用 inject 能拿到。

顺带发现同一个 bug 的第二个受害者

costMeter 也用了同样的 safeGet(ctx, 'costMeter') ——
成本联动从上线起就没生效过,只是没人发现。一并修了。

改了什么

1. 服务一律走运行时 inject

ctx.inject(['dshIm'], scoped => { imService = scoped.dshIm })
ctx.inject(['costMeter'], scoped => { costService = scoped.costMeter })

桥接层的入参从 ctx 改成服务对象或取服务的函数,
彻底避免再有人误以为「读 ctx 就行」。

2. 联动探测分成两层

  • loader 类(pocket / claudeStyle / desktopNotify):读 loader,apply 时就能定
  • 服务类(dshIm / costMeter):等 inject 回调才知道

之前两层都一次算死,inject 还没回调就把服务记成 false,之后再也不更新。
现在服务类改成惰性读取,就绪后立刻反映。

3. 顺手修掉一个 UI 显示问题

渠道代号原来直接显示内部值(feishu),改成「飞书」。

验证(这次不看测试,看真实 Cordis)

写了两个真实 Cordis 集成脚本:

=== status ===
im.installed: true
im.bots: [{"botId":"bot_real","channel":"feishu"}]

=== 发送测试 ===
ready: im
{"ok":true,"results":[{"channel":"im","ok":true,"attempts":1}]}
dsh-im 收到调用次数: 1

修前:installed=false、bots=[]、发送必失败。
修后:三项全通过。

测试(393 → 402)

旧的 im.test.mjs / integrations.test.mjs 是拿 { dshIm: fake } 当 ctx 传的 ——
在 JS 里能过,在真实 Cordis 下必然失败,正是它们给了假的安全感。

现在改成:

  • 单元测试直接喂服务对象
  • 集成测试的 mock ctx 故意不把 dshIm 挂在 ctx 外层,
    只通过 inject 提供 —— 谁再改回「直接读 ctx」会立刻挂
  • 新增断言:服务挂在 ctx 上但没走 inject 时,必须报 false

一个调试教训

我第一版验证脚本写 plugin.apply(ctx, {}).catch(...),
但 apply 是同步函数,.catch 抛 TypeError → Cordis 判定 fiber 失败
→ 释放 inject 作用域 → imService 被清空。看起来像「插件没认到服务」,
其实是我的验证脚本错了。这类假阴性最耗时间。

v1.7.1 IM 引导修:必须重启 DSH

Choose a tag to compare

@ysl289 ysl289 released this 30 Oct 12:17

fix(im): 第三步做完没反应 —— 没告诉用户要重启 DSH

用户实测反馈:跟着 IM 引导做完第三步,界面上什么都没有出现。

根因

我写了「装好并配好目标后,这一页会自动变成选择框」—— 这是错的。
dsh-im 自己的 README 明确写着:

安装后重启 Desktop 或 dsh web,Web 刷新浏览器,再打开「设置 → IM机器人」

插件不是热加载的。用户装完 dsh-im 后没有重启 DSH,面板里的 ctx.dshIm
压根不存在,所以 installed 一直是 false,界面当然不变。

用户完全按引导做完了,是我给了错误预期。

改了什么

1. 说清必须重启

  • 在「没装」分支加了警示块:装完必须重启 DSH,插件不是热加载的
  • 去掉「会自动变成选择框」这个错误承诺

2. 三个分支都加「重新读取」按钮
原来只有页面底部的「重新读取」,用户在 IM 面板里改完 dsh-im 的东西
得自己去找。现在:

  • 没装 → 「我已配好,重新读取」
  • 装了没机器人 → 「配好了,重新读取」
  • 有机器人 → 底部「重新读取」+ 说明「这里不会自动刷新」

3. load() 补上 busy 状态
原来 busy 只服务于保存按钮,点刷新没有任何反馈。
现在读取期间按钮显示「读取中…」。

4. 渠道代号翻成中文
下拉框原来直接显示 dsh-im 的内部代号(feishu),改成「飞书」。
覆盖 wechat/wecom/feishu/dingtalk/qqbot/telegram/slack/discord/whatsapp/imessage/matrix。
认不出的代号原样显示,缺字段显示「未知渠道」,都不崩。

顺带核实了一个我担心的点

怕是 listBots() 返回结构写错了,专门去 dsh-im 的 10MB 打包产物里挖了实现:

async listBots(){
  let t=[];
  for(let{adapter:o} of this.#e.values())
    for(let e of await o.listBots()) t.push({botId: aa(e), channel: o.channel})
  return t          // ← {botId, channel} 对象数组,我的假设是对的
}

所以桥接层没问题,问题纯粹在「没告诉用户重启」。

测试(388 → 393)

  • IM 面板必须说明装完要重启 DSH
  • 不能再出现「会自动变成选择框」这种承诺
  • 四个分支都要有「重新读取」入口
  • 渠道代号必须翻成中文(feishu → 飞书等)
  • 未知代号原样显示、缺字段显示占位,都不崩

v1.7.0 引导逐个核对完成

Choose a tag to compare

@ysl289 ysl289 released this 30 Oct 10:13

把所有渠道引导逐个核完了,依据是实测 + 官方文档,不再有凭印象写的部分。

Webhook:实测三家

平台 无效 token 返回
钉钉 {"errcode":300005,"errmsg":"token is not exist"}
飞书 {"code":19001,...}(用 code 不是 errcode)
企微群 {"errcode":93000,...}

修的错

  • 钉钉 token 错误码 300001 → 300005(实测)
  • 补上飞书 / 企微群的独立排查规则
  • 步骤顺序反了:原来第 1 步让选平台,但用户还没拿到地址,无从选起。
    改成先拿地址,再按地址开头(oapi.dingtalk.com / open.feishu.cn / qyapi.weixin.qq.com)识别

QQ:拿到官方文档正文,纠正三处

  • Token 已弃用 —— 官方明确只支持 Access Token 鉴权,引导里补了「Token 字段不用管」
  • openid 来自 C2C_MESSAGE_CREATE 事件,不是「去开发设置翻日志」
  • 40034105 对应 C2C_MSG_REJECT —— 用户在机器人资料卡关了主动推送

保留了「用 dsh-im 更省事」的替代方案:这条路客观难,不给退路等于坑人。

Bark

用 API 探测确认三种地址形式都识别同一个 device token,验证了「Key 取斜杠后那段」是对的。

测试

365 → 388 项。新增 19 条 webhook 测试(三家格式差异、错误码判定、模板替换)

  • 4 条 QQ 官方事实断言。

docs/GUIDE-SOURCES.md 补了 Webhook 实测记录表、
QQ 官方事实表,并把待补清单缩到只剩三项。

v1.6.3 · 加 44 秒介绍视频 + 企业微信难度重排

Choose a tag to compare

@ysl289 ysl289 released this 30 Oct 08:42

视频

仓库里加了 44 秒介绍片:docs/demo.mp4(2.2 MB,中文字幕)

7 个场景:钩子 → 痛点 → 方案 → 通道 → 防打扰 → pocket 联动 → CTA

做法:HTML+CSS 动画 → 无头 Chrome 经 CDP 逐帧截图(50ms/帧)→ ffmpeg 编码 → 烧字幕 → 加合成配乐。

顺带修:企业微信难度问题

用户实测卡在 errcode 60020(企业可信IP)。家庭宽带是动态 IP,个人用户基本做不成。

按真实动手成本重排通道(原来按「官方程度」排,不合理):

\
旧:ntfy → 微信 → Bark → QQ → Server酱 → Webhook → 自定义 → IM
新:ntfy → Bark → Server酱 → Webhook → 微信 → 自定义 → QQ → IM
\\

企业微信降到第 5 位,卡片直标「较麻烦 / 要注册企业、配 IP」,选中后顶部明确警告,并给出「换 ntfy」这条能走通的路。

测试

343 项,新增 4 条锁住这次的教训:

  • 企业微信必须在选之前说明 IP 限制
  • 通道按真实成本排序,ntfy 必须第一
  • 麻烦的通道要在卡片上就标出来
  • 60020 的排查建议要给活路

v1.6.1 · 修引导:企业微信补上「进管理后台」

Choose a tag to compare

@ysl289 ysl289 released this 30 Oct 08:04

修用户实际卡住的引导问题。

问题

跟着企业微信引导做到第 2 步就卡住了。原文写「管理后台 → 应用管理 → 创建应用」,但从没说管理后台是什么、怎么进 —— 用户手上只有手机上的企业微信 App,浏览器里根本没有那个页面。

修复

  • 企业微信引导从 5 步扩到 7 步,第 2 步专门讲怎么进管理后台(浏览器 → 右上角「管理后台」→ 微信扫码),并给了直达链接、点明「这不是手机 App」
  • 注意事项不再写在正文里(\注意:xxx\ → 独立 note 字段),改用警示底色 + 橙色边框 + 图标标注
  • 「在哪儿做」里不再混进注意事项,位置和提醒分开
  • 清掉会被渲染成纯文本的 Markdown 星号

测试

从 331 增到 339 项,新增的都在防这类问题:

  • 提到「管理后台」这类入口必须说清怎么进
  • 必须点明是网页不是 App
  • 每步的「在哪儿做」必须含可操作线索
  • 注意事项必须走 note,不靠正文「注意:」
  • 渲染层必须把 note 渲染成带警示色的块

CI 三平台四版本全绿。