# dsh-hmm-wait 开发文档(ARCHITECTURE) 本文档面向后续开发者:说明包结构、运行架构、装配方式、构建发布流程,以及常见的扩展点。 建议先读 `README.md` 的"工作原理(30 秒版)"。 --- ## 1. 包结构 ``` dsh-hmm-wait/ ├── package.json # dsh bundle 清单(dsh.bundle.patch / dsh.client)+ peer 声明 ├── cordis.patch.yml # bundle patch:向 profile roster 插入插件行(id: hmm-wait) ├── tsdown.config.ts # 双产物构建:host ESM lib/index.js + client CJS lib/client.js ├── tsconfig.json # 类型检查配置(strict) ├── tsconfig.build.json # 声明文件产出(lib/types/) ├── scripts/build.sh # 构建入口(bash,兼容 dsh 插件工具链) ├── src/ │ ├── index.ts # ★ host 插件入口:settings 注册 + llm/stream tap + SSE hub + 路由 │ ├── schema.ts # ★ 配置 schema(schemastery)+ 默认值 + 类型(host/client 共享) │ ├── protocol.ts # ★ SSE 协议与事件类型(host/client 共享) │ ├── detect.ts # ★ 流式触发检测器(纯函数,无依赖,可单测) │ ├── routes.ts # SSE hub + 订阅/测试路由(node:http) │ └── client/ │ ├── index.ts # ★ client 入口:slots 注册(overlay + 设置卡片)+ 配置镜像 │ ├── api.ts # SSE 订阅(自动重连)+ 测试弹幕调用 │ ├── state.ts # 模块级 store(弹幕队列 + 配置快照,useSyncExternalStore) │ ├── danmaku.tsx # 弹幕层(轨道分配 + Web Animations 飞行 + 抖动) │ ├── panel.tsx # 设置卡片(settings.plugin.item 槽位) │ └── styles.ts # 注入页面的 CSS(keyframes + 卡片样式) └── docs/ARCHITECTURE.md # 本文档 ``` ## 2. 运行架构 ### 2.1 Host 侧(node 进程) ``` dsh host process ├── ctx.settings.register('dsh-hmm-wait', schema) → SettingsScope(live 生效) │ └── scope.watch(...) → 更新 config;enabled=false 时卸掉 tap ├── ctx.on('llm/stream', tap) → 每路模型流一个 Detector │ └── 扫描 reasoning-delta → 命中 → hub.broadcast(DanmakuEvent) ├── webServer.register(GET /api/dsh-hmm-wait/events) → SSE 订阅(挂起连接 + 30s 心跳) └── webServer.register(POST /api/dsh-hmm-wait/test) → 测试弹幕 ``` - **llm/stream 语义**:dsh-llm 每次流式调用都会走 `ctx.waterfall(llm, 'llm/stream', options, next)`。 监听器签名 `(options, next) => stream`。本插件是**观察者**:返回 `next()` 的包装流, 逐 chunk `yield` 原样透传;检测逻辑全部包在 try/catch 中——任何异常都不会破坏模型流。 - **chunk 形状**(dsh-llm 公开协议):`{ type: 'reasoning-delta', index, text }`。 只做鸭子类型判断,不 import dsh-llm 运行时符号。 - **会话隔离**:每个 `llm/stream` 调用新建一个 detector(句子状态互不污染)。 - **事件 id**:进程级自增序号,client 端用于去重。 ### 2.2 Client 侧(浏览器) | 表面 | 槽位 | 说明 | | --- | --- | --- | | 弹幕层 | `shell.overlay`(list, root) | 全屏 fixed、pointer-events: none、点击穿透 | | Combo HUD | `shell.overlay`(list, root) | 街机风连击计数(位置可配) | | 设置卡片 | `settings.plugin.item`(keyed, root, key=`dsh-hmm-wait`) | 官方"设置 → 插件 → 可配置"页 | - **配置镜像**:`ctx.settingsScope.bind({ namespace: 'dsh-hmm-wait' })` → `subscribe` → `publishConfigSnapshot()` 写进模块 store;两个组件通过 `useSyncExternalStore` 消费。 - **SSE 订阅**:`fetch + ReadableStream` 手动解析 SSE 帧(不用 EventSource,兼容性最好), 断线 1s 重连 + 半开看门狗(45s 无数据自动重连)+ 回前台即查。 - **动画**:Web Animations API(`element.animate`),飞行距离按方向与视口精确计算 `duration = 距离 / speed × 1000`;抖动用注入的 CSS keyframes(幅度走 `--dsh-hmm-shake` 变量)。 ### 2.3 Combo 连击 - **状态机在 host**(`src/combo.ts`,纯逻辑可单测):两次命中间隔 ≤ `comboWindowMs` 连击 +1, 超窗重置;`max` 保留最高纪录。 - **进程级存续**:combo 追踪器与 hub 一样挂在 `ctx.root`(`APP_COMBO_KEY`),热重载/重装不丢。 - **协议**:每个 `DanmakuEvent` 携带 `combo`(当前连击)与 `comboMax`(最高)。 - **浏览器 HUD**(`src/client/combo.tsx`):数字弹跳(每击 scale 动画)、分级变色 (1-4 白 / 5-9 黄 / 10-19 橙 / 20+ 红+光晕)、里程碑全屏播报(×10/×20/×30/×50/×100)、 连击中断动画(窗口内无新命中 0.5s 检测一次);位置四角可配。 ### 2.3 装配(如何被 dsh 加载) `cordis.patch.yml` 向 profile 的 cordis.yml 插入一行: ```yaml - insert: - id: hmm-wait name: 'dsh-hmm-wait' ``` - **node half**:row 按包名解析 → `main: lib/index.js`(ESM)在 host 进程运行。 - **browser half**:`package.json` 的 `dsh.client` 声明让 loader 把 `exports['./client']` (`lib/client.js`,`window.__ModuleLoader__.load` 包裹)挂到 `/plugins/dsh-hmm-wait/client.js`, 页面加载时注入浏览器插件树。 - **卸载**:`dsh plugin --profile web remove hmm-wait`(或注入器 `dev_uninject_plugin`), 所有 fiber 清理(路由、tap、SSE 连接、DOM style、slot 条目)自动随 ctx.effect 拆除。 ## 3. 构建与发布 ```bash npm install # 安装 devDependencies(SDK 类型仅供编译期使用) npm run build # = tsc -p tsconfig.build.json(类型+d.ts) + tsdown(双 bundle) npm pack # 产出 dsh-hmm-wait-0.1.0.tgz(files: lib/ + src/ + cordis.patch.yml) ``` - tsdown 的 node 产物把 `@deepseek-ai/*` 全部 external(运行时从宿主解析,绝不内联重复实例); client 产物只把平台模块(react、cordis、slots 等)external,其余内联。 - **发布 GitHub Release**:`gh release create v0.1.0 dsh-hmm-wait-0.1.0.tgz`(附件即安装包)。 ## 4. 扩展指南 ### 4.1 加/改触发词算法(`src/detect.ts`) - 每个 trigger 编译为 `(?:^|[^\p{L}\p{N}])(触发词)`,捕获组 1 为触发词本体; - `sentence-start` 模式:命中点之前(自最近句子边界 `\n。!?!?;;` 起)只能有空白/引号; - 冷却(每词)+ 全局限流(每秒滑动窗口)在 `RateLimiter` 内。 - 想加"同义词库/正则触发词":把 `triggers` 语义改为正则片段即可(`escapeRegExp` 处放开)。 ### 4.2 改弹幕动效(`src/client/danmaku.tsx` + `styles.ts`) - 方向枚举在 `src/schema.ts`(`DanmakuDirection`),新方向只需在 `flight()` 加一个分支 + schema union 加一项; - 轨道布局:`TRACKS_PER_ZONE` 与 `placement` useMemo;可改为"每轨道活跃计数"动态分配; - 视觉参数全部进 `HmmWaitConfig` 并在面板加控件(panel.tsx 加一个 `