dsh-quote-comment
Verifieddsh-quote-comment · v0.1.1 · MIT · Web UI
选中会话内容即浮出「添加评论」按钮,点击把选中文本作为引用插入聊天输入框。
Install
dsh plugin add dsh-quote-comment Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Published to npm without a public repository. Inspect the package contents before installing.
Tags
Creators
Readme
dsh-quote-comment
在 DSH 会话里用鼠标选中任意内容 → 选区旁浮出「添加评论」按钮 → 点击即把选中文本作为一枚原子引用 chip 插入聊天输入框,光标落在 chip 之后,直接接着写你的评论。
chip 上只显示一行摘要(悬停看全文),发送时由本插件的 codec 展开成 Markdown 引用(每行 > )——输入框里干净,模型收到的是完整的被引内容。
这就是「快速引用选中内容与模型交流」:不用手打 > ,也不用复制粘贴。
交互细节
| 动作 | 行为 |
|---|---|
| 在会话正文里选中文字 | 选区右下方浮出「添加评论」胶囊按钮(悬停显示选区预览) |
| 点击按钮 | 选中文本作为引用 chip 插入输入框(chip 显示 40 字摘要),光标落在 chip 之后;按钮消失、选区清空、焦点回到输入框 |
| 发送消息 | chip 按 codec.serialize 展开成 Markdown 引用块(每行 > ,空行写成 >,末尾一空行) |
| 滚动 / 缩放窗口 | 按钮跟随选区位置 |
| 按 Esc | 收起按钮 |
| 在输入框内选中文字 | 不出现按钮(只有会话正文里的选中才算) |
| 插入通道 | ① 官方引用通道 actx.bail("slash/input-insert-reference") → ② 官方 inputActions.insertText(text, captureInsertion()) → ③ 合成 paste 事件 → ④ 写入剪贴板并提示「已复制,请粘贴」 |
实现要点
- 纯客户端插件:宿主半身
lib/index.js只打印一行日志,全部行为在lib/client.js(无构建步骤,直接就是浏览器可加载的 UMD 包装,window.__ModuleLoader__.load({ id: "dsh-quote-comment" }))。 - 浮层挂在官方
shell.overlay槽位({ kind: "list", scope: "root" },由@deepseek-ai/dsh-client-ui-layout的 AppFrame 渲染,z-index 20 的pointer-events:none全屏层,直接子元素恢复pointer-events:auto),不占用会话排版、不需要会话作用域。 - 引用 chip 走官方引用通道(与官方
@引用同一条路):captureInsertion()取 span → 在会话作用域上actx.bail(actx, "slash/input-insert-reference", { reference, span })(会话作用域经sessions.binding(sessionId).ctx取,sessionId来自uiSession.current.getSnapshot().key)→ ui-conversation 的SessionInputShell.insertReference把它落成 Lexical 的reference-chip节点(data-composer-chip="quote-comment"),并自动补一个尾随空格。 - chip 只带
source名字,序列化靠插件自己注册的属主:ctx.get("inputTriggers").registerSource({ trigger: "#", name: "quote-comment", candidates: () => [], codec: { clipboardText, serialize } }),提交消息时 inputTriggers 按名字找到本插件、调用codec.serialize(ref)把ref(原始选中文本)展开成引用块。没有这个属主,发送会被no serializer for reference source "quote-comment"挡住,所以注册失败时(服务缺失/名字被占)自动退回纯文本路径。 - 兜底写输入框走官方
inputActions(ui-conversation 经uiSession服务以props.inputActions暴露):insertText(text, captureInsertion())。输入框是 Lexical 编辑器,document.execCommand("insertText")对它不可靠,所以再兜底用合成paste事件。 - 会话正文判定用官方 DOM 锚点:
[data-chat-flow]/[data-chat-flow-key]/[data-chat-anchor-key]/[data-conversation-scroll],并排除[data-composer-seat](输入区)与插件自己的[data-quote-comment-root]。 - 插入后光标必须经 Lexical 复位:只做裸 DOM
focus()会把光标丢到 contenteditable 的开头——官方注释原文 "a bare DOM focus on the contenteditable would land the caret at the start instead"(@deepseek-ai/dsh-client-ui-conversation/lib/client.js:13815-13823),于是光标停在 chip 之前。插件照官方两步法复位:rootElement.focus({ preventScroll: true })→rootElement.__lexicalEditor.focus()(Lexical 把 editor 实例挂在根元素上,见同 bundle:10026),让模型里的选区(插入后 = chip 之后)重新刷回 DOM。
安装到 desktop profile
profile 目录:%USERPROFILE%\.dsh\profiles\desktop(本机 C:\Users\wangj\.dsh\profiles\desktop)。
$src = 'C:\Users\wangj\Documents\deepseek-harness\default-workspace\dsh-quote-comment'
$profile = "$env:USERPROFILE\.dsh\profiles\desktop"
# 1) 把源码目录链接进 profile 的 node_modules(junction,改动即时生效,不用重新拷贝)
New-Item -ItemType Junction -Path "$profile\node_modules\dsh-quote-comment" -Target $src
- 在
$profile\package.json里登记两处(插件生效必须同时出现):
{
"dependencies": {
"dsh-quote-comment": "link:C:/Users/wangj/Documents/deepseek-harness/default-workspace/dsh-quote-comment"
},
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"@deepseek-ai/dsh-experimental-agent-team-profile",
"dshmarket",
"billion-context",
"@vectorize-io/hindsight-coding-agents",
"dsh-cost-meter",
"dsh-quote-comment"
]
}
}
}
- 重启 DSH(客户端 bundle 在启动时组图,改完要重启;重启后刷新页面)。
卸载:把 bundles 与 dependencies 里的 dsh-quote-comment 去掉,删掉 node_modules\dsh-quote-comment 这个 junction,重启即可。
自测
& "C:\Users\wangj\.dsh\dsh-runtimes\dsh-primary-runtime\dependencies\node\bin\node.exe" test\smoke.mjs
test/smoke.mjs 用迷你 DOM + 迷你 React 在 Node 里驱动 lib/client.js,覆盖 51 项断言:模块契约、选区浮出按钮、定位、官方引用 chip 通道(属主注册、reference 载荷、codec 序列化、摘要截断)、无 chip 服务时回退 inputActions.insertText、多行引用格式、输入区选中不触发、paste 兜底、剪贴板兜底、插入后经 Lexical 复位光标(无 __lexicalEditor 时退化为裸 focus)。
test/install-check.mjs 另做 17 项安装自检(profile 登记、加载器清单契约、bundle 形态、宿主半身)。
重启后验收(30 秒)
- 退出并重开 DeepSeek Harness(客户端 bundle 只在启动时组图,
rev由内容哈希推导,改代码后重启即换新 URL,不会吃到旧缓存),刷新页面。 - 在任意会话的模型回答里用鼠标选中一段文字 → 选区右下方出现「添加评论」胶囊按钮。
- 点它 → 输入框里出现一枚引用 chip(气泡图标 + 选中内容摘要),光标在 chip 之后,接着写你的评论即可;发送后模型看到的是完整的
>引用块。
想确认插件是否真的加载了,可在 DevTools Console 里跑:
document.querySelectorAll('[data-quote-comment-root]').length // 1 = 浮层已挂载
document.querySelector('[data-composer-chip="quote-comment"]') !== null // true = chip 插入成功
若按钮不出现,按顺序排查:插件是否在 profile 的 dependencies + dsh.profile.bundles 两处都登记(跑 node test/install-check.mjs);重启是否真的发生(宿主日志里应有 [dsh-quote-comment] host half loaded);选中的是否在会话正文而非输入框内。
若出现的是 > 选中内容 这样的纯文本而不是 chip,说明官方引用通道没走通(回退链生效):此时功能仍可用,但可以检查 ctx.get("inputTriggers") / ctx.get("sessions") 是否改名。
已知边界
- 只处理鼠标选区(
selectionchange/mouseup);键盘跨消息选中同样覆盖(走selectionchange)。 - chip 的
appearance用官方三种图标里唯一的对话图标session(另两种是file/folder);ref存的是原始选中文本,所以 chip 是「文本引用」而不是「会话引用」,点击 chip 不会打开别的会话(官方引用源才提供openReference)。 - 引用块用 Markdown
>,与模型交流时语义明确,也不会触发输入框的/命令菜单。 - 插件注册的属主触发字符是
#且不提供补全候选:输入#不会弹出额外菜单项。若日后与别的插件撞名((trigger,name)唯一),插件会自动改用纯文本路径而不会报错。 - 若 DSH 后续改了
shell.overlay槽位名、inputActions方法名、slash/input-insert-reference事件名或输入框锚点,需要同步更新lib/client.js顶部的选择器/常量与resolveInputActions()。
更新记录
- 0.1.1 — 修复「点添加评论后光标停在 chip 之前」:插入后按官方两步法经 Lexical 复位选区(
rootElement.focus()→rootElement.__lexicalEditor.focus()),test/smoke.mjs增加两条回归断言 + 一条无 Lexical 实例时的退化断言。 - 0.1.0 — 首个版本:会话正文选中 → 浮出「添加评论」→ 经官方引用通道插入原子 chip(注册
#quote-comment引用属主负责提交时序列化),四级插入兜底(chip →inputActions.insertText→ 合成paste→ 剪贴板)。