dsh-web-annotate
在 DSH 侧栏里跑一个真实浏览器,指向页面元素就地写批注,汇总成结构化指令。
Point at a live element inside the DSH sidebar, drop a comment on it, and get back a structured instruction list.
装不上、报错、或者行为和文档对不上,直接发邮件到 1992335297@qq.com,比开 Issue 更快被看到。

图为界面示意,按插件
lib/client.js的工具栏、拾取高亮、元素信息与批注列表样式渲染,页面内容为假数据,不是运行截图。可用node scripts/make-screenshots.mjs重新生成(源码在scripts/demo/annotate.html)。
它解决什么问题
改界面最累的不是改,是指路。「左边那个卡片的边距大了点」——模型得猜是哪个卡片;截一张图贴进去——又得反复圈。批注把指路变成点一下:鼠标停在哪个元素上,它的标签、尺寸、背景色、字体就摆在那儿,点一下写一句人话,最后汇总成一份带选择器的清单。
功能
- 真实浏览器:宿主侧用 Playwright 起 Chromium,页面是真跑的,不是画出来的缩略图。
- 画面推流:CDP
Page.startScreencast出帧,SSE 推到侧栏,页面一有变化就刷新。 - 元素拾取:开启批注模式后,鼠标指向哪里,就高亮哪个元素并显示
标签 / 尺寸 / 背景色 / 字体 / 字号 / 字重。 - 就地评论:点元素弹出输入框,回车即存;批注列表在下方可逐条删除。
- 框选区域:按住拖动可以框一块区域批注,适合「这一整块间距不对」这类说不清是哪个元素的意见。
- 一键回填:点 ↩ 把汇总直接写进对话输入框,不用复制粘贴。
- 快捷键:
Ctrl+Shift+.切换批注模式。 - 交互转发:退出拾取后,点击、滚轮、方向键、Enter、Tab、Esc、Backspace、PageUp/PageDown 都会转发给页面。
- 批注留得住:按页面地址存在
localStorage,关掉 tab 再打开还在;删完自动清空。 - 穿透 iframe:页面里嵌的 iframe(含跨域)也能点进去拾取,信息浮层会标出所在层级。
- 页面内输入:工具栏
⌨把一段文本(含中文)送进页面当前焦点元素,绕开键盘布局与输入法。 - 导出成文件:
⤓把汇总写成 Markdown,存到~/.dsh/web-annotate/下。 - 手动入口:对话输入框左侧有一个 ◱ 网页 按钮,随时点开面板自己输地址,不用等模型触发。
- 可被 DSH 接管:浏览器启动时暴露 CDP 端点,
playwright-mcp能以mode: attach接上同一个浏览器,模型操作的就是侧栏里看得见的这个。
让 DSH 在侧栏里操作浏览器
默认情况下 DSH 的浏览器工具启动的是独立的 headless Chromium,你看不见它在做什么。可以改成接管本插件的浏览器:
- 先打开一次「网页批注」面板,让浏览器起来(
GET /api/web-annotate/health里cdpEndpoint不为 null 即就绪)。 - 修改
$DSH_HOME/profiles/desktop/cordis.patch.yml里 playwright 提供方的配置:
- insert:
- id: browser-use
name: "@deepseek-ai/dsh-browser-use"
- id: browser-use-playwright-mcp
name: "@deepseek-ai/dsh-experimental-browser-use-playwright-mcp"
config:
mode: attach
endpoint: http://127.0.0.1:9222
- 重启 DSH。之后模型每次调用浏览器工具,操作的都是侧栏里那个浏览器。
注意:attach 模式下浏览器必须先在跑。如果插件没启动浏览器(没打开过面板、或插件被禁用),模型的浏览器工具会直接失败。想退回独立浏览器,把 mode 改回 launch、删掉 endpoint 即可。端口可用环境变量 DSH_ANNOTATE_CDP_PORT 改;端口被占用时插件会自动退化为不开调试端口,此时 cdpEndpoint 为 null。
模型侧行为
插件会往系统提示词里注册一段指导(web-annotate:guidance,order 8600),让模型在调试网页、验证前端改动时优先用这个面板:
调试网页、验证前端改动、或需要确认页面渲染效果时,优先让用户在 DSH 侧栏的「网页批注」面板里打开页面,而不是启动独立的 headless 浏览器,也不要让用户切到外部浏览器。
模型因此更容易主动建议「在侧栏打开这个地址」,你就能随时批注,不用等它自己切到外部浏览器。
不想要这个行为,卸载插件即可,提示词段随插件一起消失,不留残留。
汇总长这样
## 网页批注
页面:http://localhost:5173/
1. `#card-a`(div,936×54)
卡片内边距太大,收到 12px
2. `#card-b.accent`(div,936×54)
强调色换成主题蓝
用法
- 点右侧栏 tab 条的添加按钮,选 网页批注。
- 地址栏输入
localhost:5173,回车。首次启动浏览器要等几秒。 - 点工具栏的 ◱ 批注(或按
Ctrl+Shift+.)进入拾取模式。指向元素看信息,点一下写评论;按住拖动可以框一块区域。 - 点 ↩ 把汇总回填到输入框,或点 ⧉ 复制到剪贴板。
退出拾取模式后,点击和滚轮会正常传给页面,可以当普通预览用。
与 dsh-web-preview 的分工
| dsh-web-preview | dsh-web-annotate | |
|---|---|---|
| 渲染 | iframe 直出 | CDP 画面流 |
| 速度 | 接近原生 | 帧流,有几十毫秒延迟 |
| 能否批注 | 否(跨域拿不到 DOM) | 能 |
| 依赖 | 无 | 需要 Playwright(复用 DSH 自带的那份) |
两个插件可以同时装,各自一个 tab 类型。
技术实现
宿主 lib/index.js
├─ 从 DSH profile 目录解析 playwright,复用 DSH 自带的 chromium
├─ 每个地址一个 page 会话,CDP screencast 出帧
├─ SSE GET /api/web-annotate/frames?session=<id>
├─ POST /api/web-annotate/session 创建会话
├─ POST /api/web-annotate/input 鼠标 / 滚轮 / 键盘转发
├─ POST /api/web-annotate/pick 坐标 → 元素信息(elementFromPoint)
├─ POST /api/web-annotate/reload 重新加载
└─ DEL /api/web-annotate/session/<id> 关闭
没用 WebSocket:DSH 的 webServer 只暴露路由注册,不保证能拿到 http server 的 upgrade 事件。SSE 是普通响应,稳定可控;输入事件走 POST,量级完全够。
选择器生成走 id → 类名 → nth-of-type 的降级链,最多上溯 6 层。
权限与安全边界
这个插件比纯前端插件权限大,装之前请明确知道它做了什么。
- 会启动本地浏览器进程:使用 DSH 自带的 Playwright 与 Chromium,不额外下载。
- 会访问你输入的地址:只放行
http/https,拒绝file://与其它协议,不能读取本地文件。 - 不注入任意脚本:页面内只执行一个固定的取元素信息函数,不执行页面返回的内容。
- 不写入文件:批注只存在客户端内存里,复制由你自己触发。
- 会话隔离:每个地址一个 page,关闭 tab 即销毁;无客户端连接且空闲 5 分钟后自动回收。
dependencies为空,无preinstall/install/postinstall/prepare生命周期脚本。
已知限制
- 画面是帧流,快速滚动或动画多时会有轻微延迟。
- 拾取模式会拦截点击与滚动,要操作页面(点按钮、输入)需先退出拾取。
- 键盘只转发导航键与编辑键(方向键、Enter、Tab、Esc、Backspace、PageUp/PageDown);可打印字符走
⌨面板的整段输入,逐字键入不转。 - 回填依赖 DSH 的
conversation.input.left插槽存在。该插槽没挂上时自动退回复制到剪贴板。 - iframe 穿透最多 4 层;超出的部分会被当成普通 iframe 元素本身。
- 导出固定写到
~/.dsh/web-annotate/,不写项目目录。 - 批注存在
localStorage,按页面地址分键;换浏览器 profile 或清理站点数据会丢。 - 只在 Windows + DeepSeek Harness
0.2.x桌面端验证过。
开发与验证
node scripts/verify.mjs # 静态 + 路由级,17 项,不启动浏览器
node scripts/smoke.mjs # 端到端:起站点、起 chromium、取帧、拾取、输入、刷新、关闭,8 项
smoke.mjs 会真起一个本地 HTTP 站点和 Chromium,实测输出形如:
PASS 创建会话并打开页面
frame=13351 bytes, device=1000x700
PASS SSE 收到首帧画面
picked <div> #card-a 936x54
bg=rgb(255, 255, 255) font=16px / system-ui
PASS 拾取元素返回标签与样式
...
结果: 8 passed, 0 failed
安装
一条命令
dsh plugin --profile desktop add "github:nubeslove/dsh-web-annotate"
然后在 $DSH_HOME/profiles/desktop/package.json 的 dsh.profile.bundles 里追加 "dsh-web-annotate",重启 DSH。
插件本身不含运行时依赖;Playwright 在运行时从 DSH profile 目录解析,复用 DSH 自带的 Chromium,不会额外下载浏览器。
本地开发
自己改代码时用 link 指向工作副本:
dsh plugin --profile desktop add "link:/absolute/path/to/dsh-web-annotate"
路径含空格时手工处理:在 profiles/desktop/package.json 的 dependencies 加 "dsh-web-annotate": "link:E:/path/to/dsh-web-annotate",在 dsh.profile.bundles 追加 "dsh-web-annotate",并在 profiles/desktop/node_modules/ 下建指向本目录的 Junction,然后重启 DSH。
更新记录
- 0.4.1 — 往系统提示词里注册
web-annotate:guidance,让模型调试网页时优先用这个面板而不是 headless 浏览器,用户可以随时批注。自检增至 20 项。 - 0.4.0 — 新增对话输入框左侧的 ◱ 网页 按钮,手动打开面板输地址;浏览器启动时暴露 CDP 端点(
/health的cdpEndpoint),配合mode: attach可让 DSH 的浏览器工具接管侧栏这个浏览器。冒烟增至 12 项,含真实 CDP 端点存活检查。 - 0.3.0 — 攻克三处硬限制:拾取可递归穿透 iframe(
frame.childFrames()逐层进入子文档,跨域也能读,最多 4 层,浮层显示所在层级);新增⌨页面文本输入面板,用keyboard.insertText送中文等任意字符;新增⤓导出,把汇总写成 Markdown 到~/.dsh/web-annotate/。自检 19 项,冒烟 11 项。 - 0.2.0 — 批注汇总可直接回填输入框(在
conversation.input.left挂桥接组件拿到inputActions,桥不可用时退回复制);新增Ctrl+Shift+.快捷键、拖动框选区域、方向键与编辑键转发、批注localStorage持久化。自检增至 17 项。 - 0.1.0 — 首个版本。
许可证
反馈与联系 / Feedback & Contact
遇到 bug、安装失败、行为和文档不一致,或者想要某个功能,都可以直接发邮件。
邮件里带上这三样,定位会快很多:
- 插件版本(
package.json里的version) - DSH 版本(
dsh --version) - 报错的完整文字或截图
也可以在 GitHub Issues 提,但邮件更快。
No comments yet. Be the first to write one.