dsh-no-long-sit
DSH 的久坐与喝水提醒:一只猫待在 Web GUI 角落帮你盯时间。专注周期 30-90 分钟结束后居中弹窗并连响三声,喊你起来走两步、喝口水;点「再等会」给出 1 条久坐的真实案例 + 1 条缺水的真实案例(内置 17 条,均带来源媒体、发布日期和原文链接);点「结束」输出本次工作评价:工作时长、完成轮数、成功中断次数、推迟次数、建议喝水次数与好/中/差评分。猫可以拖到任意位置并记住位置,四个状态各一段循环动画。计时与账本在宿主侧,刷新页面不丢;只在 DSH 页面开着时提醒。周期时长、提示音音量、猫的不透明度、评价阈值都能在设置页调整。
安装
dsh plugin --profile web add dsh-no-long-sit
沙箱测试
这个插件还没测过。提交测试 →
README
dsh-no-long-sit · 猫猫桌宠(久坐提醒)
DeepSeek Harness 的桌宠:一只住在 Web GUI 角落的猫猫,可拖到任意位置,四个状态各有循环动画。
它现在最先给你干的一件事是久坐提醒——到点居中弹窗 + 三声滴滴喊你起来走动、喝口水;
你说"再等会"它就掏出带真实来源的案例劝你;本次工作结束时给一份好/中/差评价。
后续会继续长功能,目标是做一只有用的桌宠。
[English →](./README.md)
功能
能力 · 说明
猫猫桌宠 · 右下角常驻,四个状态循环动画(趴着/起身/喝水/睡着);可拖到任意位置并记住;不透明度可调;素材缺失自动回落内置 SVG
定时提醒 · 专注周期到点 → 居中弹窗(遮罩,无法忽略)+ 三声"滴滴滴"提示音,带休息倒计时
三按钮闭环 · 【休息好了】直接开下一轮专注 /【再等会】看案例、N 分钟后重弹 /【结束】出总结
伤害案例窗 · 内置 17 条真实案例(久坐 9 / 缺水 8),每次只挑 1 条久坐 + 1 条缺水:走洗牌袋——一轮内不重复、相邻两次必不同,袋子存 localStorage,刷新/重启也接着上次继续(不会每次开机都从第一条开始);每条带来源媒体、发布日期、原文链接
休息结束 · 休息倒计时跑完 → 再响一次"滴滴滴" + 弹「回来干活」,点【开始干活】进下一轮;若一直没人点,超过等待上限自动开下一轮(默认 10 分钟,可设 0 关闭)
总结评价 · 本次工作时长、完成专注轮数、成功中断次数、提醒触发次数、再等会次数、建议喝水次数 → 好/中/差 + 猫猫点评
设置面板 · 称呼、专注周期 30–90 分钟、休息周期 3–15 分钟、再等会间隔与上限、提示音开关与音量、桌宠不透明度、等待上限、评价阈值、悬浮宠物开关;带 「恢复默认值」 一键回退(两段确认)
桌宠可拖动 · 按住猫猫拖到任意位置(自动夹在视口内),位置用 localStorage 记住;拖动不会误触菜单,单击才开菜单
计时权威在宿主 · 刷新页面/切标签不丢计时;多标签页不会各弹各的
默认:每 30 分钟专注 → 提醒 → 休息 5 分钟 → 回来确认 → 下一轮。
安装
# 桌面版(Electron):DSH_HOME 指向桌面数据目录 $env:DSH_HOME='D:\dsh-desktop-home\data' & '<DSHzm>\resources\runtime\cli\bin\dsh.cmd' plugin --profile desktop add <本插件路径或包名> # Web 版 $env:DSH_HOME='C:\Users\<你>\.dsh' dsh plugin --profile web add dsh-no-long-sit
装完重启 DSH(bundle 层在启动期合成)。重启后右下角出现猫猫即表示加载成功。
设置项
设置 → 插件 → 猫猫 · 久坐提醒:
字段 · 默认 · 范围 · 含义
address · 大人 · ≤24 字 · 猫猫对你的称呼(宿主没有统一的"用户称呼"字段,所以由插件自己持有)
focusMinutes · 30 · 30–90 · 专注周期(分钟)
breakMinutes · 5 · 3–15 · 休息周期(分钟)
snoozeMinutes · 5 · 1–15 · 点【再等会】后多久重新弹提醒(案例窗一关就立刻恢复提醒)
maxSnooze · 2 · 0–5 · 每轮最多"再等会"几次,超出记一次"未中断"
soundEnabled · 开 · — · 到点播放"滴滴滴"提示音(Web Audio 现场合成,不依赖素材;设置页可试听)
soundVolume · 0.6 · 0–1 · 提示音音量
awaitReturnTimeoutMinutes · 10 · 0–60 · 「回来干活」最多等多久,超时自动开下一轮(0 = 一直等)
earlyBreakGuard · 开 · — · 休息没到点就点【休息好了】时,先软提示一次
goodRatio / midRatio · 0.8 / 0.5 · 0–1 · 评价「好」/「中」所需的中断命中率
caseRefreshEnabled · 开 · — · 启动后后台刷新案例库
caseRefreshDelayMinutes · 10 · 1–120 · 启动后多久触发一次刷新
showFloatingPet · 开 · — · 右下角常驻猫猫(可拖动,位置记忆在浏览器 localStorage)
petOpacity · 0.75 · 0.2–1 · 桌宠不透明度;0.75 = 25% 透明,设 1 完全不透明
timeScale · 1 · 0.001–1 · 仅调试:把分钟压成秒(0.01 = 30 分钟变 18 秒)
改完 ≤2 秒生效(宿主每 2 秒重读一次 settings.describe()),当前阶段会按新时长重锚。
工作原理
┌─ host 半身 index.mjs ──────────────────────────────┐ ┌─ client 半身 client.js ────────┐
│ 权威计时(1s tick)→ 状态机 focus/break/awaitReturn │ │ shell.overlay 浮层:猫猫 + │
│ 账本持久化 <DSH_HOME>/data/dsh-no-long-sit/*.json │←→ │ 提醒窗 / 案例窗 / 总结窗 │
│ 案例库:assets/cases.json + 运行期缓存 + 后台刷新 │ │ 3s 轮询宿主状态,按钮 POST 动作 │
│ 路由 /dsh-no-long-sit/{state,choice,cases,refresh- │ │ 设置卡片 settings.section + │
│ cases,art}(自查跨源,白名单素材) │ │ ctx.configForms.get(id) │
└───────────────────────────────────────────────────┘ └────────────────────────────────┘
- 计时放宿主:浏览器计时刷新即丢、多标签页无去重机制,所以状态机与账本都在 host,前端只渲染与发动作。
- 不做 host→client 事件推送:DSH 没有给第三方插件的推送通道(转发事件白名单是官方封闭常量),
所以前端用 3 秒轮询 + 本地 1 秒时钟算倒计时(宿主下发绝对时间戳,不用额外请求)。
- 持久化:
<DSH_HOME>/data/dsh-no-long-sit/state.json(同目录.tmp+ rename 原子替换,失败不阻塞插件)。 - 案例刷新:
ctx.web.search()拿来源 →ctx.llm.stream()(reasoningEffort:"off"、60s 超时)改写成短案例
→ 只保留 url 出现在检索结果里的条目(防模型编造来源)→ 原子写盘;失败保留旧库并在设置页显示原因与上次成功时间。
已知限制
1. DSH 所有页面都关掉时不会再提醒(架构决定:没有页面可弹,宿主也没有面向浏览器的第三方推送通道)。
要系统级提醒得再加浏览器通知或独立桌面窗口,本期未做。
2. 提示音需要页面里有过一次用户交互(浏览器自动播放策略):正常使用 DSH 必然点过页面,插件会在第一次点击/按键时解锁音频。
若 DSH 窗口被最小化或标签页在后台,浏览器会节流定时器,弹窗与提示音可能迟到几秒(切回前台会立刻补一次状态)。
3. 案例后台刷新可能失败:桌面版搜索凭据顺序是「账号 token → DEEPSEEK_API_KEY」,且账号 token 只在
会话活跃时可解析。失败时保留旧库并把原因写进设置页状态区,不会静默变成空列表。
4. 不统计真实喝水次数:程序无法感知你有没有喝水,按你的口径用"专注轮数"给出建议喝水次数。
5. 弹窗只在 DSH GUI 内(shell.overlay),不覆盖宿主设置弹窗之上(宿主设置面板 z-index 更高)。
开发
node --test # 40 项:状态机 / 配置 / 案例校验 / 路由鉴权与素材净化 / mock 宿主契约 / settings 同步 node --check index.mjs; node --check client.js
lib/state.mjs是纯函数状态机(零依赖,可单测);lib/config.mjs是 schemastery schema。- 前端是经典脚本(
window.__ModuleLoader__.load+React.createElement),没有构建步骤,改完刷新页面即生效。 - 依赖:
@deepseek-ai/schemastery是唯一运行时依赖(peer)。本地link:开发时把它放进插件自己的node_modules。
换猫猫素材
素材全部由 assets/pet-manifest.json 描述,换素材不用改代码:支持动图文件(WebP/GIF,当前形态)、
帧序列(frames/<状态>/<状态>-NN.png)与静态图(SVG/PNG)三种写法;清单缺失或某状态未定义时自动回落到内置猫猫 SVG。
当前装机素材:assets/anim/{idle,rise,drink,sleep}.webp —— 由用户的 512×512 透明 PNG 帧序列
(10 / 8 / 10 / 7 帧,fps 6 / 8 / 8 / 4)经「统一基线 → 缩到 256×256 → 合成为动图 WebP(q95)」流水线产出,
36 个文件 11.4 MB 压到 4 个文件 626 KB。
制作规格(画布、帧数、fps、命名、背景抠图要求)与现行装机素材对照见 [docs/ART-SPEC.md](docs/ART-SPEC.md);
最省事的交付方式是给我 4 段纯色背景的 MP4(或直接给 512×512 透明 PNG 帧序列),我来抠图/抽帧/压缩装机。
验收记录(2026-09-30)
- 单测 40/40 通过(状态机 / 配置 / 案例校验 / 路由鉴权与素材净化 / mock 宿主契约 / settings 同步)。
- 隔离实例(桌面 runtime 0.2.0-rc.2,独立
DSH_HOME+ 端口 3099)+ 无头 Chrome CDP 全流程:
宠物渲染 → 专注到点居中弹窗(实测几何中心 = 视口中心)→【再等会】案例窗(1 条久坐 + 1 条缺水,轮换)
→ 关窗后提醒窗立刻恢复并继续倒计时 → 休息结束弹「回来干活」→【开始干活】进入下一轮(breaksTaken=1)。
- 提示音路径用音频探针实测(统计
createOscillator调用):专注到点 +3、休息结束再 +3,两处都真的执行了。 - 设置生效链路:面板值 → profile patch →
settings.describe()→ 插件 ≤2s 生效(cfgSource=user)。
修复记录
日期 · 问题 · 根因 · 修复
09-30 · 到点没声音、弹窗被忽略 · 提示音从未实现;提醒窗是右下角贴边卡片 · Web Audio 现场合成三声"滴滴滴"(设置页可试听/调音量);提醒窗改为居中遮罩弹窗
09-30 · 看完案例后休息倒计时不再出现、跑完也不进下一轮 · 前端用 dismissedAt === phaseStartedAt 做永久屏蔽,点过一次【再等会】后整个阶段都不再弹窗 · 改为只由 snoozeUntil 控制;案例窗关闭时发 resume 立刻恢复提醒;休息结束时清空 snoozeUntil;另加「等待上限」兜底自动开下一轮
09-30 · 保存设置后整张卡片变空白,改过的值看着"改不回去" · form.subscribe() 的回调不带参数(store 只通知"变了"),旧代码把回调参数当新快照 → 快照被写成 undefined,全部字段渲染成空 · 回调里重新 getSnapshot(),并只接受有效快照;保存改为一次原子 mutate;新增「恢复默认值」与调试加速「一键刹车」
09-30 · 点「恢复默认值」后宿主仍按旧值跑 · 所有字段被 unset 后设置服务把 profile patch 里整条条目剪掉,宿主重读时"没有这一行"就直接 retu
…(truncated)
以上内容来自 npm 上的 README,我们只做格式化,不改内容。
版本
npm 上共发布:4,最后更新 2026-10-02