diff --git a/.gitmessage b/.gitmessage index 7fcc422..a2121fc 100644 --- a/.gitmessage +++ b/.gitmessage @@ -5,7 +5,8 @@ # # 类型与 emoji:feat ✨ / fix 🐛 / docs 📝 / refactor ♻️ / perf ⚡️ / # test ✅ / build 📦️ / ci 💚 / chore 🔧 / revert ⏪️ -# 作用域(可选):client 浏览器半 / host 宿主半 +# 作用域(可选):client 浏览器半 / host 宿主半 / build 构建与测试 # 例:✨ feat(client): 后台走系统通知,前台走应用内轻弹窗 # 例:🐛 fix(client): 前台判定改为现读可见性 # 例:📝 docs: 安装说明补充 Git 标签与本地路径两种方式 +# 注意:改 src/client/** 后必须 `pnpm run build`,产物 client.js 要一起提交 diff --git a/CHANGELOG.md b/CHANGELOG.md index 820c6b0..064e772 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ 版本说明按倒序排列。提交信息遵循 [CONTRIBUTING.md](CONTRIBUTING.md) 的单行规范,因此每次改动"为什么这样改、影响面是什么"记在这里,而不是提交信息里。 +## v1.0.7 + +- 重构:**客户端源码拆成模块,`client.js` 从「手写文件」变成「构建产物」**。原来的 `client.js` 是 1452 行单文件,通知引擎、轻弹窗、插件页、观察器、字典、样式、图标全塞在一个 `apply` 里,改任何一处都要在同一个文件里上下翻。现在: + - `src/client/core/` 是行为:`observe`(一轮派生)、`delivery`(通道决策、节流、400ms 确认窗口)、`system-channel`(系统通知与平台回执)、`replay`(回前台补发)、`toasts`(弹窗栈与计时)、`actions`(两个测试按钮、权限请求、开关)、以及 `copy` / `session` / `storage` / `store` / `log`; + - `src/client/ui/` 是界面:`ToastLayer`、`KindRow`、`ConfigSection`、`NotifyObserver`、`icons`、`styles`、`hooks`; + - `src/client/i18n/` 是字典与翻译器,`src/client/platform.js` 是浏览器与平台事实,`src/client/plugin.js` 是组合根(建 store、装配模块、注册三个 slot),`src/client/index.js` 是构建入口。 +- **`client.js` 仍然是包根那一个文件,安装路径与发布清单都没变**:`exports`、`files`、`main`、`icon`、`dsh` 与 `cordis.patch.yml`、`locale/` 全部原样,使用者依旧零依赖、零构建。 +- 新增 `scripts/build-client.mjs`(esbuild):把 `src/client/**` 打包成一个自包含单文件,并校验它仍然满足注册契约(只有一个 factory、`require('react')` 落在 factory 内、没有顶层 `import`/`export`、除了 `react` 不再向模块表要别的模块)。这一步无法省略:客户端模块系统只交给插件一个自包含 bundle,factory 里的 `require` 不能 require 同一个包里的相对文件。 +- 新增 `tests/`(`pnpm test`,用 `node --test`,无第三方依赖):26 个纯函数单测(文案、会话派生、存储、字典与翻译器)、4 个产物测试(注册 id、factory 返回值、`apply` 在没有 DOM 的 Node 里也能跑通、**产物与 `src/**` 是否一致**)、6 个渲染测试(用一份最小 React 替身渲染插件页与轻弹窗,验证重构后的 props 装配:开关的选中态、诊断行、按钮回调、观察器的健康上报)。以后改完源码忘了重新构建,测试会直接失败。 +- 维护者现在需要跑一次 `pnpm install`(唯一 devDependency 是 esbuild);**使用者不需要**。README 的安装说明按这个区分改写了,并新增「仓库结构」一节。 +- 行为未改:三类提醒的判定、通道选择、节流与确认窗口、补发与去重、开关的存储键、CSS 全文与 `dsn-*` 类名、全部字典键与文案都逐字保持(构建产物与 v1.0.6 的字符串逐条比对通过,样式表 4615 字节完全一致)。 +- 顺带记录一个**本次没有修的既有缺陷**:`body.planReview`(「计划正在等待你确认」)在 v1.0.6 里已是死代码——`copyFor` 先判断 `kind === 'question'`,而计划确认正是以 `kind: 'question'` 入队的,所以计划提醒实际显示的是提问那段文案。修它要调整判断顺序、属于行为变更,因此这次只把现状写进 `src/client/core/copy.js` 的注释,并在 `tests/unit/copy.test.js` 里钉住当前输出。 + ## v1.0.6 - 修复:**窗口不在前台时,提醒有可能一次都看不到**。排查证据:一次真实投递(会话 17:52:29 结束)在插件页留下 `09:52:30 · 系统通知`——`09:52:30` 是 UTC,本机时间是 17:52:30;而 Windows 的通知平台记录 `LastNotificationAddedTime` 停在 70 分钟前。也就是说 `new Notification()` 没抛错,但**构造函数成功不等于通知真的弹出来了**,加上系统通知的气泡只存在几秒,人不在电脑前时它等于没送达。这一版不再把"发出去"当成"收到了": diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fb36966..78ea099 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,10 +41,11 @@ ## 作用域约定 -这个插件只有一个包,作用域用来区分改动落在哪一半,不是必需的: +这个插件只有一个包,作用域用来区分改动落在哪一块,不是必需的: -- `client`:浏览器半(`client.js`)—— 通知引擎、轻弹窗、插件页配置、观察器; +- `client`:浏览器半(`src/client/**`,以及由它构建出的 `client.js`)—— 通知引擎、轻弹窗、插件页配置、观察器; - `host`:宿主半(`index.js`、`cordis.patch.yml`)—— 目前是空壳,只有它变化时才需要重启 DSH; +- `build`:构建与测试(`scripts/build-client.mjs`、`package.json`、`tests/**`); - 其它临时作用域(如 `deps`、`naming`)按需起,不必登记。 ## 示例 diff --git a/README.md b/README.md index 8691680..f39faee 100644 --- a/README.md +++ b/README.md @@ -59,10 +59,10 @@ DeepSeek Harness(DSH)插件:会话**需要你注意**时提醒你——窗 在 DSH 的 **插件 → 添加插件** 的上方输入框里填: ```text -https://gitea.iwake.top/dsh-plugin/session-notify.git#v1.0.6 +https://gitea.iwake.top/dsh-plugin/session-notify.git#v1.0.7 ``` -`#` 后面跟标签或提交,用来锁定版本;不写则取默认分支。跟随 1.x 最新版可以写 `#semver:^1.0.6`。仓库是公开的,不需要凭据,也不用改「安装源」。 +`#` 后面跟标签或提交,用来锁定版本;不写则取默认分支。跟随 1.x 最新版可以写 `#semver:^1.0.7`。仓库是公开的,不需要凭据,也不用改「安装源」。 ### 本地路径安装 @@ -74,8 +74,9 @@ D:\DeepSeek Harness Plugins\dsh-session-notify ### 安装之后 -- 装好**刷新页面**即生效;本插件没有第三方依赖,也不需要 pnpm 下载或构建步骤。 +- 装好**刷新页面**即生效;仓库里已经带好构建产物,所以**既不需要 pnpm 下载,也不需要构建**,本插件运行时没有第三方依赖。 - 卸载:在插件页里移除 `@dsh-plugin/session-notify`。 +- 上面这两条说的都是**使用者**。要改插件本身,只需要一个 `pnpm install`(唯一依赖是打包用的 esbuild),见下面的[仓库结构](#仓库结构)。 ## 提醒规则 @@ -100,6 +101,32 @@ D:\DeepSeek Harness Plugins\dsh-session-notify 需要 **DeepSeek Harness 0.2.0-rc.2 或更高**。 +## 仓库结构 + +给要改这个插件本身的人;只是使用它的话,这一节不需要看。 + +| 路径 | 是什么 | +| --- | --- | +| `src/client/` | 浏览器半的全部源码:`core/`(观察、投递、系统通知通道、补发、弹窗栈、开关与动作)、`ui/`(弹窗、开关行、插件页、观察器、图标、样式、hooks)、`i18n/`(中英文字典与翻译器)、`platform.js`(窗口可见性、通知权限、图标、平台提示)、`plugin.js`(组合根:建 store、装配模块、注册 slot) | +| `client.js` | **构建产物**:`src/client/**` 打包成一个自包含单文件,由 DSH 的客户端模块系统加载。它提交进仓库——这正是安装免构建的原因 | +| `index.js` / `main.js` | 宿主半(目前是空壳),以及给按 `main` 解析的加载器用的同义入口 | +| `cordis.patch.yml`、`icon.svg`、`locale/` | 组合包 patch、图标、插件展示用的标题与简介 | +| `scripts/build-client.mjs` | 构建脚本:打包,并校验产物仍然满足注册契约 | +| `tests/` | `node --test`:纯函数单测、组件渲染测试(含轻弹窗与插件页)、产物冒烟、以及产物与源码是否一致 | + +**为什么必须是单个文件**:DSH 的客户端模块系统只把**一个自包含 bundle** 交给插件(`window.__ModuleLoader__.load({ id, factory })`),factory 里的 `require` 只能解析 `react` 这类平台模块,**不能** require 同一个包里的相对文件。所以源码拆成模块之后,必须再打包回单文件——官方客户端插件(例如 `dsh-experimental-client-ui-voice-input`)也是这个形态:`src/**` 源码 + 构建出的客户端 bundle。 + +维护者命令: + +```bash +pnpm install # 只装 esbuild 一个 devDependency +pnpm run build # 从 src/client 重新生成 client.js —— 改完源码必须跑 +pnpm test # node --test +``` + +- **不要直接改 `client.js`**:它每次构建都会被覆盖;改完 `src/` 忘记重新构建,`tests/bundle.test.js` 会直接报「产物已过期」。 +- 改完产物后,正在运行的 DSH 里要在插件页把这一行**停用再启用**(或重启 DSH):宿主对客户端 bundle 有缓存,只刷新页面不一定重新读取它。 + ## License [MIT](LICENSE)