DSH Web 浏览器端批注插件:选中助手回复文字打标,回车随消息发给模型,回复按编号逐条回应。
- 语言
- HTML
- License
- MIT
- 分支
- main
安装
$ dsh plugin --profile web add github:omdsh-dev/dsh-annotation在终端中运行以上命令,通过 dsh CLI 安装此插件。可在右上角切换 Profile。 第一次用 dsh?看这篇新手教程
对话式安装
帮我安装 DeepSeek Harness 插件 omdsh-dev/dsh-annotation:先查看仓库 https://github.com/omdsh-dev/dsh-annotation.git 确认安全性,然后执行安装命令并验证插件加载成功。
把这段指令粘贴给 DSH Web GUI 里的助手,由它代你完成安装与验证。
一句话定位
为 DSH Web 增加对助手回复内容的批注能力——选中助手消息里的任意一段文字、写备注(或留空仅做标记)、按回车,批注清单会随你输入框里的问题一起发给模型。模型按批注编号逐条回应,回复里每个「Annotation N:」都可悬浮查看对应原文和批注内容。
核心能力
- 选中助手回复里的任意文字即可弹出批注工具条,写备注后原文出现蓝色编号脚标 + 高亮
- 批注备注可留空(仅做标记),跨消息、跨对话回合连续累积,编号从 1 开始
- 输入框旁出现「批注 ×N」小标签,悬浮可见全部批注内容、可逐条删除
- 在输入框按回车时,批注清单会在提交前一刻拼进草稿,与你的问题一起发送
- 用户消息气泡内不会显示批注块文本,只保留问题 + 「批注 ×N」标签(hover 看内容)
- 模型回复里的「Annotation 1:」…「Annotation N:」渲染为可悬浮芯片,hover 显示对应原文与批注
技术实现
- 语言: 浏览器端 hand-written CJS JavaScript(client.js),外加一个空实现的 TypeScript Node 入口(src/index.ts)
- 关键依赖: cordis(peerDependency ^4.0.0-rc.7 || ^4.0.1,仅声明不打包)、@deepseek-ai/dsh-client-runtime、@deepseek-ai/dsh-client-ui-conversation(注入目标,非运行时导入)
- 架构模式: 官方 bundle 插件形态——Node 侧为空(apply() 无操作),全部能力在 client.js 经 dsh.client 声明注入到浏览器;cordis.patch.yml 只插入一行自身 id,不动 DSH 核心;通过
exports.inject = ['sessions','conversation']接入会话与对话服务 - 入口文件: 浏览器入口 client.js(1801 行),Node 入口 lib/index.js(src/index.ts 编译产物,6 行空实现)
适用场景
适合需要让模型针对助手回复中特定段落做精细化追问或纠正的用户:比如让模型逐段解释长答案里的某几行、要求模型修改某一段措辞、或者对多段内容分别给出不同指令。普通的一问一答用不到,**想"指着某段话跟模型聊"**的场景才是它的主战场。
前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| DSH | 未声明 | package.json 未声明 DSH 版本,仅声明 cordis ^4.0.0-rc.7 || ^4.0.1 作为 peer;安装时按当前 DSH 版本对应的 cordis 即可 |
| Node | >=20 | 仅影响 Node 侧空实现的构建(tsc 编译),浏览器端运行不依赖 Node |
| 平台 | 跨平台 | 全部能力在浏览器内运行(client.js),平台无关 |
| 原生模块 | 无 | 不引入任何原生依赖,纯 DOM + Cordis 服务订阅 |
安装方式
dsh plugin --profile web add github:omdsh-dev/dsh-annotation
配置项
本插件无需额外配置。所有行为(编号样式、回复格式、分隔标记「提问:」、历史消息自修复等)均为内置默认,源码中未暴露任何 Schema/options 字段。
常见问题
Q: 安装后要不要重启 DSH Web?
A: 建议重启。安装命令本身只注册 bundle,但 client.js 是按请求 no-cache 提供给浏览器的;macOS 上官方推荐 launchctl kickstart -k "gui/$(id -u)/com.dsh.web",其他平台按你的 DSH Web 服务管理方式重启即可。
Q: 怎么确认插件安装成功?
A: 跑两条自检命令:dsh --profile web --dump-config | rg "id: dsh-annotation" 必须恰好输出 1 行(避免和 profile/home 的 cordis.patch.yml 重复 insert);curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3080/plugins/@omdsh-dev/dsh-annotation/client.js 应返回 200。
Q: 同一段文字能不能打多个批注?或者批注后想改备注怎么办?
A: 同一段选区文本会被判定为同一批注,重复操作会进编辑态更新已有备注;想加新批注就在不同位置再选一次即可。已保存的批注可以点输入框旁的「批注 ×N」标签里的删除按钮逐条移除。
Q: 切到别的对话,批注会带到新对话吗?
A: 不会。订阅 sessions.list 检测到会话切换时,插件会主动清空已收集的批注集合,避免跨会话污染。
Q: 会和 DSH 自带的引用功能冲突吗?
A: 不会。插件没有依赖任何发送完成事件链,也不改 composer textarea 的 DOM;只通过 capture 阶段拦截 Enter、在提交前一刻用 setDraft 拼入批注块(不覆盖用户草稿),然后让 composer 自己的提交逻辑继续走。
Q: 装了 dsh-focus-chat 之类的聚焦视图插件能一起用吗?
A: 可以。插件同时识别 DSH 主视图(带 data-time-hover-root 的助手行)和 focus-chat 聚焦视图(带 data-focus-flow 且 class 含 assistant 的容器),选区批注、回复芯片、滚动重定位在两种视图下都生效。
上手难度
入门 — 装一条命令就能用,没有配置项;理解"选中→工具条批注→回车发送"这一条交互路径即可,不需要了解 Cordis 或 DSH 内部结构。
已知问题与限制
- 初始化时机会话未加载:
watchInputDraft在插件刚启用、会话列表还没加载出来时无法订阅输入草稿状态(client.js:1388)。插件的兜底策略是 MutationObserver + 1s 轮询(client.js:1747-1750),因此批注气泡隐藏逻辑仍生效,但极端首启动场景下首次发送完成事件可能错过一次,会在下一轮轮询中自修复。 - 气泡隐藏依赖纯文本渲染:DSH 用户气泡是 MessageText 单文本节点(非 markdown),批注块隐藏算法按最后一个「\n提问:」切分(client.js:1436-1445);如果未来 DSH 把用户气泡改成 markdown 渲染或拆成多文本节点,隐藏手术需要相应调整。
- 回复芯片依赖 TreeWalker 快照:流式结束后要把「Annotation N:」替换为悬浮芯片,源码强制先快照 TreeWalker 收集到的文本节点再逐个替换(client.js:1630-1650)——遍历中途 replaceChild 会让 walker 指针失效只处理到第一个节点,这是显式维护的脆弱点。
- 批注数据只存在内存:ui.quotes 仅保存在当前页面内存中(client.js:655-667),刷新页面后未发送的批注会丢失;已发送的批注通过用户消息上的
tag.__annotationItems重建(client.js:1500-1507)。 - DSH 版本未声明:package.json 未指定 DSH 版本号或兼容范围(package.json:38-58),仅以 cordis peerDependency 间接约束;升级 DSH 后如果 cordis 主版本不兼容,需手动验证。
English · 简体中文
Selection-annotation plugin for DSH Web: select text → annotate → press Enter to send it along with your message; the model replies to each annotation by number.
Select any text in an assistant reply to annotate it (the annotation body may be left empty = just mark the passage). Annotations accumulate across messages and turns. An Annotations ×N chip appears next to the input box — hover to view all annotations, remove them one by one. Press Enter and the annotation block goes to the model together with whatever question is in the input box. The annotation block never shows up as text in your own message bubble — only the question plus the chip (content visible on hover; hidden before paint, zero flicker). The model replies with Annotation 1: … … Annotation N: …, one per annotation, and every Annotation label in the reply is a hoverable chip showing the annotated passage and your note.
Form: official bundle plugin (dsh.bundle + a dsh.client declaration in package.json, injected into the browser via client-modules; the Node half is an empty implementation). Zero core changes — no DSH files are touched; cordis.patch.yml only inserts its own id once, and the profile patch stays [].
Features
| Feature | Description |
|---|---|
| Select-to-annotate | Select assistant text → toolbar "Annotate" → write your note (may be empty); dismiss by clicking elsewhere or pressing Esc |
| Numbered marker + highlight | A blue numbered marker + highlight anchored to the passage, viewport-anchored with collision avoidance, never lost when scrolled out of view |
| Cross-turn collection | Any number of annotations accumulate across messages/turns, numbered from 1 |
| "Annotations ×N" chip | Small chip beside the input box; hover shows every annotation, deletable individually |
| Enter sends with your message | Annotation block + the question in the input box are sent to the model together (the model receives the full content) |
| Hidden in your bubble | The annotation block is removed from your bubble's DOM the moment you send (before the browser paints), leaving only the question + the chip (hover to view); historical messages self-heal after a refresh |
| Numbered reply correspondence | A format instruction is injected into the message so the model replies Annotation 1: … … Annotation N: … one by one |
| Reply annotation chips | Annotation N: in the reply renders as hoverable chips showing the passage + your note |
Interaction flow
Select assistant text ──▶ Toolbar "Annotate" ──▶ Write note / save empty ──▶ Blue numbered marker + highlight
▲ │
└────────────── any number, accumulate across turns ◀────┘
│
▼
"Annotations ×N" chip beside the input (hover to view / delete)
│
Press Enter
▼
Model receives: annotation block (number + passage + note) + your question
Your bubble: question only + "Annotations ×N" chip (zero flicker)
Model reply: Annotation 1: … Annotation 2: … (hoverable chips)
Install (official bundle path · the only one)
# Public GitHub install (works without an npm account)
dsh plugin --profile web add git+https://github.com/omdsh-dev/dsh-annotation.git
# local path install (development / debugging)
cd /path/to/dsh-annotation
dsh plugin --profile web add .
# restart the web service — see "Restarting the web service" below
| Do | Don't |
|---|---|
Only dsh plugin add / only write bundles | Never insert the same id again in the profile/home cordis.patch.yml |
Self-check:
dsh --profile web --dump-config | rg "id: dsh-annotation" # must be exactly 1 line
curl -s -o /dev/null -w '%{http_code}\n' "http://127.0.0.1:3080/plugins/@omdsh-dev/dsh-annotation/client.js" # 200
Restarting the web service
Pick the command for your platform:
# macOS (launchd)
launchctl kickstart -k "gui/$(id -u)/com.dsh.web"
# WSL / Linux with systemd user services
# The unit name may differ by install method; check with:
# systemctl --user list-units | rg dsh
systemctl --user restart dsh-web
Environments without a service manager (e.g. some containers) often need no restart at all: client.js is served per request with no caching, so a hard refresh (Cmd/Ctrl+Shift+R) picks up plugin changes. The self-check commands above are platform-neutral.
Architecture notes
-
Pure browser-side: everything lives in
client.js(a hand-written CJS bundle, no build step, served no-cache per request) -
Message format (the literal protocol block sent to the model; follows the DSH
localepreference — zh or en):zh: 我批注了以下 N 处内容…\n\n1. 原文\n 批注:…\n\n请用「Annotation 1:…」…\n\n提问: en: I annotated the following N passage(s)…\n\n1. quote\n Note: …\n\nPlease respond… "Annotation 1: …"…\n\nAsk:The zh delimiter is 「提问:」(ask:) rather than 「问题:」(question:) — the heading line "回答我的问题:" also contains the latter, and the bubble-hiding surgery would misfire on it; the en delimiter is
Ask:. Hiding and reverse-parsing accept both languages plus the legacy 「问题:」 marker. -
Bubble hiding: user bubbles are plain-text rendered (a single MessageText node, not markdown); a MutationObserver in the microtask phase (before paint) splits at the last
\n提问:, cuts the annotation block, and attaches the chip; a 1 s polling fallback plus historical-message repair after refresh -
Reply chips: after streaming settles (
data-streamingremoved), eachAnnotation N:is replaced with a hoverable chip; item data is stored on the most recent user message carrying the annotation tag (tag.__annotationItems) and rebuilt after refresh; snapshot the text nodes collected by the TreeWalker before touching the DOM, then replace one by one — replacing a child mid-walk invalidates the walker pointer and only the first node gets processed -
Locale-aware: UI copy and the protocol block follow DSH's
localeservice (zh/en, live switch); historical bubbles stay parseable across languages; missing locale service falls back to zh -
IME-safe: the Enter interception carries
isComposing/ keyCode 229 guards; never hard-edits the composer textarea's DOM;setDraftonly assembles the annotation block at the last moment before submit and never clobbers the user's draft -
No reliance on send-completion event chains: bubble decoration uses MutationObserver + polling (
watchInputDraftcan be ineffective before the session is loaded at init; it is only a staging entry) -
Focus-chat compatible: works inside the focus conversation view of dsh-focus-chat — assistant rows there are
[data-focus-flow]containers with a*_assistantCSS-Modules class (plusdata-streamingwhile running); selection, annotation, reply chips, and re-anchoring all work in the focus tab alongside the main chat view
Version history
| Version | Highlights |
|---|---|
| v1.4.x | Locale-aware: zh/en UI copy and annotation protocol block, live switch via DSH locale service |
| v1.3.x | Numbered reply correspondence: format-instruction injection + hoverable Annotation N: chips (TreeWalker snapshot fix) |
| v1.2.x | Hidden annotation block in bubble: MutationObserver microtask zero-flicker + polling fallback + historical-message repair |
| v1.x | Self-contained annotation flow (replaces the v0.9 chip design): capture-Enter assembles the block and sends it with the message |
| v0.9.x | Early chip design (insertReference + slash codec), superseded by v1.x |
Friendly links
License
MIT
收录徽章
[](https://deepseek-plugin.org/plugins/omdsh-dev/dsh-annotation)把这段 markdown 粘贴到你的 GitHub README,链接回本插件详情页。徽章只声明已被本站收录,不代表安全认证。