@yfwu2020/dsh-selection-explain 0.9.1 → 0.10.1
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 +93 -547
- package/assets/detail.png +0 -0
- package/assets/quotes.png +0 -0
- package/assets/translation.png +0 -0
- package/assets/voice-card-light.png +0 -0
- package/assets/voice-settings.png +0 -0
- package/assets/voice.png +0 -0
- package/lib/client.js +1914 -122
- package/lib/index.js +612 -188
- package/lib/index.js.map +1 -1
- package/lib/settings.js +3 -0
- package/lib/settings.js.map +1 -1
- package/lib/types/index.d.ts +90 -14
- package/native/mic-probe.c +160 -0
- package/package.json +5 -2
- package/scripts/build-vcard-lab.mjs +834 -0
- package/scripts/build.sh +17 -0
- package/scripts/fixtures/fake-mic-probe.sh +9 -0
- package/scripts/smoke-panel.mjs +279 -11
- package/scripts/test-client.mjs +1402 -54
- package/scripts/test-effort-map.mjs +129 -0
- package/scripts/test-effort-retry.mjs +109 -0
- package/scripts/test-filters.mjs +5 -88
- package/scripts/test-mic-idle.mjs +158 -0
- package/src/client/index.js +1914 -122
- package/src/index.ts +674 -179
- package/src/settings.ts +3 -0
package/README.md
CHANGED
|
@@ -1,637 +1,183 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# 划词解读
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
[](https://www.npmjs.com/package/@yfwu2020/dsh-selection-explain)
|
|
7
|
-
[](./package.json)
|
|
8
|
-
[](https://github.com/yfwu2020/dsh-selection-explain)
|
|
5
|
+
**划一下,说一句。把不懂的地方,就地问明白。**
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
## 它解决什么问题
|
|
13
|
-
|
|
14
|
-
读英文文档、看代码注释、翻聊天记录时,卡住的往往不是"这个词什么意思",而是**"它在这里什么意思"**:
|
|
15
|
-
|
|
16
|
-
- `commit` 是"提交"还是"承诺"?`stale closure` 在这个 bug 报告里指哪种闭包?
|
|
17
|
-
- 同事写的 "let's circle back on the migration" 到底在安排什么?
|
|
18
|
-
- 这段报错里的 `EADDRINUSE`,在当前这个项目里是端口冲突还是残留进程?
|
|
19
|
-
|
|
20
|
-
普通翻译工具只给词义,**不给语境**。这个插件把**选中文字 + 它周围的内容 + 当前会话的对话背景**一起喂给模型,所以答案能落到"这里"——并且它知道你是在读文档、读代码,还是在跟人对话。
|
|
21
|
-
|
|
22
|
-
## 功能
|
|
7
|
+
DSH 中的翻译、上下文解读与语音追问。
|
|
23
8
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
点开面板**先只出翻译**(上下文窗口收窄到 8 条消息,首字最快);翻译出来后才出现 **`↓ 展开详解`**,点了才用完整会话背景(24 条消息)做深度解读。想要快就停在第一段,想要透就展开——**不强迫你为深度等首字**。
|
|
27
|
-
|
|
28
|
-
<img src="assets/stage1.png" alt="首轮:只有翻译一节 + 展开详解按钮" width="540">
|
|
29
|
-
|
|
30
|
-
### 翻译:按选中内容的语言和形态自适应
|
|
31
|
-
|
|
32
|
-
不是无脑翻译,而是先判断这段文字是什么,只给对应的那一种:
|
|
33
|
-
|
|
34
|
-
| 选中内容 | 输出 |
|
|
35
|
-
| --- | --- |
|
|
36
|
-
| 中文句子(夹英文) | 中文部分**不翻译**,只把句中夹着的英文片段逐条译成中文 |
|
|
37
|
-
| 中文词 / 词组 | 列常用义项(每行一条) |
|
|
38
|
-
| 英文句子 / 词组 | 直接给中文译文 |
|
|
39
|
-
| 英文单词 | 逐个义项一行,**带音标** |
|
|
40
|
-
| 日 / 韩 / 法 / 德等其他语言 | 译成中文 |
|
|
9
|
+
[快速安装](#install) · [语音输入](#voice) · [功能一览](#read) · [使用与设置](#settings)
|
|
41
10
|
|
|
42
|
-
|
|
11
|
+
`DSH 插件` · `v0.10.1` · `MIT`
|
|
43
12
|
|
|
44
|
-
|
|
13
|
+
</div>
|
|
45
14
|
|
|
46
|
-
|
|
47
|
-
- **专业名词两层讲**:先给通用含义(本领域标准定义或全称),再说它在这段上下文里具体指什么。
|
|
48
|
-
- **讲透承接关系**:关键说法各指什么、承接了什么、能推出什么。
|
|
49
|
-
- **可以联网**:拿不准或是新出现的名词、缩写、产品名,会先搜索确认再解释,事实融进解释里,不罗列搜索过程。
|
|
50
|
-
|
|
51
|
-
### 选中代码 → 出注释
|
|
15
|
+
---
|
|
52
16
|
|
|
53
|
-
|
|
17
|
+
<a id="voice"></a>
|
|
54
18
|
|
|
55
|
-
|
|
19
|
+
## 01 / 划完,直接说
|
|
56
20
|
|
|
57
|
-
|
|
21
|
+
选中一句话,直接用语音问“它在这里是什么意思?”——问题留在选区旁,回答接着出现在小窗里。
|
|
58
22
|
|
|
59
|
-
|
|
23
|
+
### 从划词到提问,一口气完成
|
|
60
24
|
|
|
61
|
-
|
|
62
|
-
- 回答流式出现,多轮上下文保留。
|
|
63
|
-
- 追问可以**联网查证**,但工具日志不进小窗——只给你模型消化后的结论。
|
|
64
|
-
- 生成中可以点**停止**打断;没有可用结果时给「重新生成」。
|
|
65
|
-
- 关掉面板**不会丢**:点右下角胶囊原样回来,不重新请求。
|
|
25
|
+
**小窗还没打开**:划选文字后启动输入法语音,选区旁会出现临时卡片,承接你说的话。结束语音、文字定稿后,默认自动发送,直接进入追问。
|
|
66
26
|
|
|
67
|
-
|
|
27
|
+

|
|
68
28
|
|
|
69
|
-
|
|
29
|
+
*选区通过青色下划线和虚线连接到临时输入卡片。*
|
|
70
30
|
|
|
71
|
-
|
|
31
|
+
**小窗已经打开**:划选新的文字,再启动输入法语音。这段文字会作为引用加入小窗,你说的话写入追问框。
|
|
72
32
|
|
|
73
|
-
|
|
74
|
-
| --- | --- |
|
|
75
|
-
| 点一下 🎤 | 工具行换成**录音行**:`✕` 取消 · **实时波形**(跟着说话强弱) · `■` 停止 |
|
|
76
|
-
| **说话的时候** | **字就跟着往输入框里长**:半句预览(说了 1 秒多就出字,之后每 ~1.3 秒刷新)+ 停顿定稿(停 0.6 秒算这句说完,定稿的字不再变) |
|
|
77
|
-
| 点 `■` / 到了 60 秒 | 结束录音 → 整段再识别一次做收口(这段时间显示「识别中…」+ 呼吸点) |
|
|
78
|
-
| 点 `✕` / 录音中按 `Esc` | 取消这次语音:**松开麦克风**、实时字幕停下;**已经说出来的字留在输入框**(不删字)。`Esc` 只取消这一次语音,**面板留着**(再按一次才关窗) |
|
|
79
|
-
| 收起小窗 / 切走这个窗口 | 停录、**松开麦克风**(不会留下"正在使用麦克风"的标识),屏幕上已有的字留着 |
|
|
80
|
-
| 录音中把这条消息发出去了 | 停止录音(已经写进输入框的字跟着这条消息发出去) |
|
|
33
|
+
### 在小窗里,也能直接开口
|
|
81
34
|
|
|
82
|
-
|
|
35
|
+
点击输入框旁的麦克风开始录音,查看实时波形与转写;点击停止完成输入,或按 `Esc` 取消本次录音。实时转写不可用时,会在停止后识别。
|
|
83
36
|
|
|
84
|
-
|
|
85
|
-
- **框会跟着字长**:边说边落字时输入框自己往上撑(最多约 7 行,再多就内部滚动并停在最新一行)——字是"自己长出来"的,别指望你会去拖那个框。
|
|
86
|
-
- **实时字幕是"预览 + 定稿"两层**:说的过程中每 ~1.3 秒把没说完的半句送去识别一次,半句预览**整段替换**上一拍(窗口固定 ≤10 秒,所以说到第 40 秒也不会变慢);停 0.6 秒就算这句说完,送一次识别后**定稿**,之后不再变。点 `■` 结束时用整段录音再识别一次收口——**整段结果以已定稿的文字开头就只补后半句,定稿的字一个不动**(对不上才整块替换)。
|
|
87
|
-
- **实时字幕只在本地识别器上默认开**:每一拍都是一次识别调用,本机模型不花钱、只占点 CPU(实测 8 秒窗口约 0.2 秒);云端识别器按次计费,不做预览。拿不到麦克风原始采样(老浏览器)、或你手动改了预览那半句,就自动退回"停止后出字"。
|
|
88
|
-
- **音频不落盘、不进会话**:录到的音频只在这一次识别里用一下,host 转写完即丢——不会写进 `~/.dsh`,也不会出现在会话记录里。
|
|
89
|
-
- **识别在 host 上做**(装了本地 SenseVoice 就是**在你机器上**):音频不出机器;识别器走 DSH 的语音识别服务,本机模型 / 云端服务都能用。
|
|
90
|
-
- **出错留在录音行里说清是哪一种**:没授权 / 没麦克风设备 / 浏览器不支持 / 没启用识别服务 / 录音太长 / 没听清——各给一句原因 + 一个行内动作(🎤 重录,或模型没准备好时的 **`准备模型`** 按钮,下载在 host 上跑、关掉面板也继续)。按 `✕` 就收起、工具行原样还回来。
|
|
91
|
-
- 一次最长 **60 秒**;需要 DSH 装着语音识别服务(`@deepseek-ai/dsh-experimental-speech-to-text`)。没有那个服务时,点麦克风会明说不可用,不会装作在录。
|
|
37
|
+

|
|
92
38
|
|
|
93
|
-
|
|
39
|
+
*英文例句的解读小窗中,直接启动语音追问。*
|
|
94
40
|
|
|
95
|
-
|
|
41
|
+
语音卡片默认开启**说完直接发送**和**空录音时收起**。在 **设置 → 界面 → 语音输入** 中可以分别关闭。
|
|
96
42
|
|
|
97
|
-
|
|
43
|
+

|
|
98
44
|
|
|
99
|
-
|
|
100
|
-
| --- | --- |
|
|
101
|
-
| **小窗里的内容**(翻译 / 详解 / 某条回答里的几句) | 直接在小窗里划选 → 浮出 `❝ 引用` → 点一下 |
|
|
102
|
-
| **主界面上选中的文字**(小窗开着的时候) | 在页面里划选 → 同一个 `❝ 引用` → 点一下(侧边栏网页里划词也一样) |
|
|
103
|
-
| **整条回答** | 鼠标移到那条回答上,末尾浮出 `❝ 引用整条`(网页回答会先折成 Markdown,不会引用一整页 HTML 源码) |
|
|
104
|
-
|
|
105
|
-
<img src="assets/quotes.png" alt="引用:输入框上方的引用卡片、气泡里的引用块、回答末尾的「引用整条」" width="540">
|
|
106
|
-
|
|
107
|
-
- **引用卡片贴在输入框上方**:来源(小窗第 2 轮回答 / 小窗「翻译」节 / 主界面选中 / 侧边栏网页…)+ 内容摘要,右侧 `✕` 单条删除。最多 4 段,重复的不会重复加。
|
|
108
|
-
- **引用只带"模型看不到的东西"**:每段引用都写清**出自哪里**;只有材料本身不在模型上下文里时,才额外附上它当时所在的那一段对话。
|
|
109
|
-
- **小窗里的引用**(某条回答 / 翻译节 / 详解节 / 顶部选中文字条)→ **只写出处**(`小窗第 2 轮回答`、`小窗「翻译」节`……):小窗这几轮对话本来就会随历史进模型,再贴一遍是重复;
|
|
110
|
-
- **主界面(会话)里的引用** → 附上 `【引用处上下文】`:引用所在的**那一组对话 ± 一组**(一组 = 一条用户消息 + 它的回答,被引用的部分用 `【】` 标出),由 host 从**会话记录**里取,工具调用/思考/系统注入全部滤掉;
|
|
111
|
-
- **侧边栏网页里的引用** → 附上网页里选区前后各 1500 字的窗口。
|
|
112
|
-
- **发送时才拼进消息**:`【引用 1】(来自小窗第 2 轮回答)` +(需要时)`【引用处上下文】…` + `【我的问题】…` —— 出处/上下文在前、问题在后,用户气泡里也照这个顺序显示,回头翻记录知道当时在问什么。
|
|
113
|
-
- **只挂引用、一个字不写也能发**:这时用一句兜底提问("就上面引用的文字,说说它在这里是什么意思。"),不会空发。
|
|
114
|
-
- **只属于下一条提问**:发出即清空;换一段新的选中文字、或从「最近」回放另一段对话时也会清掉(那些引用属于上一次对话)。
|
|
115
|
-
- **小窗开着时浮标就是「引用」**:要解读新的一段,先按 `Esc` 收起小窗再划词——避免两个小窗抢同一处选区。
|
|
116
|
-
- **点完「✦ 解读」那行高亮会立刻收掉**(`Esc`、重开也一样):小窗顶部本来就写着选中文字,留着高亮反而碍事——
|
|
117
|
-
小窗亮着时它是「活的选区」,在输入框里按一下方向键就会又冒出一个「❝ 引用」,指的却是刚才解读的那段。
|
|
118
|
-
要引用它,重新划一次即可(那时浮标才是「❝ 引用」)。
|
|
119
|
-
- 引用会**进历史、也进升格**:回放这段对话时引用块跟着回来(以拼好的原文形式),升格出来的正式会话里同样带着。
|
|
120
|
-
|
|
121
|
-
### 输出偏好:Markdown ↔ 网页
|
|
122
|
-
|
|
123
|
-
输入区左下角那个两格开关(`M↓` = Markdown / `▭` = 网页)决定**追问的回答用什么形式**。默认是 **Markdown**,选择记在浏览器本地,下次打开还是它。
|
|
124
|
-
|
|
125
|
-
| 档位 | 行为 |
|
|
126
|
-
| --- | --- |
|
|
127
|
-
| **Markdown**(默认) | 普通文字 / Markdown 回答——标题、列表、表格、代码块都可以。**并不禁止 HTML**:如果某个问题用一页网页明显讲得更清楚,照样会给网页;只是不会为了"好看"把简单问题做成页面 |
|
|
128
|
-
| **网页** | **网页优先**(不是"一律网页"):需要对比、流程、层级、图表,或文字答案会长到不好读 → 直接给**完整的单文件 HTML**,页面本身就是答案;一两段话能说清的简单问题**仍然用文字回答** |
|
|
45
|
+
> 输入法联动需要 **macOS 14.4+** 与支持的语音输入法(已适配豆包)。小窗麦克风需要 DSH 启用语音识别服务。未配置语音时,文字解读与键盘追问仍可使用。
|
|
129
46
|
|
|
130
|
-
<
|
|
47
|
+
<details>
|
|
48
|
+
<summary>小窗麦克风提示不可用?</summary>
|
|
131
49
|
|
|
132
|
-
|
|
50
|
+
请检查 DSH 宿主是否启用了 `@deepseek-ai/dsh-experimental-speech-to-text`,以及识别模型是否已就绪。首次使用时,如果小窗显示 **准备模型**,点击后按提示准备;下载与识别在宿主运行。
|
|
133
51
|
|
|
134
|
-
|
|
135
|
-
- **页面就是答案,不是附件**:生成的是**完整单文件 HTML**(样式与脚本全内联),小窗当场渲染成可交互页面——能悬停、能点、能滚,不用保存成文件再打开。
|
|
136
|
-
- **两个视图随时切**:回答上方有 `预览 / 源码` 两个页签,切到源码就能把 HTML 拿走。
|
|
137
|
-
- **默认就整页展示**:预览高度**由页面内容决定**(内部不滚动),所以一页对比表能完整看下来;想收起来点右侧 **`⤡ 还原`**,回到固定高度(320px、内部滚动)。
|
|
138
|
-
- **有设计规范兜底**:开网页模式时,插件会把自己带的 `skills/web-design/SKILL.md` 注入提示词——按小窗**约 500px 宽的窄容器**写的单列布局规范,避免生成那种"三栏卡片 + 渐变大标题"的通用 AI 模板感。(该文件读不到时有一份内置兜底,不会因此失败。)
|
|
139
|
-
- **回答仍然可追问**:网页结果会作为前情带回下一轮,可以继续说"把第二列改成时间线"。
|
|
52
|
+
这与输入法语音是两个独立入口:输入法联动使用输入法本身的识别能力。
|
|
140
53
|
|
|
141
|
-
|
|
54
|
+
</details>
|
|
142
55
|
|
|
143
|
-
|
|
56
|
+
<a id="read"></a>
|
|
144
57
|
|
|
145
|
-
|
|
58
|
+
## 02 / 先读懂,再深入
|
|
146
59
|
|
|
147
|
-
|
|
60
|
+
在 DSH 中选中文字,点击 **解读**。英文先给译文,中文按内容给解读,代码则生成注释;需要更多背景时,再点 **展开详解**。
|
|
148
61
|
|
|
149
|
-
|
|
62
|
+

|
|
150
63
|
|
|
151
|
-
|
|
64
|
+
*选中 “The migration ran long, so we will ship on Wednesday”,先得到译文。*
|
|
152
65
|
|
|
153
|
-
|
|
66
|
+
展开后,模型结合前后文与可用的会话背景解释术语、语气和承接关系。你可以直接在底部继续追问。
|
|
154
67
|
|
|
155
|
-
|
|
68
|
+

|
|
156
69
|
|
|
157
|
-
|
|
70
|
+
*接着问“ran long 是说迁移失败了吗?”,把问题留在同一段讨论里。*
|
|
158
71
|
|
|
159
|
-
|
|
72
|
+
<a id="quotes"></a>
|
|
160
73
|
|
|
161
|
-
|
|
74
|
+
## 03 / 把相关内容,一起带进来
|
|
162
75
|
|
|
163
|
-
|
|
76
|
+
小窗打开时,在主界面、小窗正文或支持的侧边栏网页预览中划选文字,点 **引用**,就能带着新材料继续问。一次最多加入 **4 段引用**,每段都可以单独移除。
|
|
164
77
|
|
|
165
|
-
|
|
166
|
-
| --- | --- |
|
|
167
|
-
| 内置「文档预览」的 HTML 文件预览 | 在侧边栏点开一个 `.html` 文件(预览模式) |
|
|
168
|
-
| 其它插件在侧边栏渲染的生成网页 | **「图解」**(`dsh-reply-visual`:模型产出的单文件 HTML 在侧边栏渲染)等 |
|
|
78
|
+

|
|
169
79
|
|
|
170
|
-
|
|
80
|
+
不必每次从头来:
|
|
171
81
|
|
|
172
|
-
|
|
|
82
|
+
| 想做什么 | 入口 |
|
|
173
83
|
| --- | --- |
|
|
174
|
-
|
|
|
175
|
-
|
|
|
176
|
-
|
|
|
177
|
-
|
|
178
|
-
**浮现与消失的时机都与主会话一致**:
|
|
179
|
-
|
|
180
|
-
| 动作 | 结果 |
|
|
181
|
-
| --- | --- |
|
|
182
|
-
| 拖拽划词(按住鼠标) | **期间不弹**;松手后才浮出,位置是**最终**选区末端 |
|
|
183
|
-
| 键盘扩选(`Shift`+方向键) | 松键即弹 |
|
|
184
|
-
| 在网页里点一下(取消选区 / 点别处) | **按下就收**(不等松手),选区真没了再补一条清空 |
|
|
185
|
-
| 点另一个网页 | 也收(跨帧点击同样算"点了别处") |
|
|
186
|
-
| 点主界面别处 / 滚主界面 / 改窗口大小 | 收(与主会话同一套规则) |
|
|
187
|
-
| 页面重绘、动画、字体回流导致的**瞬时**读不到选区 | **不**收(连续 6 拍 ≈1.5s 都读不到才算真没了) |
|
|
188
|
-
| 选区滚出网页可视区 | 收(滚回来会重新浮出) |
|
|
189
|
-
|
|
190
|
-
**安全边界没有退让**:帧始终是不透明源,**不会**加 `allow-same-origin`——网页读不到 GUI 的 DOM / Cookie / localStorage,也不能导航顶层窗口。桥只往外报「选中了什么 + 周围那点文本」。给宿主预览补 `allow-scripts` 是因为那份 HTML 已被清洗成"无脚本",放宽 `script-src` 之后真正会跑的只有桥自己;别人插件的沙箱一概不碰(只桥它们自己已经开了脚本的帧)。
|
|
191
|
-
|
|
192
|
-
不想要这个行为就关掉:`bridgeSidebarPreview: false`(见下面的配置表)。
|
|
84
|
+
| 回到刚才的解读 | 右下角悬浮胶囊,或小窗里的 **最近** |
|
|
85
|
+
| 把讨论继续做下去 | **升格**,将解读与追问带入正式会话 |
|
|
86
|
+
| 阅读代码 | 选中代码,查看注释与原代码交替呈现的解释 |
|
|
87
|
+
| 用图表理解复杂问题 | 将**输出偏好**切为网页,模型可生成 HTML 页面 |
|
|
193
88
|
|
|
194
|
-
>
|
|
89
|
+
<a id="install"></a>
|
|
195
90
|
|
|
196
|
-
|
|
91
|
+
## 快速安装
|
|
197
92
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
<!--
|
|
201
|
-
上面几张演示图的来源(要换图看这里):
|
|
202
|
-
· 图由 scripts/build-demo-pages.mjs 生成演示页 —— 样式直接从 lib/client.js 抽取真实 CSS,
|
|
203
|
-
DOM 结构与文案照 src/client/index.js 的渲染器写,所以和真实面板一致(非手绘示意图)。
|
|
204
|
-
· 截图:Chrome 无头(--headless --screenshot --force-device-scale-factor=2)
|
|
205
|
-
· 裁边:node scripts/crop-png.mjs <in.png> assets/<name>.png 24
|
|
206
|
-
· 想换成真实环境截图,直接用同名文件覆盖 assets/ 下的图片即可。
|
|
207
|
-
-->
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
## 安装
|
|
212
|
-
|
|
213
|
-
### 方式一:从 npm 安装
|
|
214
|
-
|
|
215
|
-
已发布到 npm registry:
|
|
93
|
+
这是 DSH 环境中的插件。请先安装 DSH、配置一个可用模型,再运行:
|
|
216
94
|
|
|
217
95
|
```bash
|
|
218
|
-
npm install @yfwu2020/dsh-selection-explain
|
|
219
|
-
# 或直接装配进 profile(npm 包名可直接用)
|
|
220
96
|
dsh plugin --profile web add @yfwu2020/dsh-selection-explain
|
|
221
97
|
```
|
|
222
98
|
|
|
223
|
-
|
|
99
|
+
安装后刷新 DSH 页面,选中一段文字,点击 **解读** 即可。
|
|
224
100
|
|
|
225
|
-
|
|
226
|
-
用 `latest` 路径可以不写版本号、永远取最新:
|
|
101
|
+
也可在 [GitHub Releases](https://github.com/yfwu2020/dsh-selection-explain/releases) 下载构建包,按下方说明安装。
|
|
227
102
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
VER=$(curl -sI https://github.com/yfwu2020/dsh-selection-explain/releases/latest | grep -i '^location:' | sed 's|.*/tag/v||' | tr -d '\r\n')
|
|
231
|
-
curl -LO "https://github.com/yfwu2020/dsh-selection-explain/releases/download/v$VER/yfwu2020-dsh-selection-explain-$VER.tgz"
|
|
103
|
+
<details>
|
|
104
|
+
<summary>安装 Release 构建包</summary>
|
|
232
105
|
|
|
233
|
-
|
|
234
|
-
mkdir -p ~/.dsh/plugins/dsh-selection-explain
|
|
235
|
-
tar -xzf "yfwu2020-dsh-selection-explain-$VER.tgz" \
|
|
236
|
-
-C ~/.dsh/plugins/dsh-selection-explain --strip-components=1
|
|
106
|
+
将下载的 `.tgz` 放到当前目录,以 v0.10.1 为例:
|
|
237
107
|
|
|
238
|
-
|
|
108
|
+
```bash
|
|
109
|
+
mkdir -p ~/.dsh/plugins/dsh-selection-explain
|
|
110
|
+
tar -xzf yfwu2020-dsh-selection-explain-0.10.1.tgz -C ~/.dsh/plugins/dsh-selection-explain --strip-components=1
|
|
239
111
|
dsh plugin --profile web add ~/.dsh/plugins/dsh-selection-explain
|
|
240
112
|
```
|
|
241
113
|
|
|
242
|
-
|
|
243
|
-
|
|
114
|
+
安装完成后刷新 DSH 页面。其他版本请替换文件名中的版本号。
|
|
115
|
+
|
|
116
|
+
</details>
|
|
244
117
|
|
|
245
|
-
|
|
118
|
+
<details>
|
|
119
|
+
<summary>从源码安装 / 卸载</summary>
|
|
246
120
|
|
|
247
121
|
```bash
|
|
248
|
-
git clone
|
|
122
|
+
git clone https://github.com/yfwu2020/dsh-selection-explain.git
|
|
249
123
|
cd dsh-selection-explain
|
|
250
|
-
|
|
251
|
-
# 装依赖(编译用的 @deepseek-ai/* 类型与 tsc 都在 devDependencies 里)
|
|
252
124
|
npm install
|
|
253
|
-
|
|
254
|
-
# 构建(自动探测 DSH 运行时;host 走 tsc,client 是手写 bundle 直接拷贝)
|
|
255
|
-
bash scripts/build.sh
|
|
256
|
-
|
|
257
|
-
# 装配
|
|
125
|
+
npm run build
|
|
258
126
|
dsh plugin --profile web add .
|
|
259
127
|
```
|
|
260
128
|
|
|
261
|
-
|
|
262
|
-
> → `DSH_CHECKOUT` → PATH 上的 `dsh` → 常见 checkout → npx 缓存。
|
|
263
|
-
> 探测不到时可以显式指定:`DSH_CHECKOUT=/path/to/dsh-harness bash scripts/build.sh`。
|
|
129
|
+
源码构建需要 package.json 中列出的 DSH 开发依赖可用。
|
|
264
130
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
### 卸载
|
|
131
|
+
卸载 npm 安装的插件:
|
|
268
132
|
|
|
269
133
|
```bash
|
|
270
134
|
dsh plugin --profile web remove @yfwu2020/dsh-selection-explain
|
|
271
135
|
```
|
|
272
136
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
## 用法
|
|
276
|
-
|
|
277
|
-
| 动作 | 结果 |
|
|
278
|
-
| --- | --- |
|
|
279
|
-
| **鼠标划选** / `Shift` + 方向键 | 选区末端浮出 `✦ 解读` 药丸按钮(✦ 是图标,按钮文字是「解读」;输入框内的选择不触发,避免干扰打字)。侧边栏的 HTML 文件预览里同样生效 |
|
|
280
|
-
| 点击按钮 | 弹出面板:顶部是选中文字,正文是「翻译」卡片 |
|
|
281
|
-
| 点 `↓ 展开详解` | 加载完整会话背景,追加「详解」卡片 |
|
|
282
|
-
| 面板底部输入框 | 就这段文字继续追问(`Enter` 发送 / `Shift+Enter` 换行) |
|
|
283
|
-
| 输入框右边 🎤 | **语音输入**(与主会话同一套 UI):点一下开始说,`■` 结束并把文字插进输入框、`✕` 取消(`Esc` 同样取消,一次最长 60 秒) |
|
|
284
|
-
| 小窗开着时划词 | 浮出的是 `❝ 引用`(不是 `✦ 解读`):把这段文字挂进输入框的引用区,跟着下一条提问发给模型 |
|
|
285
|
-
| 鼠标移到某条回答上 | 末尾浮出 `❝ 引用整条`:把整条回答挂进引用区(网页回答先折成 Markdown) |
|
|
286
|
-
| `🕘 最近` | 打开最近划过的列表,点一条调回那段对话 |
|
|
287
|
-
| `↗ 升格` | 把这次解读变成正式会话 |
|
|
288
|
-
| `✕` / `Esc` | 关闭面板。**语音进行中按 `Esc` 只取消这次语音(等价于点 `✕`,已说出来的字留着),面板留着**;再按一次才关窗 |
|
|
289
|
-
| 右下角悬浮胶囊 | 点一下:关着就回到最近一次小窗,开着就收起(来回切换,不重新请求) |
|
|
290
|
-
|
|
291
|
-
**几个不别扭的细节:**
|
|
292
|
-
|
|
293
|
-
- **与完整视图聊天小窗共存(0.9.1)**:Esc 优先退出最后点击或操作的展开小窗。点标题、消息或窗内空白就算激活,不要求点中输入框。聊天先收回紧凑条,解读按设置下拉、设置、录音、面板逐层退出;点击两窗之外会清除活动归属,之后先退解读,再按一次收起聊天。每次只退一层,无需重新点击;历史列表属于解读,原生菜单和输入法仍优先。需要解读插件 0.9.1 与完整视图插件 0.1.5 配合,普通分栏保留原 Esc 行为。
|
|
294
|
-
- **点面板外不会关闭面板**。答案留在原地,只有 `✕` / `Esc` / 右下角胶囊能收起——不会因为手滑点一下就得重新问。
|
|
295
|
-
- **输入框跟着内容长高**:写(或说)到第二行就自己撑开,最多约 7 行(132px);再长就内部滚动并**自动停在最新一行**——不会像以前那样第二行以下的字被裁掉。
|
|
296
|
-
- **开窗即能打字**:小窗打开、输入框出现后会自动获得焦点(只有"你正在别处打字"时才不抢)。`Tab` 也能摸到输入框和 🎤。
|
|
297
|
-
- **说话时看得见光标在跳**:录音一开始光标就落在输入框里,预览每落一句、光标跟到最新文字末尾——像个正在打字的人,而不是一坨自己冒出来的字。
|
|
298
|
-
- 关闭时**会中止正在飞的请求**,并记成「已停止」(不是失败);内容都留着,点胶囊回来还是原样。
|
|
299
|
-
- 拖动面板:抓头部即可拖动。
|
|
300
|
-
- 追问时消息区**贴底跟随**;你主动往上翻就不会被拽回去。
|
|
301
|
-
- `🕘 最近` 的列表点别处就收(不连带关面板),但 `Esc` 会把面板和它一起收。
|
|
302
|
-
- **引用是"下一条提问"的**:发出去就清空;引用卡片上的 `✕` 可以单条撤掉;最多 4 段。
|
|
303
|
-
|
|
304
|
-
### 调试钩子
|
|
305
|
-
|
|
306
|
-
浏览器控制台里可以直接驱动,方便验证渲染链路:
|
|
307
|
-
|
|
308
|
-
```js
|
|
309
|
-
window.__dshSelectionExplain.open('the migration ran long', 'Dev: the migration ran long, so we ship Wednesday.')
|
|
310
|
-
window.__dshSelectionExplain.state() // { phase, chars, model, sections }
|
|
311
|
-
window.__dshSelectionExplain.ping() // 当前模型路由与限制
|
|
312
|
-
window.__dshSelectionExplain.bridge() // 侧边栏网页划词桥:{ on, frames:[{connected,own,srcdoc,sandbox}], active, selection }
|
|
313
|
-
window.__dshSelectionExplain.selection() // 当前选区:{ text, source:'document'|'iframe', label, context }
|
|
314
|
-
window.__dshSelectionExplain.quotes() // 待发送的引用:[{ id, label, text, context, contextChars, session }]
|
|
315
|
-
window.__dshSelectionExplain.quote('某段文字', '主界面选中') // 手工加一段引用(等价于点浮标)
|
|
316
|
-
window.__dshSelectionExplain.compose() // 引用 + 提问拼出来的那条消息原文(发给模型的就是它)
|
|
317
|
-
window.__dshSelectionExplain.quoteState() // 引用浮标:{ visible, selection:{text,label,source}, panelOpen }
|
|
318
|
-
window.__dshSelectionExplain.voice() // 语音输入:{ phase, capture, activity, dot, waveform, stop, action, bars, lastText, catalog, live:{ active, committed, preview, passes, rewritten, tap } }
|
|
319
|
-
window.__dshSelectionExplain.voiceTick() // 手动催一拍实时字幕(测试用,不用等 1.3 秒节流)
|
|
320
|
-
window.__dshSelectionExplain.voiceNode() // 🎤 触发键节点(无头环境里可能挂了两棵树,用它拿真正带监听的那个)
|
|
321
|
-
window.__dshSelectionExplain.voiceNodes() // 录音行的各部件:{ row, cancel, wave, activity, action, stop }
|
|
322
|
-
window.__dshSelectionExplain.askValue() // 读追问输入框(传参即写入),用来验"转写文字插进去了"
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
### HTTP 接口
|
|
326
|
-
|
|
327
|
-
host 半注册了同源路由,可以单独调用:
|
|
328
|
-
|
|
329
|
-
| 路由 | 说明 |
|
|
330
|
-
| --- | --- |
|
|
331
|
-
| `POST /selection-explain/api/analyze` | 选中文字 + 上下文 → **SSE 流式**返回翻译与详解 |
|
|
332
|
-
| `GET /selection-explain/api/ping` | 健康检查:当前模型路由、各阶段推理档位、限制 |
|
|
333
|
-
| `GET /selection-explain/api/models` | 可选模型列表(面板里的模型选择器用;带 `sessionId` 时 `current` 按该会话解析) |
|
|
334
|
-
| `GET/POST /selection-explain/api/settings` | 小窗设置页:`GET` 下发 `{ groups, values, defaults }`(字段声明即校验边界),`POST { values }` 合并写入、`{ reset: true }` 恢复默认 |
|
|
335
|
-
| `GET /selection-explain/api/route` | 极轻的"这一轮用哪个模型":`?sessionId=…` → `{ provider, model }`(设置页与模型胶囊按秒轮询它;带会话级短缓存,见下面「配置」一节) |
|
|
336
|
-
| `GET/POST /selection-explain/api/history` | 小窗对话历史的读 / 写 / 删除 |
|
|
337
|
-
| `POST /selection-explain/api/quote-context` | 引用上下文:`{ sessionId, text }` → 该引用所在的**一组对话 ± 一组**(`{ matched, context, rounds }`;定位不到时 `matched:false`,客户端退回自己采的局部上下文) |
|
|
338
|
-
| `POST /selection-explain/api/promote` | 升格为正式会话 |
|
|
339
|
-
| `GET /selection-explain/api/speech` | 语音输入:识别器目录与就绪状态(`{ available, providers:[{id,phase}], limits:{maxSeconds,maxBytes} }`) |
|
|
340
|
-
| `POST /selection-explain/api/speech/transcribe` | 一段 **16kHz 单声道 PCM16 WAV**(base64)→ 文字:`{ audioBase64, language?, providerId? }` |
|
|
341
|
-
| `POST /selection-explain/api/speech/prepare` | 显式准备本地识别模型(首次要下载,只在用户点了提示里的「准备模型」时调用) |
|
|
342
|
-
|
|
343
|
-
转写示例(`SPEECH_WAV` 是一段规范 WAV):
|
|
344
|
-
|
|
345
|
-
```bash
|
|
346
|
-
node -e 'const fs=require("fs");const b=fs.readFileSync(process.argv[1]);process.stdout.write(JSON.stringify({audioBase64:b.toString("base64")}))' /tmp/v.wav > /tmp/v.json
|
|
347
|
-
curl -s -X POST http://127.0.0.1:3080/selection-explain/api/speech/transcribe \
|
|
348
|
-
-H 'content-type: application/json' --data-binary @/tmp/v.json
|
|
349
|
-
# → {"ok":true,"text":"这是一段语音输入的测试…","providerId":"sensevoice-local",...}
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
健康检查示例:
|
|
353
|
-
|
|
354
|
-
```bash
|
|
355
|
-
curl -s http://127.0.0.1:3080/selection-explain/api/ping
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
---
|
|
359
|
-
|
|
360
|
-
## 档位兜底:「关」档不被支持时自动降级
|
|
137
|
+
</details>
|
|
361
138
|
|
|
362
|
-
|
|
363
|
-
撞上这种模型时整个划词解读会全挂。
|
|
139
|
+
<a id="settings"></a>
|
|
364
140
|
|
|
365
|
-
|
|
141
|
+
## 使用与设置
|
|
366
142
|
|
|
367
|
-
|
|
368
|
-
的 `noOff` 字段),改用 **`low`**(最接近"关"的可接受档位)重试同一轮,并推一条 notice 说明。
|
|
369
|
-
- **下次不再尝试 `off`** —— 直接发 `low`,不再先失败一轮(省一个来回的时延,也不闪报错)。
|
|
370
|
-
- 重试只在"这一轮一个字都还没吐出来"时进行:已经出正文的失败不重试,避免把半截答案丢掉。
|
|
371
|
-
- 兜底覆盖**三种轮次**(首轮 / 详解 / 追问)。以前只对追问生效、而且要客户端显式传了档位才触发,
|
|
372
|
-
所以首轮撞上这种模型时无人兜底。
|
|
143
|
+
`Enter` 发送 · `Shift+Enter` 换行 · `Esc` 收起小窗。录音期间,第一次 `Esc` 只取消录音,再按一次才关闭小窗。
|
|
373
144
|
|
|
374
|
-
|
|
145
|
+
点击小窗顶部的 **设置**,修改会自动保存:
|
|
375
146
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
**位置和尺寸是两回事,只有尺寸会记住:**
|
|
379
|
-
|
|
380
|
-
- **位置不记**。每次开窗都按选区重新锚定:落在选区下方,下方放不下就翻到选区上方,
|
|
381
|
-
再夹进视口。拖动只对这一次有效,关掉重开就回到划词的地方 —— 这是刻意的设计。
|
|
382
|
-
- **尺寸记**。拖角调过的大小存在 host 上(`${DSH_HOME:-~/.dsh}/selection-explain/panel.json`,
|
|
383
|
-
接口 `GET/POST /selection-explain/api/panel`,载荷只有 `{w,h}`),
|
|
384
|
-
刷新、换标签页、换浏览器 profile 都回到那个大小。
|
|
385
|
-
- **双击右下角角标**恢复默认尺寸(清掉存档)。
|
|
386
|
-
|
|
387
|
-
其余约定:
|
|
388
|
-
|
|
389
|
-
- 尺寸下限 320×240,且始终夹在视口内(窗口缩小也不会超出)。上限是视口大小。
|
|
390
|
-
- 用户拉过大小后,设置页那条约 560px 的高度下限就**不再插手**(`data-resized` 标记)。
|
|
391
|
-
- 改动过程中不写盘,松手后防抖 300ms 再存 —— 不然每帧一个请求。
|
|
392
|
-
- 改大小用 **pointer 事件**(不是 mouse):触控板/触屏一样能拖,并且 `setPointerCapture`
|
|
393
|
-
保证鼠标划出窗口也不会丢掉这一轮。
|
|
394
|
-
- 只有**真的拖动过**才会触发存档逻辑(点一下角标不算改了大小)。
|
|
395
|
-
|
|
396
|
-
## 配置
|
|
397
|
-
|
|
398
|
-
### 方式一:在小窗里改(推荐)
|
|
399
|
-
|
|
400
|
-
小窗头部有一枚 **⚙ 设置**(和「最近」「升格」同款的文字键),点开是一个从右侧滑入的抽屉,
|
|
401
|
-
六个分类按任务分组:
|
|
402
|
-
|
|
403
|
-
| 分类 | 管什么 |
|
|
147
|
+
| 设置 | 可以调整什么 |
|
|
404
148
|
| --- | --- |
|
|
405
|
-
| 模型 |
|
|
406
|
-
|
|
|
407
|
-
| 背景 |
|
|
408
|
-
| 联网 |
|
|
409
|
-
| 界面 |
|
|
410
|
-
| 数据 |
|
|
411
|
-
|
|
412
|
-
- **改完即时生效,无需重启。** 改动会**自动保存**(防抖 400ms),底部那行只有脏了才报
|
|
413
|
-
「未保存 · N 项」,不弹 toast、不打断。
|
|
414
|
-
- **模型是一个下拉**,不用自己拼 provider 与 model:选项来自 host 已有的模型清单
|
|
415
|
-
(`GET /selection-explain/api/models?sessionId=…`,含每个 provider 下的全部模型)。
|
|
416
|
-
写配置时展开成 `provider` + `model` 两个字段。
|
|
417
|
-
- **「跟随主会话」是名副其实的**:不选具体模型时,插件按**小窗所属会话**的模型解析路由 ——
|
|
418
|
-
从会话原始事件里取最后一条 `model/selection`(`sessionModelOf`),
|
|
419
|
-
所以两个会话用不同模型时各用各的,下拉里「(当前:…)」报的也是本会话那一个。
|
|
420
|
-
会话没记过模型选择时落回 DSH 的全局默认模型。优先级:
|
|
421
|
-
小窗与设置页的统一选择 → **本会话的模型** → 全局默认 → 第一个可用。
|
|
422
|
-
- **「小窗所属会话」= 划词那一刻的会话**(`panelSessionId`),不是"当前正在看的会话"。
|
|
423
|
-
新版 DSH 从 `uiSession.adapter.current` 的主视图绑定读取会话 ID;会话目录不再提供 `current`。
|
|
424
|
-
小窗开着时切走会话,解读 / 追问 / 升格与模型解析仍按原来那个会话算 ——
|
|
425
|
-
否则背景会取错会话,模型也会跟着变。
|
|
426
|
-
模型清单请求使用这个会话 ID,并保留各自的响应快照,避免切换时较慢的旧请求覆盖新会话。
|
|
427
|
-
- **模型名是实时的(小窗开着就在跟)**:**小窗或设置抽屉任意一个开着**时按 2 秒轮询那条轻量接口
|
|
428
|
-
`GET /selection-explain/api/route?sessionId=…`,主会话里换了模型,设置页括号里和**小窗输入框那枚
|
|
429
|
-
模型胶囊**自己就跟着变,不用关掉重开(`/models` 要列全部模型、`/ping` 要枚举工具表,都不适合按秒轮询)。
|
|
430
|
-
两个都收起来即停。host 侧这条接口带**会话级短缓存**:live 会话的事件数(`seq`)没涨就直接回上次的结论,
|
|
431
|
-
涨了也至少隔 3 秒才重读一次 —— 否则"小窗一直开着"会变成每 2 秒把整份会话日志重放一遍
|
|
432
|
-
(实测 1.5MB 的会话一次 80~125ms)。用户动作路径(解读 / 追问 / 升格 / 模型清单 / ping)不吃这份缓存,
|
|
433
|
-
拿到的永远是此刻那一份。
|
|
434
|
-
- **胶囊上显示的是模型名,不是 id**。跟随态也一样:开窗时顺手拉一次模型清单(60 秒缓存),
|
|
435
|
-
按 **provider + model 两个键**换成清单里的显示名 —— 同一个 model id 在多个 provider 下是不同模型、
|
|
436
|
-
不同名字(实测 `deepseek-flash` 在 opencode-go 叫「DeepSeek Flash」、在 deepseek-official /
|
|
437
|
-
deepseek-account 叫「DeepSeek-V41-Flash」),只按 id 查会张冠李戴。清单还没回来时先显示 id,
|
|
438
|
-
回来后再画一次;provider 全名在胶囊的 hover 提示里。
|
|
439
|
-
- **输入框那枚胶囊点开的模型列表里,当前在用的那个会打勾**(跟随态也一样,并在名字后面带一枚
|
|
440
|
-
「跟随」小标 —— 说明这个勾来自主会话、不是你选的);下面「推理等级」列的是**在用模型**声明的
|
|
441
|
-
档位。名字过长时收窄名字显示省略号,保证一行里放得下 勾 + 名字 + 小标 + provider。
|
|
442
|
-
- **首轮、次轮和追问采用同一个模型**。小窗输入框与设置页双向同步,任一处修改均自动保存。
|
|
443
|
-
旧版独立追问配置与浏览器模型缓存不再覆盖这一选择;三个阶段的思考强度仍分别设置。
|
|
444
|
-
- **「恢复默认」立即生效**:恢复各项配置,同时更新模型选择、控件禁用状态与悬浮入口。
|
|
445
|
-
- **时间一律按「秒」填**。配置里仍是毫秒(schema 与既有 yml 值不动),
|
|
446
|
-
换算由 spec 上的 `scale: 1000` 声明 —— 边界的唯一事实来源还是 Config schema。
|
|
447
|
-
- 写完落在插件自己的目录,**不碰 `cordis.patch.yml`**:
|
|
448
|
-
|
|
449
|
-
```
|
|
450
|
-
${DSH_HOME:-~/.dsh}/selection-explain/settings.json
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
只存**改过的键**;「恢复默认」就是把它清空,回到下面表里的 yml/出厂值。
|
|
454
|
-
那个 yml 是**装配清单**,让 UI 去改它既要人肉重启、又可能把 YAML 结构写坏,所以不碰。
|
|
455
|
-
|
|
456
|
-
> 字段的标题 / 说明 / 边界由 host 通过 `GET /selection-explain/api/settings` 下发,
|
|
457
|
-
> 客户端只负责画(见 `src/settings.ts`)。这样 UI 的边界和 host 的 clamp 不会各写一份、久了互相漂。
|
|
458
|
-
|
|
459
|
-
### 方式二:写 `cordis.patch.yml`
|
|
460
|
-
|
|
461
|
-
配置写在 profile 的 `cordis.patch.yml` 里同 id 的行上(`dsh plugin add` 装好后,包自带的 `cordis.patch.yml` 里已有可改的样板):
|
|
462
|
-
|
|
463
|
-
```yaml
|
|
464
|
-
- insert:
|
|
465
|
-
- id: selection-explain
|
|
466
|
-
name: '@yfwu2020/dsh-selection-explain'
|
|
467
|
-
config:
|
|
468
|
-
reasoningEffort: high
|
|
469
|
-
sessionContextMaxMessages: 24
|
|
470
|
-
```
|
|
149
|
+
| 模型 | 跟随主会话,或为解读单独指定模型 |
|
|
150
|
+
| 思考强度 | 首轮、次轮与追问分别选择 **关 / 低 / 高 / 最高**;按模型实际能力映射 |
|
|
151
|
+
| 背景 | 是否附带会话消息,以及引用的上下文范围 |
|
|
152
|
+
| 联网 | 是否允许模型调用工具,以及允许的工具范围 |
|
|
153
|
+
| 界面 | 语音卡片收尾、侧边栏网页划词与悬浮胶囊 |
|
|
154
|
+
| 数据 | 保留多少条最近解读 |
|
|
471
155
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
### 模型与推理
|
|
475
|
-
|
|
476
|
-
| 字段 | 默认 | 说明 |
|
|
477
|
-
| --- | --- | --- |
|
|
478
|
-
| `provider` | `''` | 固定 provider 路由;留空 = 跟随「设置 → 默认模型」 |
|
|
479
|
-
| `model` | `''` | 固定 model id;留空 = 跟随主会话 |
|
|
480
|
-
| `reasoningEffort` | `high` | **详解**阶段推理档位(`off` / `low` / `high` / `max`) |
|
|
481
|
-
| `translationReasoningEffort` | `off` | **首轮**翻译、解读或注释单独一档,默认关闭思考 |
|
|
482
|
-
| `chatReasoningEffort` | `high` | **追问**档位(追问要准不要快;想更快可调 `low`) |
|
|
483
|
-
| `temperature` | `-1` | 采样温度。**`-1` = 不传**,交给供应商默认(与主会话一致);`0`~`2` 之间才真的传 |
|
|
484
|
-
| `maxTokens` | `0` | 单次输出上限(tokens)。**`0` = 不限制**。填正数可控成本 / 控时延 |
|
|
485
|
-
| `timeoutMs` | `300000` | 单次调用超时(含工具轮;客户端断开即中止上游) |
|
|
486
|
-
|
|
487
|
-
> 三个阶段的思考强度可分别设置。默认 `translationReasoningEffort: off` 优先让首轮快速回答;代码中的详解初始值为 `reasoningEffort: high`,安装配置为 `off`,追问默认 `high`。
|
|
488
|
-
|
|
489
|
-
输出上限、采样温度和结果缓存不在设置页展示;既有底层配置继续兼容。
|
|
490
|
-
|
|
491
|
-
### 上下文与缓存
|
|
492
|
-
|
|
493
|
-
| 字段 | 默认 | 说明 |
|
|
494
|
-
| --- | --- | --- |
|
|
495
|
-
| `maxSelectionChars` | `4000` | 选中文字长度上限(超了不出现按钮,host 侧也会拦截并给出明确报错) |
|
|
496
|
-
| `maxContextChars` | `12000` | 局部上下文上限(选区前后各取一段窗口) |
|
|
497
|
-
| `sessionContext` | `true` | 是否注入当前会话的对话背景(判断指代与前提的关键) |
|
|
498
|
-
| `sessionContextFastMessages` | `8` | **首轮翻译**用的背景条数;越小首字越快 |
|
|
499
|
-
| `sessionContextMaxMessages` | `24` | **详解 / 追问**用的背景条数(自选中文字所在消息向上取) |
|
|
500
|
-
| `sessionContextMaxChars` | `0` | 会话背景字符上限;**`0` = 不限**(只按条数取窗口) |
|
|
501
|
-
| `quoteContextRounds` | `1` | **引用**带几组上下文:引用所在那一组对话 ± 这么多组(一组 = 用户消息 + 它的回答) |
|
|
502
|
-
| `quoteContextMaxCharsPerTurn` | `2000` | 引用上下文里单条消息的上限(超了**围绕引用**截断,不裁掉引用本身) |
|
|
503
|
-
| `quoteContextMaxChars` | `6000` | 引用上下文整段上限(超了先丢最早的、再丢引用之后的) |
|
|
504
|
-
| `resultCacheTtlMs` | `600000` | 共享结果缓存 TTL(毫秒);`0` = 关闭 |
|
|
505
|
-
| `historyMaxEntries` | `20` | 保留最近对话数量;`0` = 不保存新对话,已有对话保留 |
|
|
506
|
-
| `maxRequestsPerMinute` | `40` | 本地限流,防误触发刷爆额度 |
|
|
507
|
-
| `bridgeSidebarPreview` | `true` | 侧边栏 HTML 预览里的划词桥。关掉 = 预览网页里选中文字不再弹按钮(预览帧不再补 `allow-scripts`,也不注入桥脚本) |
|
|
508
|
-
| `chatProvider` | `''` | 旧版兼容字段,不再参与模型选择 |
|
|
509
|
-
| `chatModel` | `''` | 旧版兼容字段,不再参与模型选择 |
|
|
510
|
-
| `pillEnabled` | `true` | 右下角悬浮胶囊(最近一次划词的状态入口)。关掉 = 右下角不再出现胶囊 |
|
|
511
|
-
| `pillIdleMs` | `10000` | 静置多久把胶囊收成小球(毫秒,2000~600000)。只在**没装费用胶囊**时生效(有费用胶囊时两者并排,收球会让它们错位) |
|
|
512
|
-
|
|
513
|
-
### 工具(联网 / 文件)
|
|
514
|
-
|
|
515
|
-
| 字段 | 默认 | 说明 |
|
|
516
|
-
| --- | --- | --- |
|
|
517
|
-
| `tools` | `true` | 是否允许解读过程调用工具 |
|
|
518
|
-
| `toolNames` | `advanced_search,platform_search,read,grep,glob` | 工具白名单(逗号分隔)。默认用 free-search 插件的搜索工具,支持多引擎、时间过滤、平台搜索 |
|
|
519
|
-
| `fallbackToolNames` | `web_search` | **备用**清单:只在首选里的联网工具一个都不在时才补进来(没装 free-search 时兜底) |
|
|
520
|
-
| `maxToolRounds` | `2` | 最多几轮工具调用(用完会补一轮不带工具的收尾,避免"转半天没回答") |
|
|
521
|
-
| `toolResultMaxChars` | `3000` | 单个工具结果注入模型前的字符上限 |
|
|
522
|
-
| `toolReadRoot` | `''` | 文件类工具(`read`/`grep`/`glob`)的读取边界:`''` = 会话工作目录,`'*'` = 不限制,其他 = 指定目录。越界在**执行前**被拒 |
|
|
156
|
+
## 数据与使用范围
|
|
523
157
|
|
|
524
|
-
|
|
158
|
+
选中文字、必要的前后文,以及开启的会话背景会交给你配置的模型。可在设置中关闭会话背景或工具调用。
|
|
525
159
|
|
|
526
|
-
|
|
160
|
+
插件不将录音保存为音频文件,也不把音频附到会话;小窗录音由 DSH 配置的识别服务处理,是否经过云端取决于该服务。输入法语音由输入法本身处理,插件接收输入框中写入的文字。
|
|
527
161
|
|
|
528
|
-
|
|
529
|
-
浏览器(client half,挂在官方 shell.overlay 槽 —— 帧级浮层,不遮挡 App)
|
|
530
|
-
① 监听 selection(mouseup / Shift+方向键)→ 在选区末端定位浮标
|
|
531
|
-
② 点击后采集三段信息:
|
|
532
|
-
· 局部上下文:选区所在容器文本中,选区前后各 1500 字的窗口,【】标出选中部分
|
|
533
|
-
· 所在位置:容器类型(对话消息 / 代码块 / 表格 / 页面内容)+ 页面标题与地址
|
|
534
|
-
· 当前会话 id
|
|
535
|
-
③ POST /selection-explain/api/analyze → SSE 边收边渲染
|
|
536
|
-
↓
|
|
537
|
-
宿主(host half,进程内)
|
|
538
|
-
④ 解析模型路由:默认取「设置 → 默认模型」
|
|
539
|
-
⑤ 会话背景:按会话 id 读该会话的真实对话(工具调用 / 系统提示 / harness 注入内容全部剔除),
|
|
540
|
-
从最新往前取到预算上限,并把选中文字所在的那条标 ▶
|
|
541
|
-
⑥ 组两节提示词 → ctx.llm.stream(...) → text-delta 直接透传 SSE
|
|
542
|
-
(reasoning-delta 另走 thought 事件,只喂"正在想什么"那一行,不参与正文)
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
**几个设计取舍:**
|
|
546
|
-
|
|
547
|
-
- **不建会话**:小窗对话落在插件自己的 `history.json` 里。会话是重型对象,高频功能那样做只会堆死会话列表。
|
|
548
|
-
- **缓存 key 不含选中文字之后的内容**:否则主会话后面追加新消息时,同一个词的 key 就变了,表现为"找不着上次的小窗、又生成一个"。
|
|
549
|
-
- **上下文窗口锚定选中文字所在的那条消息**,向上取——不是简单取最近 N 条,这样"这个词在这段对话里什么意思"才有前提。
|
|
550
|
-
- **侧边栏预览里的选区由帧内桥上报**:那段上下文是在**帧内**用同一套算法(容器 ≥120 字、前后各 1500 字、`【】`标记、key 只取选中前 300 字)算出来的,所以侧边栏网页里的解释质量与主会话里一致,而不是退化成"只有选中文字、没有上下文"。
|
|
551
|
-
|
|
552
|
-
---
|
|
553
|
-
|
|
554
|
-
## 自检与开发
|
|
555
|
-
|
|
556
|
-
```bash
|
|
557
|
-
npm run build # 构建(= bash scripts/build.sh)
|
|
558
|
-
npm test # 600+ 条断言(filters / prompt / bridge / smoke / client / transcript / guard / speech / voice)
|
|
559
|
-
npm run test:smoke # 只跑划词桥的真浏览器冒烟(本机 Chrome;找不到自动 SKIP)
|
|
560
|
-
npm run test:voice # 只跑语音输入的真浏览器冒烟(Chrome + 假麦克风 + 真识别;缺东西自动 SKIP)
|
|
561
|
-
npm run typecheck # tsc --noEmit
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
| 脚本 | 用途 |
|
|
565
|
-
| --- | --- |
|
|
566
|
-
| `scripts/test-client.mjs` | 无浏览器集成测试:真实 host 路由 + 最小 DOM 桩,覆盖槽注册 → 划词浮标 → 点击 → SSE 流式渲染 → 两节内容 → 侧边栏网页划词桥(注入 / 来源校验 / 坐标换算 / 帧内清空)→ 清理(host 不在跑时自动跳过在线断言) |
|
|
567
|
-
| `scripts/test-bridge.mjs` | **划词桥单测**(无需宿主):从 bundle 里抠出桥脚本与注入函数单独跑 —— 插在哪、CSP 只放宽哪一条、沙箱补哪个 token、帧内报出来的文字 / `【】`上下文 / `keyContext` / 坐标对不对 |
|
|
568
|
-
| `scripts/smoke-bridge.mjs` | **划词桥真浏览器冒烟**(本机 Chrome,找不到就 SKIP):起临时 http 服务,用两种预览形态(`srcdoc` / `blob` bootstrap)验沙箱 + CSP + 跨不透明源 postMessage + `document.write` 冲不掉监听 |
|
|
569
|
-
| `scripts/test-prompt.mjs` | **提示词契约测试**:把两节结构标题、结论条格式、音标规则、详解来源判断、追问网页模式要求等关键约束固化成断言,误删即 CI 红 |
|
|
570
|
-
| `scripts/test-filters.mjs` | host 侧流式过滤器单测(思考泄漏、工具调用残渣等) |
|
|
571
|
-
| `scripts/test-transcript.mjs` | 会话背景窗口的离线测试(锚点 / 条数 / 不截断 / 噪音剔除 / 退化路径) |
|
|
572
|
-
| `scripts/test-guard.mjs` | 文件工具读取边界的路径校验 |
|
|
573
|
-
| `scripts/test-speech.mjs` | **语音输入契约 + 真路由**:`validateWave` 逐字段打表(采样率/声道/位深/长度字段/多余 chunk 全都要拒)+ 真 host 的三个语音路由(坏 base64 / 非规范 WAV / 超大音频 / 非 POST / 正弦波不会"听"成话);给了 `SPEECH_WAV=/tmp/v.wav` 还会真识别一句 |
|
|
574
|
-
| `scripts/smoke-voice.mjs` | **语音输入真浏览器冒烟**:headless Chrome + `--use-file-for-fake-audio-capture` 把一段真人语音当麦克风,页面里跑**真的 lib/client.js**,走完 录音 → MediaRecorder → 重采样 → WAV → 真 host 识别 → 插进输入框;顺带断言浏览器产出的 WAV 能过 host 的逐字段校验(缺 Chrome / 缺 `say` / host 没在跑都自动 SKIP) |
|
|
575
|
-
| `scripts/dump-prompt.mjs` | 打印实际注入的提示词(system + user + 各段尺寸),排查"模型到底看到了什么" |
|
|
162
|
+
解读历史默认保留最近 **20 条**,以明文存储在 `~/.dsh/selection-explain/history.json`。可在 **设置 → 数据** 中调整,设为 `0` 停止保存新历史;已有历史不会因此清空。
|
|
576
163
|
|
|
577
|
-
|
|
578
|
-
# 看某次解读到底喂了什么
|
|
579
|
-
node scripts/dump-prompt.mjs <sessionId> "<选中文字>"
|
|
580
|
-
node scripts/dump-prompt.mjs --no-session "<选中文字>" # 不带会话背景的对照组
|
|
581
|
-
```
|
|
164
|
+
支持 DSH 界面与可注入的侧边栏 HTML 预览。输入框、源码视图、外部站点浏览器标签页,以及部分第三方预览不触发划词(直接通过独立 URL 加载、无法注入划词桥的页面)。模型回答与联网查证仍可能出错。
|
|
582
165
|
|
|
583
|
-
|
|
166
|
+
<details>
|
|
167
|
+
<summary>开发与兼容性</summary>
|
|
584
168
|
|
|
585
|
-
|
|
169
|
+
依赖 DSH 的 `webServer`、`llm` 等宿主服务;具体版本范围见 [package.json](https://github.com/yfwu2020/dsh-selection-explain/blob/main/package.json)。
|
|
586
170
|
|
|
587
171
|
```bash
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
172
|
+
npm test
|
|
173
|
+
npm run typecheck
|
|
174
|
+
npm run build
|
|
591
175
|
```
|
|
592
176
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
npm 侧只需配一次:包设置 → **Trusted Publisher** → GitHub Actions,填 `yfwu2020` / `dsh-selection-explain` / `publish.yml`,并勾选允许 **`npm publish`**(默认只允许 `npm stage publish`)。
|
|
177
|
+
配置项见 [src/settings.ts](https://github.com/yfwu2020/dsh-selection-explain/blob/main/src/settings.ts),默认值见 [src/index.ts](https://github.com/yfwu2020/dsh-selection-explain/blob/main/src/index.ts)。
|
|
596
178
|
|
|
597
|
-
>
|
|
598
|
-
> npm 上这些包的 `latest` 还停在 `0.0.1-rc.x`,API 更旧(缺 `WebServer`、`createSystemMessage` 等),
|
|
599
|
-
> 用 `latest` 会编译失败——升级运行时时要同步改这里。
|
|
600
|
-
>
|
|
601
|
-
> **本机运行时通常比它新**(如 `0.1.7-rc.2`),两者 API 会漂移:例如 `createSystemMessage` 在
|
|
602
|
-
> `0.1.5-rc.3` 是 `(text, plugin)` 两个必填参数,在 `0.1.7-rc.x` 只剩 `(text)`。此时**别只按本机类型写**,
|
|
603
|
-
> 走一层宽松别名兼容两边(见 `src/index.ts` 的 `systemMessageOf`)——v0.4.0 就是漏了这一步,
|
|
604
|
-
> 本机构建全绿、CI 构建报 TS2554,整条发布卡在第一步(与 Trusted Publisher 无关)。
|
|
605
|
-
> 想提前发现:把仓库复制到临时目录、`npm install --no-save`(会装到 `0.1.5-rc.3`)再 `bash scripts/build.sh`。
|
|
179
|
+
</details>
|
|
606
180
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
```
|
|
610
|
-
src/index.ts host 半:SSE 路由 / 模型调用 / 会话背景 / 历史落盘 / 过滤器
|
|
611
|
-
src/prompts.ts 提示词与写作规则(纯字符串常量,无逻辑)
|
|
612
|
-
src/client/index.js client 半:划词浮标 + 面板 + 渲染器(手写 ModuleLoader bundle,无打包器)
|
|
613
|
-
scripts/ build.sh + 9 个离线测试 + 3 个浏览器冒烟 + dump-prompt + 演示图生成
|
|
614
|
-
skills/web-design/ 网页模式注入的设计规范(运行时读取)
|
|
615
|
-
assets/ README 的演示图(由 scripts/build-demo-pages.mjs 生成)
|
|
616
|
-
cordis.patch.yml 官方装配用的 bundle patch
|
|
617
|
-
lib/ 构建产物(已 gitignore,克隆后需 npm run build)
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
> **`docs/` 不在本仓库中**:开发期的资料(UI 比选实验台——浮标动效 / 模型选择器 / 折叠实验台 /
|
|
621
|
-
> 配色方案等,以及含本机路径与逐轮实测记录的内部开发文档)**仅保留在开发者本地**,已在 `.gitignore`
|
|
622
|
-
> 中整体排除,也从未进入 npm 包。它们记录的是"当初为什么这么设计",与插件运行无关。
|
|
623
|
-
|
|
624
|
-
## 已知限制
|
|
625
|
-
|
|
626
|
-
- 主会话(以及 DSH 界面本身)里只覆盖**同源页面内**的选中文字。
|
|
627
|
-
- 侧边栏 **HTML 文件预览**已覆盖(靠帧内桥,见上);但**内置浏览器标签页里的外部站点**(远端 URL 的 iframe,插件没有任何注入手段)与**源码视图**(没有 iframe)不触发。
|
|
628
|
-
- 预览帧被换成路由 URL 而非 `srcdoc`/`blob` 的第三方预览器不接管(不动别人的资源改写与生命周期)。
|
|
629
|
-
- 面板**不跟随页面滚动**(滚动即收起浮标 / 面板),符合「划完即看」的一次性使用预期;侧边栏网页内部滚动会重新上报位置,浮标跟着走。
|
|
630
|
-
- **小窗里的网页回答预览(iframe)内部**划不了词(不透明源帧,插件不桥自己的预览):想引用它就用气泡末尾的 `❝ 引用整条`,或切到「源码」视图再划。
|
|
631
|
-
- 结论来自当前模型,专业领域术语请以人工判断为准。
|
|
632
|
-
- 依赖 DSH 的 `webServer` 与 `llm` 服务;宿主版本过旧可能不兼容(peerDependencies 见 `package.json`)。
|
|
633
|
-
- **语音输入**还需要宿主的语音识别服务(`@deepseek-ai/dsh-experimental-speech-to-text` + 一个识别器,例如本机 SenseVoice)。没装 / 没准备模型时,麦克风按钮会明说原因,不会静默失败。识别器只收 **16kHz 单声道 PCM16 WAV**(插件在浏览器里按这个格式编码);一次最长 60 秒,**不做边录边出的流式识别**(说完再转,一次给全)。
|
|
634
|
-
|
|
635
|
-
## License
|
|
181
|
+
---
|
|
636
182
|
|
|
637
|
-
MIT
|
|
183
|
+
[MIT License](https://github.com/yfwu2020/dsh-selection-explain/blob/main/LICENSE) · [反馈问题](https://github.com/yfwu2020/dsh-selection-explain/issues)
|