跳过主要内容
W

dsh-selection-followup

wanruxi/dsh-selection-followup

在对话里选中文字,选区变成输入框上方的一张引用卡片,输入框里只写问题;发送时引用原文自动跟在消息后面,模型能读到完整内容。包名 dsh-quote-tray。

安装

dsh plugin --profile web add github:wanruxi/dsh-selection-followup

README

dsh-quote-tray · 选中追问

在 DeepSeek Harness 的对话里选中一段文字,旁边会浮出一个小「追问」按钮。 点一下,选中的内容就变成输入框上方的一张引用卡片,输入框里只留你要问的问题。

dsh-quote-tray 图标

它解决什么问题

看长回答时想追问某一段,通常得先复制,再切到输入框粘贴,再手动加 > 变成引用, 结果输入框里堆满引文,自己写的问题反而找不到。这个插件把引用挪到输入框外面: 选好、点一下,卡片就贴在输入框上方,你只管写问题。

发送时卡片里的内容会以 Markdown 引用块追加到消息末尾,模型收到的仍是原文。

行为

  • 只在非空选区出现。输入框内部、以及按钮自己的选区都会跳过,所以在输入框里打字 不会误触发。
  • 按钮跟着指针走:鼠标松开处右下方弹出;键盘选区(Shift+方向键 之类)则落在选区 上方。靠近窗口边缘会翻到另一侧,贴边收拢用的是按钮实测尺寸,中文「追问」和英文 「Ask」宽度不同也不会被切掉。
  • 点一下,选区变成一张卡片,贴在输入框上方。卡片有 ×,随时能删;也可以一次挂多 条,按添加顺序排列。
  • 卡片左右边缘和下面的输入框卡片对齐,不是贴着窗口边。对齐用的是宿主自己的两个 变量(--dsh-composer-side-clearance 和 --dsh-composer-card-max-width),所以 拖动分栏宽度、开合侧栏、嵌入式窄面板下都还对齐。
  • 卡片里只显示第一行前 80 个字符,鼠标悬停能看到全文;发给模型的始终是完整引用。
  • 引用成功后按钮原地变成一句「已引用」回执,停留 0.9 秒。这个回执不接收鼠标事件, 不会挡住你下一次点击。
  • 选区滚出可视范围就不再出现按钮——不会把按钮钉在屏幕边缘,让你去引用一段自己 已经看不见的字。
  • Ctrl+Shift+Q 不用鼠标也能引用当前选区。所有平台都只认 Ctrl:Cmd+Shift+Q 在 macOS 上是「注销」。
  • Esc 收起按钮。
  • 发送时,每条挂着的引用按添加顺序以 > … 引用块追加在问题之后,中间空一行。
  • 输入框必须有问题才发得出去:宿主会忽略空草稿的 Enter,卡片原样留着,不会丢。
  • 发送失败(网络、被拒)时,卡片会退回原位,不会凭空消失。
  • 引用前先规整选区:统一换行、去掉行尾空格、合并多余空行。
  • 在别的会话(侧边对话)里选的文字,不会被引到这个会话;卡片也按会话分开存。
  • 卡片存在 sessionStorage,刷新页面不会丢。
  • 超过 8000 字符会被截断并加省略号,悬停提示会说明,而不是悄悄截断。
  • 点击前不让焦点跑掉(mousedown 被 preventDefault),选区不会提前消失。

引用是怎么送出去的

宿主没有「输入框之外的额外上下文」这种公开通道,所以卡片本身不占草稿任何位置, 发送前的最后一公里改的是会话输入壳的三个函数:

函数时机作用
sinkSerialized每次提交开始把挂着的卡片取走,记在这条发送上
deps.defaultSink文本已经序列化好、即将发出把引用块拼到消息末尾
settleDetachedFailure宿主判定这次发送失败把卡片放回去

三个一起打补丁,或者一个都不打:只打一半会让引用要么发两遍、要么失败后丢掉。 每个补丁都可逆,输入框组件卸载时会原样还原;补丁装上后如果还有发送在飞,卡片也会 退回。

这条路的代价:它依赖 SessionInputShell 的内部方法名。宿主大改这几个函数时, 插件会在加载时检测不到接缝并保持沉默——卡片还能挂、能删,但不会再自动追加到消息 里,而不是把发送链路搞坏。

和相近插件的区别

目前有几个 Harness 插件都能把选区引用进对话,路线分三类:

本插件chip 引用卡片输入框外的注释
引用放在哪输入框上方,独立卡片输入框里的引用节点输入框上方独立列表
输入框里有什么只有你的问题引文和问题混在一起只有你的问题
怎么发出去发送时拼到消息末尾由引文源 codec 还原依赖 draftContexts 服务
对宿主的依赖sinkSerialized 等三个内部函数公开的 inputTriggers该版本没有此服务

chip 那条路最“官方”,但引用会挤在输入框里,这正是要被替换掉的观感; draftContexts 在实测的宿主版本里并不存在。剩下可选的就是改发送链路——代价写在 上面,换取的是输入框干净、引用照样送到。

测试

纯函数有离线测试,不用开浏览器:

npm test      # node --test test/helpers.test.mjs
npm run check # 两半都做语法检查

覆盖引用格式、消息拼装顺序、卡片预览、空白规整、按钮贴边收拢、离屏判定、会话隔离、 卡片增删与发送失败的恢复,以及那条发送补丁:拼装位置、成功消耗、失败与拒绝后的 回填、卸载还原、缺接缝时不动宿主。界面效果本身仍需人工看一遍。

安装

在要启用它的 Harness profile 里:

dsh plugin --profile <profile> add dsh-quote-tray

也可以把本仓库目录直接作为 bundle 装进插件管理器。浏览器半边立即生效,刷新页面即可, 不用重启。

0.5.0 起包名从 dsh-selection-followup 改成 dsh-quote-tray:旧名字在 npm 上和另一个 Harness 插件上都被占了,两者没法装在同一台机器上。装过旧版的话,先从 profile 里移除 dsh-selection-followup 再装这个。

兼容性

dsh.client.platform 是 web。浏览器半边用到 conversation.input.overlay 和 conversation.input.dock 两个槽位,以及 conversation 服务的输入壳。engines.dsh 声明的是实测下限。

宿主若把 sinkSerialized / deps.defaultSink 改名,卡片照常能加能删,只是不再自动 追加到消息里;这种情况下浏览器控制台会有一行 [quote-tray] could not hook this compose box 的警告,而不是静默失效。

conversation 服务是用 ctx.get('conversation') 取的,没有写进 inject。原因是动态 插件拿到的 ctx 是个代理:属性访问只对 inject 里声明过的服务放行,ctx.get() 才是 不过门禁的可选查找。写进 inject 会让插件在缺这个服务的宿主上整体加载失败,而卡片 界面本来还能用。

文件分工

文件作用
lib/index.js宿主半边——空实现,只为让 bundle 有个可加载入口
lib/client.js浏览器半边——选区监听、浮动按钮、卡片存储与发送补丁
test/helpers.test.mjs纯函数的离线测试
cordis.patch.yml把插件行插进插件树的 bundle patch
icon.svg插件图标
assets/icon-180.png商店展示图

许可

MIT。

相关插件