dsh-drag
把左侧边栏会话列表里的一个会话拖到聊天记录区域松开,一条规范的会话引用
(@[标题](dsh-session:…) mention)就会以引用 chip 的形式插进当前输入草稿——
发送前还能再检查一眼。dsh Web GUI 插件(DSH 0.1.5-rc.x)。
为什么能成立
- 侧边栏的会话行本来就是原生 HTML5 drag 源:
ui-workspace的SessionNodeItem在dragstart里dataTransfer.setData('text/plain', node.id)(已在 0.1.5-rc.2 的 bundle 里核对)。缺的只是放下的一侧。 - 放下走的是编辑器自己的引用通道:
conversation.input.for(actx).insertReference—— 与输入框内@//挑选流程同一条 span-CAS 路径,插进去的是真正的 chip 节点,而不是一段文本。 dragover/drop挂在 document 捕获阶段并stopPropagation():输入框是 Lexical 编辑器,它在编辑器根节点上自己监听drop,会把text/plain载荷 (也就是裸 session id)当文本贴进草稿——冒泡阶段拦已经太晚,捕获阶段才行。dragstart/dragend仍走冒泡:会话行的 payload 由它自己的 handler 先写。dragstart里把effectAllowed从行自带的move放宽成copyMove:声明move的拖拽配dropEffect = 'copy'不是合法组合,Chrome 宽容、严格的引擎会 直接拒绝这次放下。- 拖回侧边栏(
[role="tree"]里的任何行)永远不拦截:官方的行内重排序行为 原样保留。
行为
- 按住一个会话行开始拖动,指针进入聊天区域 → 接受投放(光标出现"复制"角标);
- 在记录区域内松开 → 引用 chip 插入草稿(span 取自投放瞬间的
caretSpan()snapshot.draftRev);
- 松开在侧边栏 → 什么都不发生(官方拖拽继续接管);工作区分组行、文件、纯文本 等其它拖拽源一律不激活;
- 拖放当前会话自己 → 提示「当前会话不需要引用自己」;插入被拒绝(相位/版本 不匹配)→ 提示「会话引用插入失败」。
不画任何自己的浮层:拖动时的视觉反馈只有浏览器原生的拖影(跟随鼠标的会话行 副本)和上面那个复制角标。早期版本额外弹了一个「松开以引用会话『标题』」气泡, 结果就是光标旁边出现两条标题,纯噪音,已删除。
安装
# 从 GitHub(收录在市场里的那条)
dsh plugin --profile web add github:ttmouse/dsh-drag
# 或本地开发
cd ~/.dsh/profiles/web && pnpm add "dsh-drag@link:/path/to/dsh-drag"
lib/ 已随仓库提交(pnpm build 的产物),所以 GitHub 安装不需要构建授权。
dsh.profile.bundles 里加入 "dsh-drag"(dsh plugin add 会自动加),重启
dsh web 并硬刷新浏览器。
开发
pnpm install
pnpm build # scripts/build.mjs — src/client.js 包进 loader 闭包写出 lib/
pnpm test # test/smoke.mjs + test/apply-wiring.mjs + scripts/interaction.mjs
test/apply-wiring.mjs:只经apply(ctx)驱动整条链路(假 document + 假宿主服务):断言四个 document 监听真的被装上、dragover/drop的相位是 捕获、接受时确实stopPropagation、effectAllowed被放宽、一次完整拖放手势能 插 chip、span 取的是caretSpan()+snapshot.draftRev、侧边栏与工作区行一律 放行、surface 挂在 inject scope 上(不是 root fiber)、dispose 撤干净。0.1.0 的 bug 是apply()压根没挂 surface(模块导出的createDropSurface自己是对的, 老测试直接调它所以全绿)——这个文件就是为它存在的回归闸门:把src/client.js回滚到修复前,只有它会红;把上面任一条改坏也会红。test/smoke.mjs:假 doc 手势驱动createDropSurface全部分支(mention 编码 对齐宿主dsh-session-reference的规范形、侧边栏放行、dragend 之后 drop 失效、 自引用提示、插入拒绝提示、apply() 的 dock 槽与 inject 面、bundle 形状)。scripts/interaction.mjs:jsdom + 真实冒泡事件渲染真实 loader bundle,只调apply(ctx),在真实 DOM(侧边栏树 + 聊天区)上跑完整手势,并断言插件没有往 文档里插任何节点(自定义浮层回归会直接红)。
改完 pnpm build 不需要重启 dsh web:bundle 路由按内容哈希取当前字节,
浏览器硬刷新即可拿到新版本。
浏览器半(lib/client.js)零依赖、零 external:不 require 任何模块,宿主服务
全部经 cordis ctx 注入。
投放目标怎么来的
投放时现算目标输入机:sessions.list.getSnapshot().current → sessions.scope(id)
→ conversation.input.for(actx),再取 caretSpan()(detect 坐标的光标 span)和
snapshot.draftRev(输入机版本号,span CAS 用)调 insertReference。所以拖放面
不依赖任何槽位渲染,挂载只发生在 apply() 里。
conversation.input.dock 里仍注册了一个渲染 null 的条目(id: dsh-drag,
order 40)——目前没有任何代码消费它的 inject 面,它是上一版设计的遗留,可以
直接删掉;留着只是无害。文档级拖放面在 apply() 里直接挂 document。
License
MIT
No comments yet. Be the first to write one.