dsh-client-ui-quote 0.11.4-lab.9 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,17 @@
2
2
 
3
3
  > 在 DSH 里**选中一句话**,浮出「评论 / 添加到对话 / 侧边对话」——把选区以 DSH 原生**引用芯片**插进输入框,或写进右侧栏那条独立侧边对话的输入框 —— 按 Kimi Code 的交互实现的 DSH 客户端插件。
4
4
 
5
- 选中正文 → 浮层动作 → 引用变成输入框里的行内胶囊(不是纯文本),评论也放在同一颗芯片里;发送之后,引用和评论**合成一颗胶囊**留在消息里,不会摊成一屏 `>` 行。第三项「侧边对话」则把引用写进右侧栏一条**独立的侧边线程的输入框**(一条主会话一条线程,反复点就反复追加),问什么由你写,主会话一点都不动。评论框里**引用正文本身也可编辑**(选多了当场裁);从**文件预览或终端**划选的引用以**围栏代码块**的线格式发给模型,模型看到的是真正的代码块。
5
+ 选中正文 → 浮层动作 → 引用变成输入框里的行内胶囊(不是纯文本),评论也放在同一颗芯片里;发送之后,引用和评论**合成一颗胶囊**留在消息里,不会摊成一屏 `>` 行。第三项「侧边对话」则把引用写进右侧栏一条**独立的侧边线程的输入框**(一条主会话一条线程,反复点就反复追加),问什么由你写,主会话一点都不动。评论框里**引用正文本身也可编辑**(选多了当场裁);从**文件预览、终端或 diff**划选的引用以**围栏代码块**的线格式发给模型,模型看到的是真正的代码块。引用落进草稿后,**原句在来源消息里留下高亮与序号**,输入框上方出现 **`引用 ×N`** 的聚合条;发送块里的坐标行前面还带一段人可读的 **`引自第 N 轮 · 助手`**,模型不用解码就知道这段话从哪来。
6
+
7
+ ## 界面
8
+
9
+ | 划词浮层 | 评论:引用与评论各一格,都能改 |
10
+ | --- | --- |
11
+ | ![选中文字后浮出「评论 / 添加到对话 / 侧边对话」,位置贴着选区](docs/screenshots/bar.png) | ![评论框:引用正文与评论分开可编辑,可勾选「让模型逐条回应」](docs/screenshots/comment.png) |
12
+
13
+ | 发送后的胶囊 + 悬停卡片 | 右侧栏的「引用清单」页 |
14
+ | --- | --- |
15
+ | ![悬停胶囊:引用、评论、跳回原文、改这条引用、复制](docs/screenshots/card.png) | ![引用清单:搜索本会话全部引用、跳回原文、导出 Markdown,底部是侧边线程回流](docs/screenshots/panel.png) |
6
16
 
7
17
  ## 功能
8
18
 
@@ -12,8 +22,14 @@
12
22
  | **添加到对话** | 直接把引用加进输入框,不弹评论框。 |
13
23
  | **侧边对话** | 把引用写进一条**独立的侧边对话**(dsh-better-sidebar 的侧边线程)的输入框:线程继承主会话完整上下文、独立运行、不污染主会话,页签自动在右侧栏打开,**什么都不发送**——你写好自己的问题再发。同一条主会话只开一条线程,之后每次点都在同一条线程的草稿后面接着追加。鲸鱼蓝(`#4d6bfe`)的图标是它在浮层里的标识。 |
14
24
  | **键盘直达** | `Alt+Q` 引用 / `Alt+C` 评论 / `Alt+B` 侧边对话——同一个浮层的三个动作,不弹浮层直接办。走 DSH 的快捷键服务注册,所以出现在设置里、可改键、与宿主键位冲突会在注册时报错;没有选区时返回 `pass`,按键不会被吞掉。 |
15
- | **跳回原文** | 悬停卡片底部的 `跳回原文`:把引用当初所在的那条消息**停到视野上方 24px**(和宿主自己跳轮次一样的位置)并闪一下。坐标(消息行锚点 + 轮次)在插入时记下、按引用原文归一化成键存进 `localStorage`,因此跨刷新仍在。只在**引用自己那条会话的面板里**找,不跨会话误跳;被虚拟列表藏起来的行会让位给同轮次的可见行;压根没挂载的行靠滚动搜索一屏一屏地找出来——先往上找(引用总在比它更早的消息里),一路到顶还没找到就从最新处再来一遍;都找不到就明说(`原文不在当前视图里`)。 |
16
- | **引用清单** | 右侧栏的一个**页面**:本会话发出去的引用与评论的清单。搜索框按引用/评论过滤、点一条跳回原文、`导出 Markdown` 落一个文件、历史没读完时可 `加载更早的消息`。它作为**页面类型**注册进右侧栏(`ctx.sidebarRightTabs.register`),并在指南页留下一个胶囊作入口;`Alt+L` 同样能打开。数据来自会话自己的事件日志(`ctx.sessions.retain` → `session.eventSource`),不依赖屏幕上有没有那条消息。 |
25
+ | **原文侧高亮** | 引用落进草稿或发出后,被引用的原句回到**来源消息**里留下淡黄高亮与**序号角标**(序号就是坐标里的同句序号,和 `「引用N」` 对得上)。滚动、流式输出、虚拟列表重新挂载都会重画;行不在视口里、文字已经变了、宿主不给 `Range` 时就静默跳过,**不改宿主的 DOM**(高亮是浮层,不是包一层 `mark`)。 |
26
+ | **聚合引用条** | 输入框上方一条 **`引用 ×N`**:每颗芯片一个摘要胶囊,点一下跳回原文(跳不到就把光标放到芯片上,一次退格删掉),悬停就是完整卡片。主会话与侧边线程各自的输入框各有一条;芯片删光时条自己消失。 |
27
+ | **来源头** | 发送块尾部的坐标行前面多一段人可读的出处:**`※ 引自第 12 轮 · 助手 │q1:…`**,模型不解码也知道来自哪一轮、谁说的;角色属性宿主没给时就只写轮次。胶囊悬停卡片里同样显示 `引自第 12 轮 · 助手`,按界面语言本地化。 |
28
+ | **Diff 划选** | 在 diff / 每轮改动面板里划选也走同一个浮层:引用按**代码块**发,围栏信息位带探测到的文件路径与行号(`src/app.ts#L12`);路径、行号是宿主 DOM 上的探测,拿不到就只带代码,不影响引用本身。 |
29
+ | **清单多选插入** | 引用清单每行一个勾选框,`插入所选` 一次把多条引用写进输入框——每条各成一颗芯片、各自带坐标,不合并;芯片通路不可用时退回一段包含多个引用块的文本。这一栏是只读导航 + 批量插入,不做就地删除(宿主没有删除 chip 的 API)。 |
30
+ | **审阅工作台** | 清单页把引用当**对象**管:每行一个 **`待处理 / 已解决`** 状态钮(点一下切换,行整体随之变淡),页首的 `只看未解决 (N)` 一键筛出没处理完的;每行可以打 **`#标签`**(回车添加、点 × 移除、点标签按它筛选,页首列出全部标签及数量);勾选若干行后 `标记已解决 (N)` 批量落章,`插入所选` 批量送回输入框。状态与标签按引用坐标归一键(老引用退回按原文),记在本机 `localStorage`(`dsh-client-ui-quote:v1:quote-meta`,上限 800 条)——跨刷新仍在,不跨设备同步、不进模型上下文;`导出 Markdown` 的标题行会带上 `已解决` 与标签标记,没标过的行输出和从前逐字相同。 |
31
+ | **跳回原文** | 悬停卡片底部的 `跳回原文`:把引用当初所在的那条消息**停到视野上方 24px**(和宿主自己跳轮次一样的位置)并闪一下。坐标(消息行锚点 + 轮次 + 同句序号)在插入时**写进消息自己的文本**——一段只在线上存在的 `※ 引自第 N 轮 · 助手 │q1:…` 行(base64url 的 JSON),显示、胶囊、清单导出里都剥掉;它随消息本身走,所以跨刷新、跨窗口、以及清单页里根本没被本插件看着选中的引用都跳得回去。只在**引用自己那条会话的面板里**找,不跨会话误跳;被虚拟列表藏起来的行会让位给同轮次的可见行;压根没挂载的行靠滚动搜索一屏一屏地找出来——先往上找(引用总在比它更早的消息里),一路到顶还没找到就从最新处再来一遍;都找不到就明说(`原文已变`——消息还在、那段文字不在了;或 `原文不在当前视图里`)。 |
32
+ | **引用清单** | 右侧栏的一个**页面**:本会话发出去的引用与评论的清单。搜索框按引用/评论过滤、点一条跳回原文、`导出 Markdown` 落一个文件、历史没读完时可 `加载更早的消息`。它作为**页面类型**注册进右侧栏(`ctx.sidebarRightTabs.register`),并在指南页留下一个胶囊作入口;`Alt+L` 同样能打开。数据来自会话自己的事件日志(`ctx.sessions.retain` → `session.eventSource`),不依赖屏幕上有没有那条消息;每行带着**自己那份坐标**(从消息文本的 `※ … │q1:…` 行读出来),所以没被本插件看着选过的引用也能跳。 |
17
33
  | **侧边线程回流** | 同一个页面底部是这条会话的**侧边线程**:显示它最后问的那个问题与消息条数,并给两个动作——**`带回主会话`** 把线程最后「问题 + 答案」写成主会话输入框里的草稿(**不发送**,你自己按发送),**`导出 Markdown`** 把整条线程落成一个文件。线程内容同样从它自己的事件日志读;没有线程时这一节只说明「还没有侧边线程」。 |
18
34
  | **改这条引用** | 悬停卡片里的 `改这条引用`:把那条引用当初的**引用与评论都原样放回评论框**(引用正文、评论各占一个输入框,都能改),你改完按 `⏎` 就发出一条**新的**引用(DSH 没有编辑已发送消息的 API,这是唯一诚实的做法)。旧胶囊随即挂上 **`已修订`** 标记,卡片里直接显示改后的评论;**连正文一起改过的**,卡片里也把改后的原文亮出来。标记按引用原文记在本机,跨刷新仍在。 |
19
35
  | **逐条回应** | 评论框里的一格**勾选项**(默认关):勾上,这条引用的块尾就多一行**只有模型读得到的指令**——请按出现顺序把引用记为「引用1」「引用2」…并在回复中逐条对应。模型的回复里每个 `「引用N」` 会变成一枚**小徽章**:悬停就是那条引用的完整卡片(含 `跳回原文` 与 `改这条引用`),点了就跳;编号超出这条消息引用数的标签保持纯文本。指令行与围栏一样只是**线上形态**,胶囊、卡片、导出里都不出现。 |
@@ -31,7 +47,7 @@
31
47
 
32
48
  ### 方式一:npm 包(推荐)
33
49
 
34
- 已发布到 npm:`dsh-client-ui-quote`(0.10.0 起上架;稳定版当前 **0.11.3**)。
50
+ 已发布到 npm:`dsh-client-ui-quote`(0.10.0 起上架;稳定版当前 **0.12.0**)。
35
51
 
36
52
  ```bash
37
53
  dsh plugin --profile desktop add dsh-client-ui-quote
@@ -45,7 +61,7 @@ npmmirror 与 npmjs 一般 10 分钟内同步,DSH 配置了镜像源即可国
45
61
 
46
62
  1. 把包装进 profile:`cd <DSH_HOME>/profiles/<profile> && pnpm add dsh-client-ui-quote`;
47
63
  2. 编辑 `<DSH_HOME>/profiles/<profile>/package.json`:
48
- - `dependencies` 里应有 `"dsh-client-ui-quote": "^0.11.3"`(安装器会写);
64
+ - `dependencies` 里应有 `"dsh-client-ui-quote": "^0.12.0"`(安装器会写);
49
65
  - `dsh.profile.bundles` 追加 `"dsh-client-ui-quote"`;
50
66
  3. 重启 DSH(profile 打开 `patchReload` 时也会自动重载)。
51
67
 
@@ -70,13 +86,15 @@ link 的优点是改完刷新即生效;**日常运行请用方式一**,不
70
86
 
71
87
  - **`link`——日常开发(最快)**:profile 里依赖写成 `link:C:/…/dsh-client-ui-quote`,窗口直接加载仓库里的文件。改完 `Ctrl+Shift+R` 硬刷新就生效,不必发版。
72
88
  - **`lab`——云端预发布线**:`lab` 分支每 push 一次,CI 就把 `下一个补丁版本-lab.<run_number>`(例如 `0.11.4-lab.37`)发到 npm 的 **`lab`** dist-tag,不碰 `latest`。装它:`pnpm install dsh-client-ui-quote@lab`——用来验证「打包后的产物」,而不是工作区文件。
73
- - **`stable`——用户装到的版本**:`main` 只接受从 `lab` 合并过来的提交;发版 = 合并到 `main` 后打 `vX.Y.Z` 标签,CI 发到 npm 的 `latest`。
89
+ - **`stable`——用户装到的版本**:`main` 只接受从 `lab` 快进的提交(`git merge --ff-only lab`,不在 main 上直接提交);发版 = 快进到 `main` 后打 `vX.Y.Z` 标签,CI 发到 npm 的 `latest`。
74
90
 
75
91
  本机切换这三档(这台开发机专用的辅助脚本,不在仓库里):`node .dsh-quote-work/profile-mode.mjs link|lab|stable|status`。换完在窗口里 `Ctrl+Shift+R` 生效;只有宿主半有改动时才需要重启桌面端。
76
92
 
77
- 两条线的断言入口相同:`npm test`(306 项),CI 里 `prepublishOnly` 会先跑完再发。
93
+ 两条线的断言入口相同:`npm test`(454 项:check 12 + capsule 298 + panel 144);`.github/workflows/test.yml` 在每次 push / PR 上用 Node 18/20/22 各跑一遍,`publish.yml` 里 `prepublishOnly` 再跑一次才发。
94
+
95
+ **推送通道**:`git push` 优先,但本机 `github.com:443` **时通时不通**(2026-10-19 实测:短时可用、随后连续超时,每次失败等约 21 秒)。日常用工作区根的 `promote.ps1`:先试 git,失败自动转 REST 兜底(`.dsh-quote-work/publish-release.mjs`、`.dsh-quote-work/push-branch.mjs`,`-Channel rest` 可跳过探测),并核对远端 SHA。**别混用两条通道**:REST 重建的提交时区归一到 `+0000`,内容相同但 SHA 不同,同一分支混推会分叉(`git status` 看着干净,远端却多一条平行提交);恢复就先 `git fetch`、确认 `git diff <本地> <远端>` 为空,再把本地线 reset 到远端。
78
96
 
79
- 本机额外限制(只影响这台开发机):`git push` 到 `github.com:443` 不通,推分支/发版走 GitHub REST API(`.dsh-quote-work/publish-release.mjs`、`push-branch.mjs`),本地对象会按 API 的字节重建,`git status` 保持干净。
97
+ 两条线的完整流程(含「main 只做快进」、推 lab 与 main 的顺序、分叉事故的复盘)见工作区根目录的 `PLUGIN-WORKFLOW.md`,一键执行用 `pwsh -File promote.ps1 -Repo dsh-client-ui-quote`。
80
98
 
81
99
  **发布运维备忘**(给未来的自己):
82
100
 
@@ -153,7 +171,7 @@ DSH 的输入框是 Lexical,没有 Kimi 的 `quote` 节点,但有一个通
153
171
  (`[data-textpreview-path]`,完整路径在它的 `title` 里——标签在头部,与选区所在的 body 是兄弟分支),再退回「宿主或其祖先上的路径属性」;
154
172
  行号取自 `data-textpreview-line` 标记的行元素(`withLineRange` 拼成 GitHub 的 `#L` 写法,起止顺序自动归一)。两种都没命中就原样发;
155
173
  - **逐条回应的指令行**同样只是线上形态:勾了评论框里的开关,块尾多一行 `> ※ 指令:…`,`splitQuoteRuns` 按 `※ 指令` 前缀把它从块里剥掉,
156
- 所以胶囊、卡片、清单导出全都看不到它(用户自己的评论/引用恰好以 `※ 指令` 开头的概率,比它带来的清晰收益小得多,已记入已知限制);
174
+ 所以胶囊、卡片、清单导出全都看不到它;正文里恰好以 `※ 指令` 开头的行会被转义保护,回读时还原(见已知限制)。
157
175
  - 客户端插件用 `MutationObserver` 找「只含一个文本子节点、内部有 `❝`、位于 `[data-conversation-content]` 里」的那个 run,
158
176
  把整条消息拆成 `前文 / 引用 / 中间文字 / 引用 / 后文` 这样一串分段(`splitQuoteRuns`),给原 run 加 `display:none`,
159
177
  在它**前面**按顺序插入自己的元素:每段引用一颗胶囊,每段用户自己的话一个 `span.dshq-body`(保持 `pre-wrap`)——
@@ -220,29 +238,30 @@ transcript 侧的 `.dshq-cap` 胶囊:一颗胶囊同时装引用与评论,`
220
238
 
221
239
  ## 与同类插件的差异
222
240
 
223
- 判断依据是市场全量目录(`https://awesome-dsh-plugin.com/plugins.json`,2026-10-10 快照:**4471 条**,字段含 stars / downloads / capabilities),不是印象。**划选/引用/批注这一带一共只有 85 条**——小赛道,但很挤:「划选浮层 + 加进输入框」这个形态本身已经饱和(下面几个邻居都做得到)。
241
+ 判断依据是市场全量目录(`https://awesome-dsh-plugin.com/plugins.json`,2026-10-18 快照:**5945 条**,字段含 stars / downloads / capabilities),不是印象。按「同时讲选中与引用/批注」的严格口径是 **41 条**,广义口径 **209 条**——小赛道,但很挤:「划选浮层 + 加进输入框」这个形态本身已经饱和(下面几个邻居都做得到)。
224
242
 
225
243
  | 同类 | 它做什么 | 本插件多出来的部分 |
226
244
  | --- | --- | --- |
245
+ | `omdsh-dev/dsh-annotation`(148★) | 选中只读回复或侧栏文件 → 编号高亮 → 批注 → 随消息发送;回复按 `Annotation N` 逐条对照 | 它把批注块藏在气泡里、输入框旁只留 `Annotations ×N`;本插件是**引用与评论同一颗胶囊**,并且还有清单页、侧边线程、追加式修订、终端/预览引用 |
227
246
  | `tr1v3r/dsh-quote-followup`、`sumomok/quote-message` | 选区 → **原生 reference chip** 进输入框 | 芯片机制不是差异点(它们也用同一个 API)。差异在芯片**之后** |
228
247
  | `sunnystarye-ui/dsh-plugin-text-quote`、`choco9527/dsh-add-to-chat` | 选中 → 带注释的引用 → 进输入框;发送后可看注释 | 引用与评论**同一颗胶囊**、悬停卡片、`跳回原文`、清单页、线程回流 |
229
248
  | `latte03/dsh-select-quote` | 原文旁虚线高亮 + 序号角标 + 摘要可查看/跳回 | 它把批注挂在下一条消息上;本插件是**胶囊模型**,且坐标按引用原文归一化后跨刷新仍在 |
230
- | `dsh-sidenote`(11★)、`dsh-side-chat`、`side-chat-plus-plus`、`dsh-side-branch` 等 16 条侧聊 | 划选 → 在侧边线程里**提问/作答** | **只把引用写进线程输入框、不替你发送**:16 条里没有第二条同样语义;线程被记住并复用,`Alt+L` 那一页还能把线程结论带回主会话 |
249
+ | `dsh-sidenote`、`dsh-side-chat`、`side-chat-plus-plus`、`dsh-side-branch`、`CiteCiter` 等 22 条侧聊/主题 | 划选 → 在侧边线程或持久 Topic 里**提问/作答** | **只把引用写进线程输入框、不替你发送**:侧聊里仍少有同样语义;线程被记住并复用,`Alt+L` 那一页还能把线程结论带回主会话 |
231
250
 
232
- 全库计数里几个**零**(同一份快照的关键词探针):
251
+ 同一份快照里几个仍然很窄的格子:
233
252
 
234
- - **「引用 + 评论合成单一可交互胶囊」:0 条** —— 本插件的 `❝ 引用摘要 │ 评论摘要` 仍是空位。
235
- - **「编辑已发送的批注」:0 条** —— 所有人都是「写→发→再看」。DSH 目前**没有**任何编辑已发送消息的 API(侦察确认:`editMessage`/`updateMessage`/`rewriteMessage` 全树 0 命中,只有尚未进入会话的队列条目可改,见「已知限制」),所以这一格本插件也没填,留待宿主开口。
236
- - **消息内联操作条**仅 2 条、**消息内评论**仅 2 条:发送后还在消息里「就地操作引用」的,几乎没有。
253
+ - **「引用 + 评论合成单一可交互胶囊」**:关键词探针里仍是本插件独有;但「选中 → 批注 → 逐条回应」这个价值已有 `dsh-annotation`(148★)在做,差异在本插件的**胶囊 + 清单 + 线程**组合,而不是单点机制。
254
+ - **「原地编辑已发送的批注」**:DSH 仍没有编辑已发送消息的 API(侦察确认 `editMessage`/`updateMessage`/`rewriteMessage` 全树 0 命中),本插件走**追加式修订**;`dsh-plugin-message-edit` 走的是另一条路(原地保存 + 分支重生成 + 版本时间线),两条路线不冲突。
255
+ - **发送后仍能在消息里就地操作引用**的仍是少数:本插件用胶囊 + 悬停卡片做,不做消息级工具条。
237
256
 
238
257
  本插件相对同类的**可验证**优势集中在四件事上,都有离线断言兜底:
239
258
 
240
259
  1. **语义而不是机制**:引用与评论是同一颗节点(对齐 Kimi 的 `quote` 节点 `{text, comment}`),一颗芯片 → 一个块 → 一颗胶囊 → 一张卡片 → 一次复制;机制(reference chip、DOM 增强)谁都能抄,这条链子不好抄。
241
260
  2. **引用是带坐标的活对象**:`跳回原文` 在自己的会话面板里找行、按宿主的方式对齐到 24px,行被虚拟列表藏起来或没挂载都能找回来;坐标不随引用文本发给模型(不污染 prompt)。
242
- 3. **引用有下落**:清单页(搜索 / 跳转 / 导出 Markdown)+ 侧边线程回流(结论写回主会话草稿 / 导出线程),这两件事在 4471 条里没有对应物。
243
- 4. **工程化**:306 项离线断言(含真跑 `sessions.retain` / `setDraft` / 下载链路)、每个宿主契约都在 README 里写明出处、每个版本都有可回读的 release。同类里多数是一两百行、没有测试的插件。
261
+ 3. **引用有下落**:清单页(搜索 / 跳转 / 导出 Markdown)+ 侧边线程回流(结论写回主会话草稿 / 导出线程),这两件事在同类里仍是少数。
262
+ 4. **工程化**:454 项离线断言(含真跑 `sessions.retain` / `setDraft` / 下载链路)、每个宿主契约都在 README 里写明出处、每个版本都有可回读的 release。同类里做到同等断言规模的仍是少数。
244
263
 
245
- 反过来也要说清楚:**本插件不做的**——不做翻译/解释类选区动作(81 条同类在做)、不做多选合并引用(7 条有)、不做选区折叠/行内 diff 这类渲染花样。它只把「引用」这一件事做穿。
264
+ 反过来也要说清楚:**本插件不做的**——不做翻译/解释类选区动作(几十条同类在做)、不做多选合并引用(有同类在做)、不做选区折叠/行内 diff 这类渲染花样。它只把「引用」这一件事做穿。
246
265
 
247
266
  ## 声明(package.json)
248
267
 
@@ -268,13 +287,14 @@ package.json DSH 插件清单(bundle patch + client 半 + icon + loca
268
287
  icon.svg 插件页卡片图标(相对路径、≤ 256 KiB 的 SVG)
269
288
  cordis.patch.yml 组合包层补丁:插入 ui-quote 这个 loader 条目
270
289
  lib/index.js host 半:只声明 apply(),不提供服务
271
- lib/client.js 全部功能:vendor-CJS 工厂 + 浮层(三项动作)+ 输入框芯片 + 发送后胶囊 + 侧边对话接线 + 引用清单页
290
+ lib/client.js 全部功能:vendor-CJS 工厂 + 浮层(三项动作)+ 输入框芯片 + 发送后胶囊 + 原文高亮/聚合条 + 侧边对话接线 + 引用清单页(含多选插入)
291
+ .github/workflows/test.yml push / PR 的 Node 18/20/22 测试门
272
292
  locale/zh.json 插件页卡片文案(中文)
273
293
  locale/en.json 插件页卡片文案(英文)
274
294
  test/check.mjs 离线自检(清单 + 组件契约)
275
295
  test/dom.mjs 给离线自检用的最小 DOM / MutationObserver 垫片
276
- test/capsule.mjs 离线自检(发送后胶囊 + 悬停卡片 + 跳回原文 + 键位 + 改这条引用 + 代码引用 + 文件路径 + 逐条回应的回复徽章 + 浮层不随座位失效,210 项断言)
277
- test/panel.mjs 离线自检(引用清单:解析 / 搜索 / Markdown / 读取 / 导出 / 页面注册 + 侧边线程回流,96 项断言)
296
+ test/capsule.mjs 离线自检(发送后胶囊 + 悬停卡片 + 跳回原文 + 键位 + 改这条引用 + 代码引用 + 文件路径 + 逐条回应的回复徽章 + 浮层不随座位失效,298 项断言)
297
+ test/panel.mjs 离线自检(引用清单:解析 / 搜索 / Markdown / 读取 / 导出 / 页面注册 + 侧边线程回流 + 审阅工作台(状态 / 标签 / 批量),144 项断言)
278
298
  test/capsule.html 真浏览器里跑同一套胶囊断言(可选)
279
299
  ```
280
300
 
@@ -324,10 +344,10 @@ React 重渲染不会出现第二颗、引用变了就地重建、消息被删
324
344
  修订不改写引用原文本身、清空引用正文时确认按钮禁用、改后的正文随新芯片发出;
325
345
  **浮层不跟着座位生死**(`conversation.input.overlay` 没挂载也能选中出栏、挂上两个会话座位既不叠加也不丢监听、
326
346
  座位不在时用输入框自己的 `actions` 落芯片、无处可插时给 toast 而不是静默返回、effect 卸载/重装后监听恰好一份);
327
- (共 210 项,含文件路径与逐条回应的线上形态、回复徽章的建立/重建/卸载与悬停卡片复用,以及评论框两个输入区的自适应高度——引用正文那格曾经在被挂进栏之前就量高,读到 0 后被写成 `height:0`,首行因此被裁掉)。
347
+ (共 298 项,含文件路径与逐条回应的线上形态、回复徽章的建立/重建/卸载与悬停卡片复用,以及评论框两个输入区的自适应高度——引用正文那格曾经在被挂进栏之前就量高,读到 0 后被写成 `height:0`,首行因此被裁掉)。
328
348
  `test/capsule.html` 是同一套断言的真浏览器版,用 `file://` 打开即可(页面会把结果写进标题与 `#probe-out`)。
329
349
 
330
- `test/panel.mjs` 单独跑引用清单页与侧边线程回流(96 项):只认**持久**的 `user/message`(`transient`、助手消息、`data: null` 都跳过)、
350
+ `test/panel.mjs` 单独跑引用清单页与侧边线程回流(144 项):只认**持久**的 `user/message`(`transient`、助手消息、`data: null` 都跳过)、
331
351
  引用按原文逐行拆出来而评论跟着同一条记录、最新一条排最前、空查询保持全量、引用与评论都能被搜到且忽略大小写、
332
352
  Markdown 的标题/编号/块引用/评论行/时间戳与空列表兜底、超长引用在标题里被截断、非法时间导出为空;
333
353
  读取侧用桩 `sessions.retain` 真跑一遍——读出两行且 `hasMore` 透传、**读完必须 release**(记录 `source: "quoteList"`)、
@@ -343,7 +363,7 @@ Markdown 的标题/编号/块引用/评论行/时间戳与空列表兜底、超
343
363
  `bringThreadBack` 把结论写成**草稿**(原有草稿保留、空行隔开)、外壳没有 `setDraft` 时如实失败、没有线程时不乱写;
344
364
  `exportThread` 的文件名与整条线程内容、以及 MIME 是 Markdown。
345
365
 
346
- 合计 **306 项**(check.mjs 的契约断言 + capsule 210 + panel 96),`npm test` 全绿。
366
+ 合计 **454 组**(check 12 + capsule 298 + panel 144),`npm test` 全绿。
347
367
 
348
368
  ## 已知限制
349
369
 
@@ -353,23 +373,27 @@ Markdown 的标题/编号/块引用/评论行/时间戳与空列表兜底、超
353
373
  未安装、被禁用、或它的「侧边对话」页签在侧边卡片设置里被关掉时,浮层第三项**整个不渲染**;
354
374
  两个接口升级后若失效都不会静默:开不了线程 → toast「侧边对话没有打开,请重试。」,写得进不去草稿 → toast「侧边对话已打开,但引用没能写进它的输入框」;
355
375
  线程创建要求父会话处于运行态(历史归档会话会收到 409,同样落 toast)。
356
- - **「改这条引用」是追加,不是原地编辑**:DSH 的 transcript 只追加(侦察确认全树没有 `editMessage` / `updateMessage` / `rewriteMessage` / 回收 API,只有**尚未进入会话**的队列条目可改)。所以修订发出一条新引用(评论、引用正文都可改),旧胶囊只挂 `已修订` 标记——标记存在**本机** `localStorage`(`dsh-client-ui-quote:v1:quote-revised`,按引用原文归一键、上限 60 条),因此不会跨标签页、跨设备同步,也不进入模型上下文;模型看到的是那条改后的新引用。正文改过后键仍是旧原文:`已修订` 挂在**旧**胶囊上(它的卡片里显示改后的原文与评论),改后的新胶囊不挂标。
376
+ - **「改这条引用」是追加,不是原地编辑**:DSH 的 transcript 只追加(侦察确认全树没有 `editMessage` / `updateMessage` / `rewriteMessage` / 回收 API,只有**尚未进入会话**的队列条目可改)。所以修订发出一条新引用(评论、引用正文都可改),旧胶囊只挂 `已修订` 标记——标记存在**本机** `localStorage`(`dsh-client-ui-quote:v1:quote-revised`,按 **`ref + 引用原文`** 归一键、上限 60 条),同一句话从两个不同位置引用时互不影响;没有 ref 的旧引用仍按原文键读写。它不会跨标签页、跨设备同步,也不进入模型上下文;模型看到的是那条改后的新引用。正文改过后键仍是旧原文:`已修订` 挂在**旧**胶囊上(它的卡片里显示改后的原文与评论),改后的新胶囊不挂标。
357
377
  - **代码引用的识别靠首行围栏**:`> ❝ ``` ` 开头的块按代码解析(围栏剥掉、胶囊等宽)。因此一条**普通**引用如果正文恰好以 ``` 或 `~~~` 开头(比如在 Markdown 文件预览里引用了源码),回读时也会被当成代码引用——显示上是等宽排版,文字不丢。
358
- - **逐条回应的指令行按 `※ 指令` 前缀剥离**:剥离规则收得很窄,但一条引用或评论如果恰好以 `※ 指令` 开头,回读时那一行会被当成指令剥掉(胶囊里看不到它)。要让这样的内容活着,别勾逐条回应即可——指令行只在勾选时才写入。
378
+ - **标记形状的正文会被转义**:一行以 `❝` / `❞` / `※` 或反斜杠开头时,写入线格式前加一个反斜杠、回读时还原。因此引用一段本身含 `❞` 或 `※ │q1:` 形状的文档,不会再被当成评论分隔符或坐标行截断/丢行;旧版本发出的消息没有这层转义,仍按旧规则解析。
359
379
  - **文件预览的路径与行号是探测来的**:路径优先读宿主预览头部的路径标签(`[data-textpreview-path]` 的 `title`,实测 0.2.0-rc.2 的侧栏文档预览就是这个形状),其次探一组候选属性;行号只在预览按行渲染时可得(`data-textpreview-line`,纯文本视图有、高亮代码视图没有)。两者都拿不到就不带(引用形态与从前完全一样);终端来源的引用没有路径可言。路径与行号写进围栏信息位,`sanitizePath` 把带空白或围栏字符的值挡掉。
380
+ - **Diff 引用的路径与行号同样是探测来的**:先在 diff 卡片里找 `data-diff-path` / `data-file-path` / `data-diff-file` 等头标签(优先读 `title`),再退回祖先属性;行号读 `data-line-number` / `data-diff-line` / `data-line`。宿主换一套 diff DOM 时只会丢路径/行号,引用本身仍按代码块发。
381
+ - **多选插入按清单当前顺序写**:每条引用各成一颗芯片;宿主编辑器拒绝 chip 写入时整批退回一段文本块,不会重复插入已成功的条目。搜索框一变就清空勾选,避免“看不见的选中项”被插进去。
360
382
  - **回复徽章依赖模型守格式**:模型没按指令输出 `「引用N」` 就没有徽章——无损,只是少了联动;输出越界的编号(数错了)保持纯文本。徽章的编号以回复**之前最近的一条带胶囊的消息**为准;切分的是助手消息的文本节点,DSH 若改了助手消息的渲染形态,徽章增强器要跟着调(与胶囊的 `isQuoteRun` 同一条既有限制)。
361
383
  - 依赖 DSH 客户端的内部接口(`conversation.input.overlay` 槽位、`inputTriggers.registerSource`、`conversation.input.shell(id).insertReference`、
362
384
  `reference-chip` 节点),DSH 升级有可能失效。
363
385
  - **引用清单页依赖右侧栏的两个服务**(`sidebarRightTabs` 与 `sidebarRight`,实测 0.2.0-rc.2);宿主没有它们时页面不注册、`Alt+L` 给一条 toast
364
386
  (「引用清单打不开:右侧栏服务不可用」)而不是报错。它的数据来自会话事件日志,**历史是分页的**:只列已加载的消息,更早的要按 `加载更早的消息`;
365
387
  如果会话做过原生上下文压缩/裁剪,被裁掉的那些消息里的引用也不会出现在清单里。导出是浏览器下载(宿主没有给客户端插件写盘接口),
366
- 文件名形如 `quotes-<sessionId>.md`;只有被本插件看着选中过的引用那一行可点击跳转,其余行只是可读文本。
388
+ 文件名形如 `quotes-<sessionId>.md`;一行能不能跳看它自己带不带坐标——本版本发出的引用都带,更早发出的老引用没有坐标,那行只能当文本读。
367
389
  - 仅 web / 桌面 profile;选区必须落在会话正文里。
368
390
  - 引用在输入框里显示为胶囊,编辑器中复制粘贴出去的是它的模型形态文本。
369
391
  - transcript 胶囊是在 DSH 渲染结果之上做的覆盖层:它只隐藏原节点、不搬移 React 的节点,但若 DSH 改了消息的 DOM 结构
370
392
  (`[data-conversation-content]`、纯文本 run 的形态),需要跟着改 `isQuoteRun` / `capsuleFor`。
371
393
  - 收起状态的胶囊是**单行**:很长或分段的引用与评论会被省略号截断 —— 鼠标悬停会浮出完整内容,也可以点一下直接在原地展开。
372
394
  - 更新**之前**发出的老消息不会被变成胶囊:它们的文本里没有 `❝` 标记,插件无从辨认(重新引用一次即可)。
395
+ - **原文高亮用完整引用文本定位**:页面刚刷新、那颗胶囊还没渲染时,插件手里只有坐标表里的前 120 字身份键,长引用会先亮前 120 字;胶囊进入视口后补上全文并自动重画。行不在视口、被虚拟列表收起、或文字已经变了就不画,绝不改宿主 DOM。
396
+ - **聚合引用条是只读导航**:它按芯片计数、按芯片标签解析摘要,点一条跳回原文;跳不到时把光标放到芯片上(一次退格可删)。宿主没有给客户端插件「删除某个 reference-chip」的 API,所以条上没有单独的删除按钮。
373
397
 
374
398
  ## License
375
399
 
@@ -393,7 +417,8 @@ transcript (`quote │ comment`, hover for the full text, click to expand, copy
393
417
  the model still receives the full text. The same hover card opens over the composer chip, whose label has to be clipped,
394
418
  and it closes with the chip as soon as the message is sent. Each chip resolves its own quote at hover time — React
395
419
  reuses a chip node for the next quote, so a card can never show the previous one's text.
396
- Install with `dsh plugin --profile desktop add link:<clone-path>` (DSH >= 0.2.0-rc.2, web/desktop client).
420
+ Screenshots: [floating bar](docs/screenshots/bar.png) · [comment box](docs/screenshots/comment.png) · [capsule hover card](docs/screenshots/card.png) · [quote list page](docs/screenshots/panel.png).
421
+ Install with `dsh plugin --profile desktop add dsh-client-ui-quote` (published on npm; DSH >= 0.2.0-rc.2, web/desktop client).
397
422
  Test with `npm test`. MIT licensed.
398
423
 
399
424
  Two wire-only extras ride on the same pipeline: a per-quote reply toggle in the comment box (the block carries a
Binary file
Binary file
Binary file
Binary file