@kkabuka/dsh-clips
v1.1.1
Published
批注引用 + 摘录暂存 for DeepSeek Harness(fork 自 AHGGG/dsh-side-chat,改名独立维护)
Readme
dsh-clips
DeepSeek Harness(dsh web)的批注引用 + 摘录暂存插件。 Fork 自 AHGGG/dsh-side-chat(MIT,© 2026 dsh-side-chat contributors),改名为 @kkabuka/dsh-clips 独立维护(0.1.0 全新开始——LICENSE 与上游版权声明原样保留)。维护于 personal-docs 仓库
dsh/plugins/dsh-clips。
功能
围绕 dsh 原生 composer 的两个选中文字工作流:
| 功能 | 说明 |
| :--- | :--- |
| 批注 | 会话里选中文字 → 「批注」→ 可加备注 → 一个「N 条引用」引用胶囊进入主输入框(聚合模型:本会话所有批注都在单一胶囊内)。发送时模型收到结构化 <selected_context>。 |
| 收录 | 选中文字 → 「收录」→ 暂存进摘录抽屉(管理:填回输入框 / 复制 / 删除 / 清空 / 导出)。 |
纯 client(inject 只依赖 web client 侧;host 半边为无操作入口 apply(){}——rc.1 boot 要求每个 bundle 声明 dsh.bundle.patch,见 dsh PITFALLS §23)。
交互细节(2026-09-12)
| 行为 | 说明 |
| :--- | :--- |
| 胶囊命中区 | 只有胶囊本体响应悬停——它所在的那一行(composer banner)是 inert 的(pointer-events 门),滑过空白区不再弹预览 |
| 预览框避让(quote-flip) | 已发送消息的预览框按可见聊天区量测后裁决:上方够就上方、贴顶就下方、上下都不够就夹紧(面板内部滚动)、锚点出视口则隐藏;用 position: fixed 渲染,滚动容器不再裁它。composer 态胶囊保持恒上方 |
| 捕获守卫 | 若某祖先开始充当 position: fixed 的包含块(真捕获,如被注入 transform),打开时用一次性探针实测识别 → 回退到原先的绝对定位行为(日志出 quote-guard)。判定只实测,不读样式清单——见 docs/pitfalls-client.md §31 |
| 皮肤配件面 | composer banner 行退出皮肤适配层的 dock「配件面」(覆盖 --dsh-composer-accessory-*),胶囊外不再套一层半透明圆角带;其它 dock 条目保留上游配件面 |
适配与依赖
| 组件 | 版本 |
| :--- | :--- |
| dsh 内核 | 0.1.5-rc.2(亦实测 0.1.0-rc.7 / 0.1.1-rc.2 / 0.1.2-rc.1——见 dshCompatibility.testedVersions)。0.1.5 线已不再发布 @deepseek-ai/dsh-client-runtime(npm 止于 0.1.1-rc.2),故本插件不再依赖它:少量宿主类型改取自 dsh-api-session-controller / dsh-client-ui-renderer,或按"契约外最小形状"就地声明 |
| Node.js | ^22.19.0 或 >= 24.0.0 |
| 包管理 | pnpm 11.x(dsh plugin 是 pnpm 转发器) |
| 观测 | @kkabuka/dsh-log-shim + dsh-log-hub(scope clips,27 事件,词典见 observability/) |
快速开始
- 安装(在你运行 dsh web 的 profile):
dsh plugin --profile web add @kkabuka/dsh-clips@latest - 重启
dsh web——client bundle 在启动时合成;新装包没有 HMR。 - 验证注册:打开 web UI,在会话里选中文字——选区旁应出现「批注 / 收录」工具条;浏览器 console 不得出现
loaded without registering横幅(见 dsh PITFALLS §23)。
常用命令
| 命令 | 作用 |
| :--- | :--- |
| pnpm install | 装依赖(clone 后 / 换机时) |
| npm run build | 构建 lib/(clean && tsc && tsdown)。lib/ 不入 git——打包前先构建 |
| npm run typecheck | tsc --noEmit |
| npm run lint | oxlint |
| npm run test | vitest run(单测——含 A 聚合批注模型) |
| npm run pack:verify | 构建 + 校验打包产物(verify-package.mjs) |
| npm run check | peers + typecheck + lint + test + pack:verify(全量门禁)。⚠️ 其中 pnpm peers check 已知会报 unmet peer:devDeps 现钉 0.1.5-rc.2 线、而在役内核线为 0.2.0-rc.2(2026-10-12 实测;本条即该债的登记处)——其余四道闸全绿 |
验收 / DoD(无头真页面)
对着隔离实例跑(不要对着你正在用的那个):
node <dsh>/lib/bin.js --profile web-scroll --host 127.0.0.1 --port 3081 --no-open # 首次:从 stdout 取一次性 token
node dsh/plugins/dsh-clips/scripts/verify-quote-flip.mjs --port 3081 # 12 链 C1–C13(期望 quote-flip-acceptance-ok)
node dsh/plugins/dsh-clips/scripts/verify-quote-hover.mjs --port 3081 # 9 链(命中区 / 悬停)
node dsh/plugins/dsh-clips/scripts/verify-echo-visible.mjs --log <iso 日志> # 2 项:刚发那条立刻渲染出正文(不是空行)
node dsh/plugins/dsh-clips/scripts/verify-switch-back.mjs --log <iso 日志> # 9 项:批注 → 切走 → 切回 ⇒ 胶囊在 + 不重复投影文本
node dsh/scripts/run-runtime-probe.mjs dsh/docs/design-20260912-quote-flip # 6 条基线契约(期望 6/6 passed)避让套件断言:裁决(above/below/clamped/hidden)、首帧稳定(rAF 采样)、滚动跟随与出视口隐藏、捕获守卫双向(注入 transform → absolute,移除 → fixed)、量测异常降级、composer 面回归、off 态日志静默、零副作用不变量。链证据与设计依据见 docs/design-20260912-quote-flip.md(§14/§16)。
回显 / 切回这一对守 2026-09-25 两条真机回归:verify-echo-visible = 发出的消息必须立刻渲染出自己的正文(不是空行)—— 回显分发必须给 conversation.chat.node 传真 hookContext(根因件 = patch-files/patches.json 的 core-echo-slot-dispatch v3,细则 = patch-files/CHANGELOG.md「修(2026-09-25)」节);verify-switch-back = 切走再切回后聚合胶囊必须仍在,且宿主的剪贴板投影必须从草稿里剥掉(否则胶囊与原文并存两份)。两者取 --log/--url,默认拒绝 3080(会建会话、发消息),并支持 --archive 顺手归档自建会话 —— 收尾纪律 = docs/headless-cdp-standards.md §一。
如何更新
dsh plugin --profile web update @kkabuka/dsh-clips --latest
dsh --profile web --no-open # 重启(或重启你的托盘/web 实例)构建产物 = 安装包内的 lib/;刷新包即替换。已持久化的用户数据(见"配置")不受更新影响。
卸载
dsh plugin --profile web remove @kkabuka/dsh-clips如需彻底清理残留:
localStorage键dsh-clips:snippets:v1与dsh-clips:composer-annotations:*(浏览器 per-origin LevelDB;清站点数据/console 清除——见"收录数据"节)。- composer 草稿里残留的批注胶囊:卸载前先 Backspace 删掉——否则胶囊无 owner,发送会被阻塞(dsh 核心行为:绝不静默丢弃无主引用)。
配置
0.1.0 无配置 UI。全部状态 浏览器本地(per-origin):
| 键 | 存什么 |
| :--- | :--- |
| localStorage dsh-clips:snippets:v1 | 摘录池(重启/刷新/关浏览器都存活;与 dsh 会话、服务进程完全独立) |
| sessionStorage dsh-clips:composer-annotations:* | 未发送批注的恢复镜像(尽力而为;权威数据在胶囊本身) |
边界:清站点数据、无痕窗口、换浏览器、或改 dsh web 端口 → 摘录池丢失(或成孤儿)。
FAQ / 故障排查
| 问题 | 处理 |
| :--- | :--- |
| 选中文字工具条(批注/收录)不出现 | bundle 未注册——看是否有 loaded without registering 横幅(dsh PITFALLS §23);重装 + 重启 |
| "Could not add/remove the annotation" | composer 忙(相位非 plain/claimed)或胶囊失去 owner;重试;胶囊变红/删除线则删除重批 |
| 刷新后批注丢了 | 重新打开会话——恢复镜像(sessionStorage)会还原聚合胶囊;若存储被清且未发送,则不可恢复(未发送的 composer 草稿是临时的) |
| 摘录数据没了 | 清了站点数据 / 换浏览器 / 改端口(见"配置"边界);下次清空前先用下方救援脚本 |
| 胶囊后多个空格 | 正常——dsh 核心给每个引用胶囊补一个分隔空格(发送时 trim,模型看不到) |
摘录数据救援脚本(插件完全坏掉也能用——纯原生浏览器 API;键为 dsh-clips:snippets:v1):
copy(localStorage.getItem('dsh-clips:snippets:v1')) // 全量导出(复制到剪贴板)
JSON.parse(localStorage.getItem('dsh-clips:snippets:v1')).items.map(s => s.text) // 查看摘录池
copy(JSON.parse(localStorage.getItem('dsh-clips:snippets:v1')).items[0].text) // 复制最新一条
localStorage.removeItem('dsh-clips:snippets:v1') // 全部清空(谨慎)License
MIT(上游版权保留——见 LICENSE)。
参考
- 设计文档(selection-contract / dsh-compatibility / privacy-and-retention / threat-model / architecture 沿革)只在源码仓维护,不随包发布(仓库为私有,故此处不留链接);本包只发布 README + CHANGELOG。
- dsh 插件机制:
dsh/docs/dsh-internals.md§3.3(官方引用胶囊通道、detect/clipboard 双投影) - dsh 插件规范:
dsh/plugins/dsh-plugin-dev-standards.md(含 §1.6 隔离环境开发)、dsh/plugins/release-standards.md - 打点词典与回归:
observability/
