Skip to content

Releases

v1.6.5 — unified toolbar and annotation layout fixes

Choose a tag to compare

@jabingu jabingu released this 14 Oct 04:26

Selection Toolbar 1.6.5

本次更新集中修复选区工具条、批注入口和侧边对话的交互问题。

  • 单消息与跨消息选区共用一个工具条,包含「批注 / 引用 / 侧边对话」。独立引用浮窗已移除,不再通过隐藏第二个窗口实现合并。
  • 修复跨消息选区经过复制、状态按钮时不弹框的问题,以及快速重选、旧按钮状态和重复文字锚点问题。
  • 主动取消选区时清除旧摘录;流式重渲染造成选区缩短或丢失时保留原始摘录。
  • 待发送批注入口移到输入区左侧的预留行,修复刚保存批注后覆盖消息操作栏、需要滚动才恢复的问题。
  • 侧边对话核对目标线程后填入草稿,不自动发送;停用、热替换和切换会话时取消旧异步操作。
  • 精简并更新中英文 README;开发环境要求与 DSH 对齐为 ^22.19.0 || >=24.0.0,CI 改为 Node 22 / 24。

安装

在 DSH 插件页面更新 dsh-selection-group,或添加下方附件 dsh-selection-group-1.6.5.tgz 的下载地址。更新后完全退出并重启 DSH。

发布包包含已构建的 lib/ 和 client.js,以及构建输入、测试和许可文件,无需先自行构建。侧边对话动作需要启用 dsh-better-sidebar。

验证

  • Node 22 / 24 的 CI 均通过全部 115 项自动化测试,覆盖选区、批注布局、流式恢复、线程归属和生命周期。
  • 检查解包后的构建一致性和完整测试,核对附件与本地发布包的 SHA-256。
  • 批注布局已用模拟本机 DSH 贴底结构的 Chrome 测试页验证;实际桌面效果仍需按使用环境核对。

完整变更见 CHANGELOG。

v1.6.4 — side chat action, chat-only scope, self-verifying package

Choose a tag to compare

@jabingu jabingu released this 12 Oct 15:17

Selection Toolbar 1.6.4

自 1.3.8 以来的全部变更。最要紧的两条:

  • 新增第三个动作「◧ 侧边对话」:把选中文字原样放进 dsh-better-sidebar 的侧边对话输入框,
    不自动发送、零 token(上下文要你自己按发送才提交)。
  • 插件只在对话里生效:侧边栏、以及任何文件内容上的选区都不再弹框。

完整变更见 CHANGELOG.md。


[1.6.4]

修复(发布包)

  • npm 发布包缺构建输入,装下去无法自测:files 白名单里没有 vendor/、patches/、scripts/、src/,
    而 scripts/build.mjs 第一步就要读它们。实测在解包目录里跑 npm test:
    Error: ENOENT .../src/toolbar.js(补 src/ 之前)与 vendor/manifest.json(补之前)。
    现在四者都在 files 里,发布包大小 103 KB → 204 KB(26 个文件),
    并在解包出来的干净目录里验证过:npm test → 69/69 通过,其中包含 vendor sha256 校验与补丁落盘断言。
  • build.mjs 新增 --check 模式:只校验 client.js 是否与 vendor/ + src/ 一致,不重写文件。
    npm test / npm run verify 与 CI 都改用它——否则测试流程可能先把产物"修好"再去断言它,漂移就被洗掉了。
    已实测:无漂移时退出 0 并打印版本号与行数/字节数;人为改动后退出 1 并打印两边尺寸以及第一处差异
    (行号、字符偏移、两侧各 24 个字符的上下文)——只报尺寸是不够的:版本号从 1.6.3 换成 1.6.4 时
    两个尺寸完全相同,报告会变成"漂移了但看起来一模一样"。
  • build.mjs 读取构建输入失败时,现在抛出指名的诊断(哪个文件、哪个目录该带上),而不是深层 ENOENT。
  • .gitignore 忽略 *.tgz(本地打包验证用,不提交)。

[1.6.3]

修复(范围收紧:只在聊天里生效)

  • 在侧边栏打开的文件里选文字,也会弹出这个框。 现在整个插件只在对话内容里生效;右侧栏
    (文件树 / 文件查看器 / 侧边对话 / 终端 / 任务)与任何文件内容——不管在右侧栏还是中间主区打开
    ——都不再出现这个框。
  • 根因是我自己的补丁,不是上游:上游 annotation 的 settle 里有 if (rootEl === null) return 这道
    "这算不算可批注的根"的门;我为了"选中当回合立即出框"(immediate-toolbar)重写了那段,把这道门漏掉了。
    而这个工具条只在点击动作时才用到 ui.selectionRoot,画框从不检查它——所以任何选区都会被画上框。
    现在把 rootEl === null 的判断原样保留在立即绘制之前。
  • 同时按要求把文件正文路径也关掉:上游 annotation 本来主动支持宿主文本预览(DOCUMENT_TEXT +
    [data-textpreview-url]),而 dsh-better-sidebar 自己已经在文件查看器上提供「添加到对话」——
    两套叠在一起既有噪音、动作又是错语境的,所以合并包彻底不做文件选区。
  • 右侧栏整体拒绝:注解包里任何落在 [data-slot="sidebar"] / sidebar.* 子树内的选区都返回"没有根",
    这样即使是一个聊天行被渲染在右侧栏(例如侧边对话视图),也不会弹框。

测试

69 项(原 66 项)。新增三条:文件预览里选文字 → 完全没有框(且独立「引用」按钮保持隐藏);
先选文件再选对话 → 对话照常出框(确认不是一刀切关掉);右侧栏里的聊天行也不出框
(按列拒绝,而不是只按行类型判断)。

[1.6.2]

变更

  • 按钮文案从「◧ 放进侧边对话」改回 「◧ 侧边对话」(英文 ◧ Side chat)——功能不变,
    只是名字更短。悬浮提示仍写明"不自动发出、发送与否由你决定"。

[1.6.1]

修复(实测两次踩空:输入框定位)

  • 「放进侧边对话」在真机上完全没生效:提示"输入框 4 秒内没有出现"。我按结构定位那个输入框的四条规则全错,
    而且每一条都被我自己写的过浅测试桩放过了:

    我写的规则 真实情况
    卡片的第一个子元素是按钮 第一个子元素是 <textarea>,按钮在第 3 层 → 永远不匹配
    按钮的 closest('div') 就是卡片 落到了卡片内层的 bar,那里没有输入框
    从 textarea 往上找"包含按钮的祖先" 爬过了界,把选中文字写进了同页无关的文本编辑器(探针实测)
    容器必须直接拥有按钮 按钮是 bar 的子元素,不是卡片的 → 仍不匹配
  • 现在的规则:从每个 <textarea> 出发,它自己所在的那个容器里必须在两层内存在发送按钮
    (title="发送" / "Send",这是那个插件自己的本地化文案);若多个候选,取离按钮最近的那个 textarea。
    这样既匹配真实结构(字段与 bar 是兄弟,按钮是 bar 的子元素),又不会跨容器误抓。

  • 测试桩换成真实嵌套:之前那个桩把按钮放在卡片第一层,让错规则也能"通过"。现在桩复刻真实结构
    (textarea 是卡片第一个子元素、按钮在 bar 里),并新增两条:同页更早位置放一个无关编辑器 textarea
    时只有侧边对话输入框被写、编辑器分毫不动;没有发送按钮时必须判定为"没有输入框",绝不猜一个。

[1.6.0]

已被 1.6.1 修复:1.6.0 的输入框定位有误,真机上不会生效。

变更

  • 去掉引用外壳,选中文字原样放进侧边对话输入框:不再包 **引用自主对话:** 标题与 > 前缀,
    就是把选中文字当纯文本放进那个输入框(光标在末尾)。读起来和你自己粘进去的一样,随手可改。
  • 按钮文案随之改为 「◧ 放进侧边对话」(英文 ◧ Send to side chat);行为不变——不自动发送、
    零 token
    ,上下文仍要等你按发送才提交。
  • 诊断里 sidechatLast 的 quoted 改名为 inserted(语义变准了)。

[1.5.0]

变更(行为变更:不再自动发送)

  • 第三个动作改为「◧ 侧边引用」,且不再直接发出:选中文字后,新建一条空的侧边对话线程,并把
    选中文字作为引用块写进那个 tab 自己的输入框,光标停在末尾——发不发、什么时候发由你决定。
    点这个按钮不再消耗任何 token:线程是最小创建(question: ''),上下文要等你按发送才提交。
    这修掉了 1.4.0 的行为:当时点一下就把选中文字当第一条消息发出去,等于替你付了一次上下文注入的钱。

  • 写进输入框的引用块形状(那个输入框是纯 <textarea>,没有 chip 机制;1.6.0 已去掉这层外壳):

    **引用自主对话:**
    > 选中的第一行
    > 选中的第二行
    

    输入框里已有草稿时追加,不覆盖。

实现要点

  • React 受控输入框:那个输入框是 React 18 受控组件(元素上装了自己的 value 访问器,
    直接 field.value = … 会被下一次渲染抹掉)。写入必须走原型链上的 value setter(绑定到元素本身)
    再派发 input 事件,React 才会当成用户输入接收。
  • 不靠类名定位输入框:该插件的 CSS 模块类名带哈希、重建即变。改为按结构定位——
    "最后一个子元素是 <textarea>、第一个子元素是按钮"的卡片;因此不会误抓会话输入框(contenteditable)
    或侧栏文件编辑器。找不到时明确提示("请先展开右侧底部工作台"),不会假装引用放进去了。

诊断

  • sidechatLast 取值改为 quoted / no-composer / no-session / failed(1.4.0 的 sent 已不存在)。
  • 排障表新增一行「有按钮但引用没进输入框」;window.__DSH_SELECTION_GROUP_SIDECHAT_POLL_MS__
    可改等待输入框出现的时长(默认 4000ms,测试用)。

修复(实测到的崩溃)

  • SIDECHAT_DRAFT_POLL_MS is not defined:重构时把常量声明改成了函数,却把默认分支读的那个常量
    留成了纯引用,于是第一次真机点按钮就报"侧边对话失败:SIDECHAT_DRAFT_POLL_MS is not defined"。
    浏览器 bundle 没有构建期符号检查,而当时所有测试都设了 window 覆盖值,正好绕过出问题的那条路径
    ——所以 62 项全绿也没抓到。现在:常量补回;测试默认不再设置覆盖值(只在需要缩短等待的那条用例里设),
    并新增一条不设任何覆盖的用例,断言默认路径会真的等满 4 秒后才报"输入框没出现"(若常量缺失,
    会在同一个微任务里立刻变成 failed);另加一条静态审计(test/contract.test.mjs),
    扫描 glue 里所有 大写_下划线 标识符,凡是用到却没声明的一律测试失败。

测试

64 项(原 59 项)。新增:空线程创建(question 为空、没有任何 prompt 请求)、引用块内容与
「已有草稿则追加」、React 受控路径(模拟 React 18 的 own value 访问器,断言写入经原型 setter 生效)、
输入框不存在时提示、「选中文字绝不上线」、不设任何 window 覆盖值跑默认路径(崩过一次的正是这条),
以及 glue 常量声明的静态审计。

[1.4.0]

新增

  • 第三个动作「◧ 侧边对话」(已被 1.5.0 取代:当时是"把选中文字作为第一条消息直接发出")
    在 dsh-better-sidebar 的侧边对话里新建线程。对接方式(不去改那个插件):
    POST sidebar/api/sidechat.start 建线程 → 把线程 id 写进它自己的 dsh-sidebar:v1:sidechat-thread 键
    (先写后开)→ 用它的扩展服务 ctx.betterSidebar.openTab({ type: 'sidechat' }, { sessionId }) 打开 tab。
  • 诊断新增 sidechatEnabled / sidechatService / sidechatLast / sidechatLastThread / sidechatLastError。

设计取舍(1.5.0 仍然成立的部分)

  • 邻居是可选依赖,不是依赖:dsh-better-sidebar 不存在(或没启用)时,该动作根本不发布,
    框里仍是「批注 + 引用」。因为服务在每次渲染时重查(而不是 inject 一个可选服务——那会让整个
    fiber 等一个可能永远不来的邻居),所以它之后才挂载也能被接上。
  • 失败一律出声:主会话没在运行时那条路由会拒绝(409)、路由取不到、答案不是 JSON、
    childId 缺失——每一种都给出带原因的提示并不开 tab;线程建好但 tab 打不开则明说"线程已建好、
    tab 没打开"(它仍能从工作台的线程菜单里找到)。
  • 截断:选中超过 4000 个码点只放前 4000 个(尾部 …)。
  • 请求只在按下时发生:这是本包唯一的网络请求(同源一条路由);「批注 / 引用」两个动作不含网络请求。

v1.3.8 — a failing test can no longer hang the job

Choose a tag to compare

@jabingu jabingu released this 11 Oct 14:51

修复

  • 一次失败不再让 CI 整个卡住:测试里断言失败会跳过清理,留下的 jsdom 窗口与插件定时器
    会一直占住进程——所以 CI 从来只表现为"卡住",而不是红色失败(三次挂起、交替出现在两格上,
    根因就是这个)。现在:插件里的所有定时器一律 unref()(浏览器没有该 API,自动成为空操作),
    测试用 afterEach 无条件关闭窗口。用一个"故意失败且不清理"的临时测试验证过:
    进程正常退出并报红(pass 34 / fail 1),而不是挂住。
  • 随之暴露并修掉的偶发失败:diagnostics expire by themselves… 用固定 500ms 等一个 150ms 的过期定时器,
    在 CI 负载下定时器会晚到。测试里的固定等待改为等条件(waitFor,含 250ms 节流写入的最后一份快照)。
    本地连跑 4 次全绿,并在 4 路 CPU 负载下再跑 2 次全绿。