# 参与开发 改动这个插件不需要构建步骤,也不装任何运行时依赖:客户端半是一个 `window.__ModuleLoader__.load(...)` 自注册的脚本,宿主侧 `index.js` 有意留空。Node 20+ 即可跑全部自检。 ## 目录结构 | 文件 | 作用 | | --- | --- | | `client.js` | 全部实现:SVG 绘制、store、气泡、面板、守护提醒、持久化(约 2000 行,唯一运行时文件) | | `index.js` | 宿主半,**故意是空的** —— 桌宠不注册服务、不发网络、不读凭据 | | `cordis.patch.yml` | bundle 补丁,把 `medic-pet` 这一行插进 composition | | `package.json` | bundle 清单:`dsh.bundle.patch` + `dsh.client`(`platform: web`、`immediately`、三个 inject 目标) | | `locale/zh.json` · `locale/en.json` | 卡片标题与描述(插件管理器与设置页读它,不激活插件) | | `icon.svg` | 插件图标(`package.json` 顶层 `icon`,SVG,<256 KiB) | | `smoke.mjs` | 无头冒烟测试,见下 | | `audit.mjs` | 文案审计:ZH/EN 键数对齐、无重复、无缺键、无死键、`{占位符}`一致 | | `art.mjs` | 开发期共用桩:从 `client.js` 抽出**真正会画**的 SVG,预览页与横幅都用它(不存在手抄的第二份美术) | | `preview.mjs` | 用 `art.mjs` 拼成形象对照页(生成 `preview.html` / `preview.svg`,已 gitignore) | | `banner.mjs` | 生成仓库社交预览横幅源页 `banner.html`(1280×640 版式,成品是 `docs/social-preview.png`) | ## 四条自检(提交前都要绿) ```powershell npm run check # node --check client.js / index.js npm test # node smoke.mjs && node audit.mjs npm run preview # 重新生成形象预览页,肉眼核对 npm run banner # 重新生成社交预览横幅源页(改形象或文案后需要重出 docs/social-preview.png) ``` `smoke.mjs` 用桩替换 `React` / DOM / `localStorage` / `setTimeout` / `setInterval` / `Date`,把整段客户端代码 放进一个 `new Function(...)` 沙箱里跑,所以: - 不需要浏览器,也不需要 DSH 在跑; - 时间逻辑可以真断言(拨快时钟 → 断言久坐提醒确实响、20 分钟内确实不重复响); - 断言覆盖装载、按天养成、v1 迁移、逐轮结算、统计/勋章/任务、主题落盘并重绘、隐藏态、导出的两条路径、 拖拽吸附、快捷键、两步重置,最后遍历一遍全部按钮确认没有处理器抛异常。 **改了行为就补断言。** 这个测试抓到过真实 bug(`bestDay` 被写成翻译函数导致统计项静默丢失), 不是形式化覆盖率。 ## 硬性约定 1. **不引入任何外部素材。** 形象只能是手写 SVG 路径 + CSS。不嵌图片、不引字体、不下载素材。 也不使用受保护的红十字标识 —— 臂章和隐藏态小圆点都是白/绿十字。 2. **不新增网络、凭据或文件系统访问。** 只消费 Host 已经接好的 `tokenUsage` / `sessionStats` 投影。 运行时依赖只有 `require('react')`。 3. **界面色只用主题令牌**(`--dsw-alias-*`),字面色只允许出现在画面本体里,否则深色/浅色主题会糊。 4. **数据诚实。** 没有用量的一天就是没有成长;不补签、不倒计时、不发零、不编造金额(没有官方单价依据就不说「花了多少钱」)。 互动只影响情绪、计数与勋章,永远不加成长值。文案要写「本页观察累计」而不是「账号消耗」。 5. **文案双语对齐。** 加一个键就同时加 `zh.json` 和 `en.json`,`{name}` 这类占位符两边必须一致,`audit.mjs` 会拦。 6. **持久化是增量的。** 新字段加进同一组 `dsh.medicPet.v2.*` 键里并做防御性读取(缺什么补什么、非法值夹回范围), 不要另开新键,也不要做「版本升级就清空数据」的迁移 —— 用户的位置、天数与勋章不能因为升级消失。 7. **养成单位是自然日。** 分桶用本地日期(`dayKey`),一天只记一次;跨时区或改系统时间会改变分桶结果, 这是已知取舍,别在插件里做服务器校时。 ## 在真实页面里验证 自检全绿不等于线上跑对。装进 profile(对你的 DSH 助手说一句即可,见 README), **刷新一次页面**(客户端半不会热更新,除非 `pnpm run dev:web` 在跑),然后看: - 浏览器控制台没有 slot 注册崩溃; - 浅色与深色主题下气泡和面板都读得清,且与宿主界面同源; - 拖动、吸边、镜像、隐藏、快捷键、两步重置的手感。 ## 报告问题 请带上:浏览器与 Harness 版本、复现步骤、控制台报错原文、以及「你预期的数字 vs 实际显示的数字」。 涉及统计口径的问题请附 `复制统计` 导出的 JSON —— 那比截图更能定位。