dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DeepSeek Harness 插件:在右侧栏的文档预览里直接圈画标注,标注可以保存到文档旁边,也可以填入会话(作为一条用户消息发给当前会话的智能体),让它按标注去改生成源,而不是靠用户打字描述。
English summary: annotate a document (image, SVG, PDF) in the right-sidebar preview, save the marks beside the file, and fill them into the session as a user message: a PDF is one document annotated once, written back as native PDF annotations attached to the message. Markdown is not annotated but edited: the difference against the version it was opened with goes into the session as a unified diff, and nothing is written to disk.
本插件要为 DSH 预览器里的下列文档提供标注,标注后均能保存并填入会话:
| 文档类型 | 后缀 | 状态 |
|---|---|---|
| 图片 | png jpg jpeg gif webp bmp ico | 已实现 |
| SVG | svg | 已实现(内联渲染,支持图元锚定) |
pdf | 已实现(整份文档一份标注:逐页画、一次保存,标注写回原生 PDF 注释对象) | |
| Markdown | md markdown | 已实现(编辑:改完与原文件对比,改动作为 unified diff 送进会话;插件不落盘) |
| HTML | html htm | 已实现(在固定 1024px 宽的渲染面上标注:元素锚定 + 坐标,只写标注文件,不产出标注图) |
| Office | doc docx ppt pptx xls xlsx | 待实现,仍由内置 Office 预览器打开 |
「保存」= 把标注写进文档旁边的侧车文件(Markdown 不落盘,见下);「填入会话」= 把标注(以及标注图、带批注的 PDF 副本、Markdown 的 diff)作为一条用户消息发进当前会话。目前图片、SVG、PDF、Markdown 与 HTML 走通了完整链路,Office 尚未接入。
专利说明书附图的返工意见通常是「这个标号指错了」「这里少一个件」「这条线该连到那边」——用文字描述既慢又容易误解。本插件让用户在图面上直接画箭头/圈选/文字,插件把图面坐标 + 落在哪个图元上 + 用户那句话一起交给智能体。
插件是一个标准的 DSH 第三方插件包(Host 半 + Client 半),装进 profile 即可:
// <profile>/package.json
{
"dependencies": { "dsh-annotator": "link:/Users/xujian/projects/dsh-annotator" },
"dsh": { "profile": { "bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-annotator"] } }
}
pnpm install # 在 profile 目录里建立链接
pnpm run build # 在本插件目录里构建 lib/index.js 与 lib/client.js
改动 Host 半后必须重启宿主进程:Node 会缓存已导入的插件模块,运行中的进程不会重新导入它(Client 半只需刷新页面)。开发时建议用隔离实例验证:
DSH_HOME=/tmp/dsh-annotatordev node --import tsx/esm apps/cli/src/bin.ts --profile annotatordev --port 3099 --no-open
present 卡片都能打开)。插件目前为 svg/png/jpg/jpeg/gif/webp/bmp/ico、pdf、md/markdown、html/htm 注册了预览实现,是这类文件的默认渲染器;下拉里仍可切回内置查看器。Markdown 走另一条路(编辑,不是标注):预览里直接改源文本,「对比」看这一版与打开时那一版的逐行差异(unified diff,两侧行号齐全),「送至会话」把这份 diff 作为一条用户消息发出去。插件不写这个文件:智能体拿到 diff 后自行落地到源文件,磁盘上的 md 在提交前后都是旧版。差异超过 400 行时,消息只展开前面几处,完整结果作为 <文件名>.edited.md 附件随消息发送。
对比视图里每一处改动下面都有一行说明输入框(「第 N 行起这一处的说明」):写一句"为什么这么改",它会作为消息里的「我的说明:」清单随 diff 一起发给智能体;说明挂在基线侧的行号上,所以你继续改别处时它不会漂。工具栏另有两个小工具:「复制 diff」把整份 unified diff 放进剪贴板(是完整那份,不是消息里被裁过的),以及常显的光标位置「第 N 行 · 第 M 列」。
编辑视图在关闭「自动换行」后显示行号槽(换行开关是文档工具栏那个)。开着换行时不显示行号,因为一行会占多个视觉行、行号必然与它对不上——那种情况下用工具栏的行列读数。
HTML 又是另一条路:在一份渲染面上标注,但不产出标注图。打开 .html 后文档会渲染在一个不能执行脚本、也不允许任何外部请求的同源框架里(自带 default-src 'none' 的 CSP,并剔除 <base> 与会刷新的 <meta>),画法与其他类型一样;区别在于每条标注除了坐标,还会记下它落在哪个元素上——tag/id/元素文字,以及一条从 body 起的选择器路径(形如 #s > p:nth-of-type(2),每一段都能在源文件里直接命中)。渲染宽度固定为 1024px 再缩放到面板宽度,所以缩放、拖窄拖宽面板都不会让坐标漂。已知取舍:脚本不执行、相对引用的图片与外部样式也不会加载(与 DSH 内置 HTML 预览的静态模式一致),因此靠脚本渲染出来的内容是空的。
SVG 图会内联渲染,因此标注能锚定到图元:Graphviz 产出的节点(<g><title>102</title>)会被识别为「元素标题 102」,圈到某个标号文字上则记为该文字的标签。
PDF 是一份文档一次标注:上排工具栏翻页(3 / 12 与「共 12 页 · 5 处标注」),每页各自画、各自撤销,翻页时已画的内容不会丢也不会串页;「仅保存」/「保存并提交给智能体」是整份文档的两个动作,各只按一次。
标注不修改原文档,只在文档旁边写文件:
| 文件 | 内容 |
|---|---|
<文件名>.annot.json | 标注文档:文档信息(绝对路径、sha256,PDF 另有 pageCount)、marks[](kind/color/points/page/anchor/text)、总体说明 |
<文件名>.annotated.png | 图片/SVG:原图 + 标注的合成图(2 倍分辨率),随消息作为图片附件发给模型 |
<文件名>.annotated.pdf | PDF:原件副本,标注写成原生 PDF 注释对象,同时作为附件随消息发送 |
| (无) | Markdown:不在磁盘上留任何东西。改动以 unified diff 进会话;只有 diff 超出消息预算时才把 <文件名>.edited.md 作为附件发送(附件存在会话的附件库里,不落在文档旁边) |
| (无) | HTML:只有标注文件。浏览器无法把渲染出来的文档栅格化成图片,所以没有标注图,标注靠 anchor.selector(元素选择器)定位,随消息的文本送出 |
两个名字都带着被标注文件自己的扩展名(fig1.svg → fig1.svg.annot.json):同目录下同主名不同后缀的两份文件各有各的标注,绝不会互相读成对方的那一份。
marks[].points 是图面像素坐标(原点在左上角);PDF 里每条标注另外带 page,坐标是那一页自己的页面单位(PDF 点),因此缩放、翻页、旋转页都不影响它指向的位置。marks[].anchor 记录标注落在哪个元素上:SVG 给 tag/id/title/元素文字/包围盒,HTML 给 tag/id/元素文字/包围盒与 selector(从 body 起、每段为 tag:nth-of-type(n) 或 #id 的精确路径)。原文被重画后 sha256 变化,插件会提示旧标注可能失准。
带批注的 PDF 里,箭头是 /Line、框选 /Square、圈选 /Circle、手绘 /Ink、文字 /FreeText,每个对象都带自绘的外观流,因此在任何阅读器(Acrobat、Preview、浏览器内置查看器、PDF.js)里都能看到与页面上一致的形状,也能在阅读器里继续编辑或删除;文字批注若是中文,外观交给阅读器用它自己的字体合成(自绘外观需要把 CJK 字体嵌进文件,那会让产物大一截)。
智能体侧同时收到一段【附图标注】消息:文档路径、页数与标注处数、侧车与带批注副本的路径、逐条编号的标注(PDF 会写明「第 N 页」),以及一句处理纪律(改生成源、未标注处不动、逐条回应)。插件还会注入一段系统提示,说明这类消息该怎么处理。
同一工作区里的姊妹标注器(Sati)把标注写进同一份侧车文件,但用的是 v2 形状(用 target 承载图面信息,而不是 figure)。本插件读得懂 v2 并继续写自己的 v1 形状,所以两边都能看到对方留下的标注。回存时本插件按自己的形状落盘,v2 独有、本插件不建模的字段(target.kind、逐条 targetFingerprint、锚点的 nodeId/ref)会在这一步丢掉。
早期版本按主名派生侧车名(fig1.svg → fig1.annot.json)。那个名字仍会被读回(作为优先级更低的只读回退),所以升级不会让既有标注消失;但同一个主名被同目录的另一份文件(fig1.png)共用时,回退读回会先核对文档指向的是不是当前文件,指向对方就拒绝。
更早的版本对 PDF 按页各存一份(report.pdf.p3.annot.json)。那些文件在整份文档还没有统一侧车时会被读回并合并成一份显示(pageCount 取出现过的最大页号,真实页数由浏览器半边下次保存时写回),旧文件原样保留不删。
anchor.selector 为准,points 只是辅助(渲染宽度变了坐标会漂)。git diff 的语义不同(你可能已经提交过中间状态)。文件在编辑期间被改动时,插件会警告这份 diff 基于较早的版本。@@ 头与行号都以行为单位;只改了文件末尾换行这类「行内看不到的差异」,对比区会显示为没有改动。<textarea>(因此插件至今零新增运行时依赖)。加高亮要引入 CodeMirror 并新开一个懒加载分块,作为独立一步评估。analyze_patent_figure 或图像输入模型回看)。开发规范(门禁、负控制、决策记录、架构红线)的家在 AGENTS.md,开工前先读它的 §2。决策背景在 docs/notes/。
pnpm run check # 三个门禁一次跑完:typecheck + lint + test:coverage
pnpm run build # tsc --noEmit + tsdown(Host ESM / Client 单文件 CJS)
pnpm test # vitest:地址解析、标注模型、侧车读写、路由、绘制、导出、锚定
pnpm run typecheck
Client 半边必须匹配 DSH 动态客户端契约:单文件 CJS、唯一副作用是 window.__ModuleLoader__.load({id, factory})、只把 react/react/jsx-runtime 留给模块表,其余全部内联(插件路由只发 client.*.js,发不了字体等静态资产)。