@restregular/dsh-notepad 0.5.0 → 0.7.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
@@ -1,8 +1,8 @@
1
1
  # dsh-notepad
2
2
 
3
- 在 DeepSeek Harness 的会话界面里放一张**属于当前工作区**的分段便签浮窗:一行一个问题、一段一个卡片,回车就能新建下一段;需要时把某一段、或勾选后的多段一起插入 / 替换 / 发送到对话框。除了文字,也可以直接**粘贴或选择图片**,输入框里会用缩略芯片显示,插入 / 发送时按路径引用。0.4 起新分段置顶、点击序号选中、拖拽排序、合并多段;0.5 起空标题只在点击后显示输入框,上下方向键可切换分段,AI 优化只处理选中的分段且优化后按钮变为「撤销优化」,相同内容复用上次结果;自动刷新保持列表滚动位置,新增 `Ctrl/Cmd + Shift + 0` 复位浮窗位置和尺寸,浮窗也不会再越过窗口标题栏。
3
+ 在 DeepSeek Harness 的会话界面里放一张**属于当前工作区**的分段便签浮窗:一行一个问题、一段一个卡片,回车就能拆出下一段;需要时把某一段、或勾选后的多段一起插入 / 替换 / 发送到对话框。除了文字,也可以直接**粘贴或选择图片**,输入框里会用缩略芯片显示并可点击预览。0.4 起支持选中、拖拽排序、合并多段;0.5 起空标题只在点击后显示输入框,方向键可切换分段,AI 优化只处理选中的分段并支持撤销 / 缓存;0.6 起插入 / 发送按钮 hover 时展开成「当前会话 / 新会话」按钮组,图片可在浮窗内预览,底部快捷键提示点击轮换,复制全部排除归档,发送行为跟随「设置 > 通用设置」的繁忙发送策略;0.7 起分段改为从上到下、旧→新排列,回车拆分的新卡片紧跟原卡片,段首 Backspace 与上一段合并,空分段按 Backspace 或点删除会直接移除,插入 / 发送 / 归档+删除按钮组会稍候再展开以避免快速划过闪烁,文本达到最大高度才出现滚动条,复位快捷键改为 `Ctrl/Cmd + Shift + R`。
4
4
 
5
- > A per-workspace floating segmented notepad for DeepSeek Harness sessions. New segment cards lead the list; an empty title is a click-to-edit placeholder, arrow keys move between cards, and AI improvement only touches the selected cards — the button toggles to undo and unchanged content reuses the cached answer. Pending counts appear on the hidden launcher and the sidebar workspace row. Paste or pick images as in-input thumbnail chips; insert, replace, or send one segment or a multi-selection; sending archives the segment automatically. Auto refresh preserves the list scroll position, `Ctrl/Cmd + Shift + 0` resets the window geometry, the panel never covers the host title bar, and notes live in `<workspace>/.dsh/notepad.json` shared by every session from that workspace.
5
+ > A per-workspace floating segmented notepad for DeepSeek Harness sessions. Cards run oldest-to-newest; Enter splits after the source card and Backspace at the start merges into the previous card, while empty cards are removed by Backspace or the delete action without a prompt. An empty title is a blank click target, arrow keys move between cards, and AI improvement only touches the selected cards — the button toggles to undo and unchanged content reuses the cached answer. Hover groups wait briefly before opening: insert/send include current-session and new-session actions, archive+delete shares one group, and replace stays single. Image chips open an in-panel preview, the footer rotates shortcut tips, copy-all skips archived content, sending follows the General Settings busy-send policy, and the textarea only scrolls at its height limit. Auto refresh preserves the list scroll position, `Ctrl/Cmd + Shift + R` (or the header reset button) resets the geometry, the panel never covers the host title bar, and notes live in `<workspace>/.dsh/notepad.json` shared by every session from that workspace.
6
6
 
7
7
  ## 安装
8
8
 
@@ -16,39 +16,52 @@ dsh plugin --profile web add @restregular/dsh-notepad
16
16
 
17
17
  ```
18
18
  ┌────────────────────────────────────────────┐
19
- │ ● 记事本 [workspace] 复制 归档+ 折叠 最小化 │
19
+ │ ● 记事本 [workspace] 复位 复制 归档+ 折叠 最小化 │
20
20
  │ ☐ 全选 勾选分段可批量操作 ↙ ⇄ ➤ ⊕ 合并 ✦ 归档 │
21
21
  │ ┌────────────────────────────────────────┐ │
22
22
  │ │ ☐ 01 [标题] 🖼 ↙ ⇄ ➤ ✦ 归档 │
23
- │ │ 记录最新的一段…… │ │
23
+ │ │ 记录较早的一段…… │ │
24
24
  │ └────────────────────────────────────────┘ │
25
25
  │ ┌────────────────────────────────────────┐ │
26
26
  │ │ ☐ 02 [标题] 🖼 ↙ ⇄ ➤ ✦ 归档 │
27
- │ │ 记录上一段…… │ │
27
+ │ │ 记录最新的一段…… │ │
28
28
  │ └────────────────────────────────────────┘ │
29
29
  │ 已保存 · 42 字符 · 2 段 Enter 新建分段… │
30
30
  └────────────────────────────────────────────┘
31
31
  ```
32
32
 
33
- - **新建分段置顶**:标题栏 `+` 或分段末尾回车会在列表顶部创建新卡片;卡片从上到下保持最近在前,拖拽后按你的顺序保存。
34
- - **标题**:空标题只显示「分段标题(可选)」占位,没有 hover 底色;点击后才出现输入框。已有标题的卡片直接显示输入框,边输入边自动保存;在输入框里清空后不会立刻消失,失焦才回到占位状态。
33
+ - **排列方向**:卡片从上到下按旧分段到新分段排列;`+` 新建的分段追加在列表底部,拖拽后按你的顺序保存。
34
+ - **标题**:空标题不显示任何占位文字,只保留一块可点击的空白;点击后才出现输入框。已有标题的卡片直接显示输入框,边输入边自动保存;在输入框里清空后不会立刻消失,失焦才回到空白状态。
35
35
  - **序号**:显示当前顺序(`01`、`02`…),点击可选中 / 取消选中这一段,拖动序号可调整卡片顺序。
36
36
  - **AI 优化**:勾选分段后,顶部工具栏的 ✦ 只优化选中的分段;卡片工具栏的 ✦ 优化此段。优化成功后,刚点过的按钮变成「撤销优化」,再点一次恢复原文;相同且未改动的标题 + 内容再次优化时直接复用上次结果,不再调用大模型。
37
37
  - **方向键**:光标在分段首行按 `↑` 跳到上一张卡片末尾;在末行按 `↓` 跳到下一张卡片开头。多行分段内部仍按正常文本光标移动。
38
- - **复位浮窗**:`Ctrl / Cmd + Shift + 0` 把位置和尺寸恢复为右下角的默认 `420 × 460`,最小化 / 归档展开等状态不变。
38
+ - **按钮组**:插入、发送、归档+删除按钮在鼠标 hover 约 0.2 秒后才横向展开成按钮组,并用细圆角边框框起,快速划过不会闪烁:
39
+ - 插入 / 发送组:当前会话 + 在同一工作区新会话中执行;
40
+ - 归档+删除组:归档(主按钮)+ 直接删除(悬停后出现的危险按钮);
41
+ - 替换按钮保持单键。
42
+ - **回车拆分**:在分段中按回车会从光标处拆开,新卡片**紧跟原卡片下方**;在末尾回车就是在该段后面新建一个空分段。
43
+ - **Backspace 合并 / 删除**:光标在段首按 `Backspace` 会与上一段合并;如果当前段完全为空(无标题、无文字、无图片),则直接删除该段,不弹确认框。删除按钮对空分段同样跳过确认。
44
+ - **图片预览**:点击任意图片芯片会在浮窗内打开预览(带「打开原图」和关闭按钮),点击遮罩或按 `Esc` 关闭。
45
+ - **删除分段**:非空分段的删除按钮位于归档+删除按钮组的展开项中,点击后弹窗二次确认,并明确提示删除后无法恢复;确认后直接删除,不进入归档,也不提供撤销。
46
+ - **复制全部**:只复制未归档分段,归档内容不会混入结果。
47
+ - **快捷键提示**:底部一次只显示一条快捷键提示,点击它会轮换显示下一条。
48
+ - **发送行为**:发送按钮在智能体繁忙时使用「设置 > 通用设置 > 繁忙时的发送行为」中选择的排队 / 插话策略;空闲时按普通发送处理。
49
+ - **复位浮窗**:`Ctrl / Cmd + Shift + R` 或标题栏的复位按钮把位置和尺寸恢复为右下角的默认 `420 × 460`,最小化 / 归档展开等状态不变。
50
+ - **高度上限**:分段文本区最大 240px,只有内容确实超过该高度时才出现纵向滚动条;未到上限前不会预留滚动条。
39
51
  - **合并**:勾选 2 段以上后点顶部工具栏的合并按钮,合并结果留在最靠上的选中位置。
40
- - **回车 / Enter**:把当前分段从光标处拆开,创建一张新的分段卡片(光标前的文字留在原卡片,光标后的文字进入新卡片)。在末尾回车就是新建一个空分段并置顶。
41
- - **Shift + Enter**:分段内换行。
42
- - **Ctrl / Cmd + Enter**:把当前分段(有勾选时改为勾选的多段)插入对话框。
52
+ - **Enter**:拆分当前分段(见上);**Shift + Enter** 是分段内换行;**Ctrl / Cmd + Enter** 把当前分段(有勾选时改为勾选的多段)插入对话框。
43
53
  - 卡片上的 **↙** 插入此段;**⇄** 用此段替换整个对话框内容;**➤** 直接发送此段。
44
54
 
45
55
  ## 功能
46
56
 
47
57
  | 操作 | 入口 | 说明 |
48
58
  | --- | --- | --- |
49
- | 新建分段 | 标题栏 `+`,或分段末尾按回车 | 新卡片置顶并自动获得焦点 |
59
+ | 新建分段 | 标题栏 `+`,或分段末尾按回车 | 新卡片追加在列表底部(旧→新),并自动获得焦点 |
60
+ | 回车拆分 | 分段文本内按 `Enter` | 从光标处拆开,新卡片紧跟当前卡片下方 |
61
+ | Backspace 合并 | 光标在分段最前面按 `Backspace` | 与上一张卡片合并,光标停在新旧文字交界处 |
62
+ | 空分段删除 | 空分段内按 `Backspace`,或空分段的删除按钮 | 无标题、无文字、无图片时直接删除,不弹确认 |
50
63
  | 编辑分段 | 点击卡片文本区 | 停止输入约 0.5 秒后自动保存 |
51
- | 分段标题 | 空标题卡片的占位 / 已有标题的输入框 | 空标题点击后才出现输入框;停止输入约 0.5 秒后自动保存 |
64
+ | 分段标题 | 空标题的空白点击区 / 已有标题的输入框 | 空标题不显示文字和 hover 效果,点击后才出现输入框;停止输入约 0.5 秒后自动保存 |
52
65
  | 选中分段 | 点击左上角序号,或左侧复选框 | 序号与复选框状态同步;点击序号即可参与批量操作 |
53
66
  | 拖拽排序 | 按住左上角序号拖动到另一张卡片的上 / 下半区 | 松开后按新顺序保存,序号自动重排 |
54
67
  | 合并分段 | 勾选 2 段以上 → 顶部工具栏合并按钮 | 按当前显示顺序合并文字、标题和图片,可撤销 |
@@ -60,22 +73,28 @@ dsh plugin --profile web add @restregular/dsh-notepad
60
73
  | 添加图片(选择) | 卡片头部的图片按钮 | 桌面端直接用所选文件的宿主路径引入,不复制文件 |
61
74
  | 添加图片(粘贴) | 在分段文本区粘贴截图 / 图片 | 写入 `.dsh/notepad/images/`,再以该路径引入 |
62
75
  | 添加图片(拖入) | 把图片拖到分段文本区 | 优先使用宿主路径,浏览器拿不到路径时按粘贴方式保存 |
63
- | 图片芯片 | 分段输入框内 | 缩略图 + 文件名 + 移除按钮;点击缩略图打开原图 |
76
+ | 图片芯片 | 分段输入框内 | 缩略图 + 文件名 + 移除按钮;点击缩略图在浮窗内预览,可再打开原图 |
64
77
  | 带图插入 / 发送 | 与文字相同入口 | 图片以 `![名称](路径)` 追加在分段文字之后 |
65
78
  | 单段插入 | 卡片上的左下箭头 | 插入到当前会话输入框光标处,不发送 |
66
79
  | 多段插入 | 勾选分段 → 工具栏左下箭头,或全选后操作 | 按卡片顺序用空行合并后一次插入 |
67
- | 替换对话框 | 卡片上的 ⇄,或勾选后工具栏 ⇄ | 用该段 / 多段合并内容替换整个输入框 |
80
+ | 在新会话中插入 | hover 插入按钮片刻后出现的第二个按钮 | 在同一工作区新建会话并打开,内容填进它的输入框 |
81
+ | 替换对话框 | 卡片上的 ⇄,或勾选后工具栏 ⇄ | 用该段 / 多段合并内容替换整个输入框(没有按钮组) |
68
82
  | 单段发送 | 卡片上的纸飞机 | 直接发进当前会话;发送成功后自动归档该段 |
69
83
  | 多段发送 | 勾选分段 → 工具栏纸飞机 | 合并成一条消息发送;发送成功后自动归档这些段 |
84
+ | 在新会话中发送 | hover 发送按钮片刻后出现的第二个按钮 | 在同一工作区新建会话并发送,随后切换到该会话 |
70
85
  | 全选 | 列表上方「全选」复选框 | 一键选中所有未归档分段,再点一次取消 |
71
- | 归档 | 卡片上的归档图标,或工具栏归档图标 | 把分段移入归档区(不发送) |
86
+ | 归档 | 卡片上归档+删除按钮组的主按钮,或工具栏归档图标 | 把分段移入归档区(不发送) |
72
87
  | 显示 / 隐藏归档 | 标题栏归档图标 | 展开 / 收起归档区,状态按浏览器记忆 |
73
88
  | 恢复 | 归档卡片上的恢复图标 | 把归档分段放回未归档列表 |
74
- | 删除 | 归档卡片上的垃圾桶 | **只有已归档分段可以删除**,可撤销 |
89
+ | 删除(未归档) | 归档+删除按钮组中 hover 出现的垃圾桶 | 空分段直接删除;非空分段弹窗二次确认并提示 **删除后无法恢复** |
90
+ | 删除(已归档) | 归档卡片上的垃圾桶 | 删除归档分段,可撤销 |
75
91
  | 清空归档 | 归档区标题「清空归档」 | 批量删除归档,可撤销 |
76
- | 复制全部 | 标题栏复制按钮 | 复制所有分段(含归档),用空行合并 |
92
+ | 复制全部 | 标题栏复制按钮 | 只复制未归档分段,用空行合并;归档内容不包含在内 |
93
+ | 快捷键提示 | 底部的一条提示文字 | 点击轮换显示下一条快捷键说明 |
94
+ | 发送策略 | 卡片 / 工具栏发送按钮 | 繁忙时按「设置 > 通用设置 > 繁忙时的发送行为」选择排队或插话 |
95
+ | 滚动条 | 分段文本达到 240px 高度上限后 | 未到上限不显示纵向滚动条,超过后才出现 |
77
96
  | 调整尺寸 | 拖动右下角斜纹手柄 | 宽高按浏览器记忆,最小 320 × 240 |
78
- | 复位位置和尺寸 | `Ctrl/Cmd + Shift + 0` | 恢复默认 420 × 460 并停到右下角,其它状态不变 |
97
+ | 复位位置和尺寸 | `Ctrl/Cmd + Shift + R`,或标题栏复位按钮 | 恢复默认 420 × 460 并停到右下角,其它状态不变 |
79
98
  | 折叠 / 展开 | 标题栏上下折叠箭头 | 只保留标题栏,内容继续自动保存 |
80
99
  | 最小化 / 恢复 | 标题栏最小化图标,或 `Ctrl/Cmd + Shift + N` | 收成右下角小图标,带平滑过渡 |
81
100
  | 拖动位置 | 按住标题栏拖动 | 位置按浏览器记忆;不会越过 DSH 窗口最顶部的标题栏(最大化 / 最小化 / 关闭那一栏) |
@@ -94,8 +113,14 @@ dsh plugin --profile web add @restregular/dsh-notepad
94
113
 
95
114
  ```json
96
115
  {
97
- "version": 4,
116
+ "version": 5,
98
117
  "segments": [
118
+ {
119
+ "id": "seg-1",
120
+ "text": "遇到的问题\n下一步:补测试",
121
+ "title": "待办",
122
+ "createdAt": "2026-10-23T03:19:57.000Z"
123
+ },
99
124
  {
100
125
  "id": "seg-2",
101
126
  "text": "看这张报错截图",
@@ -103,23 +128,17 @@ dsh plugin --profile web add @restregular/dsh-notepad
103
128
  "createdAt": "2026-10-23T03:19:58.000Z",
104
129
  "images": [{ "path": ".dsh/notepad/images/m1x2-image.png", "name": "image.png" }]
105
130
  },
106
- {
107
- "id": "seg-1",
108
- "text": "遇到的问题\n下一步:补测试",
109
- "title": "待办",
110
- "createdAt": "2026-10-23T03:19:57.000Z"
111
- },
112
131
  { "id": "seg-3", "text": "已发送的分段", "archived": true, "archivedAt": "2026-10-23T03:20:00.000Z" }
113
132
  ],
114
- "text": "看这张报错截图\n![image.png](.dsh/notepad/images/m1x2-image.png)\n\n遇到的问题\n下一步:补测试\n\n已发送的分段",
133
+ "text": "遇到的问题\n下一步:补测试\n\n看这张报错截图\n![image.png](.dsh/notepad/images/m1x2-image.png)\n\n已发送的分段",
115
134
  "updatedAt": "2026-10-23T03:20:01.000Z"
116
135
  }
117
136
  ```
118
137
 
119
- `text` 是 `segments`(含图片引用)用空行合并后的副本,方便人工查看,也兼容旧版本。旧版单段文件(只有 `text`)、v2/v3 分段文件和手动改成纯文本的文件会在读取时迁移;没有 `createdAt` 的旧笔记会按「最近在前」反转一次显示顺序,之后按卡片数组顺序保存(拖拽后的顺序不会被自动重排)。`images[].path` 工作区内用相对路径,工作区外保留绝对路径;`name` 仅用于显示和 Markdown 替代文本。`title` 最长 200 字符。
138
+ `text` 是 `segments`(含图片引用)用空行合并后的副本,方便人工查看,也兼容旧版本。v5 起卡片按从旧到新、从上到下的顺序保存;读取到 v4(0.4–0.6)文件时,客户端会**反转一次**以迁移顺序,之后按卡片数组顺序保存(拖拽后的顺序不会被自动重排)。旧版单段文件(只有 `text`)、v2/v3 分段文件和手动改成纯文本的文件会在读取时迁移为分段;`images[].path` 工作区内用相对路径,工作区外保留绝对路径;`name` 仅用于显示和 Markdown 替代文本。`title` 最长 200 字符。
120
139
  - **粘贴的图片**保存在 `<工作区>/.dsh/notepad/images/`,由宿主接口原子写入,只接受 PNG / JPEG / WebP / GIF,单张最大 20 MB;笔记只保存路径,不内嵌 base64。**选择的图片**直接引用原路径,不复制。每段最多 20 张。
121
- - **删除图片芯片**会同时删除插件自己保存在 `.dsh/notepad/images/` 下的对应文件;引用工作区外原路径的图片只删引用,不碰原文件。删除已归档分段 / 清空归档时,未被其它分段引用的插件图片会在撤销窗口结束后自动清理。
122
- - 浮窗位置、尺寸、折叠 / 最小化 / 隐藏状态、归档区是否展开保存在浏览器 `localStorage` 的 `dsh-notepad.ui.v1` 中,按浏览器 / 设备生效,不写入工作区。位置会被限制在宿主标题栏下方(读取 DSH 的 `--dsh-frame-chrome-top` / `--dsh-frame-top-clearance`),`Ctrl/Cmd + Shift + 0` 可随时复位位置和尺寸。
140
+ - **删除图片芯片**会同时删除插件自己保存在 `.dsh/notepad/images/` 下的对应文件;引用工作区外原路径的图片只删引用,不碰原文件。删除已归档分段 / 清空归档 / 直接删除未归档分段时,未被其它分段引用的插件图片会在撤销窗口(未归档删除没有撤销窗口,按同样延迟)结束后自动清理。
141
+ - 浮窗位置、尺寸、折叠 / 最小化 / 隐藏状态、归档区是否展开保存在浏览器 `localStorage` 的 `dsh-notepad.ui.v1` 中,按浏览器 / 设备生效,不写入工作区。位置会被限制在宿主标题栏下方(读取 DSH 的 `--dsh-frame-chrome-top` / `--dsh-frame-top-clearance`),`Ctrl/Cmd + Shift + R` 或标题栏复位按钮可随时复位位置和尺寸。
123
142
  - 每个工作区一份笔记,最多 **200 段 / 512 KB**(单段输入框限制 16 万字符,每段最多 20 张图片)。浮窗内是纯文本编辑,不做 Markdown 渲染;图片以缩略芯片展示。
124
143
  - 浮窗只在**会话视图**出现:打开设置等非会话页面时自动隐藏,回到会话后恢复。
125
144
  - 写入走宿主同源回环路由 `/dsh-notepad/api/note`,粘贴图片走 `/dsh-notepad/api/image`,AI 优化走 `/dsh-notepad/api/optimize`,侧边栏角标计数走 `/dsh-notepad/api/unsent`;这些都只接受 127.0.0.1 的同源请求,并且只允许当前会话的 cwd 或已注册工作区。图片接口只会在固定的 `.dsh/notepad/images/` 下写入 / 删除合法的图片文件,不能用来读写或删除工作区里的其它文件。AI 优化使用当前默认模型,未配置模型时返回 `unavailable`,不会影响记事本的其它功能。
@@ -129,9 +148,10 @@ dsh plugin --profile web add @restregular/dsh-notepad
129
148
  ## 实现
130
149
 
131
150
  - **Host**(`index.js` + `lib/note-store.js` + `lib/image-store.js`):`ctx.inject(['webServer'])` 注册 `/dsh-notepad/api` 下的 `GET/PUT /note`、`POST/DELETE /image`、`GET /unsent` 与 `POST /optimize`,用临时文件 + rename 原子写入 `.dsh/notepad.json`;`GET` 返回 `segments`(含 `title` / `createdAt` / `images`)与合并后的 `text`,`PUT` 接受 `{ segments: [...] }`,也兼容旧的 `{ text }`。`POST /image` 校验 base64、MIME 与文件签名后写入 `.dsh/notepad/images/`;`DELETE /image` 只允许删除该目录下的普通文件。`/unsent` 为已注册工作区统计未发送段数;`/optimize` 先用 `workspace + title + text` 查进程内缓存,命中时返回 `{ cached: true }`,否则通过 `agentDefaultModel.currentSelection()` + `llm.prepareCall()` 调用当前默认模型,流式拼接文本后写入缓存并返回。
132
- - **Client**(`client.js`):整个 bundle 包在 IIFE 里;通过 `shell.overlay` 注册浮窗,读取 `uiSession.adapter.current` 得到当前会话与工作区。标题、序号、拖拽排序和合并都在本地分段数组上完成,数组顺序就是保存顺序;空标题用占位按钮渲染,点击后才挂载输入框,`↑` / `↓` 在首 / 末行时把焦点和光标移到相邻卡片。侧边栏角标用 `MutationObserver` 监听 `[data-row-key^="workspace:"]` 并把未发送数量写成 `data-np-unsent` 属性,由 CSS 伪元素显示。粘贴图片用 `FileReader` 读成 base64 交给宿主保存;选择 / 拖入图片优先通过桌面桥 `__DSH_HOST_PATHS__.pathFor(file)` 拿宿主路径。插入 / 替换通过会话绑定里的 `inputActions.captureInsertion()/insertText()` 与 `setDraft()`;发送优先走 `sessions.scope(id).conversation.send()`,并回退到 `sessions.binding(id).session.prompt()`。图片在插入 / 发送时以 Markdown 图片语法追加,聊天记录里的 DSH 渲染器会按工作区 cwd 解析这些路径。
151
+ - **Client**(`client.js`):整个 bundle 包在 IIFE 里;通过 `shell.overlay` 注册浮窗,读取 `uiSession.adapter.current` 得到当前会话与工作区。卡片按从旧到新排列,新建/拆分都追加在原卡片之后;光标在段首按 `Backspace` 时,空分段直接删除,非空分段与上一段合并(合并标题、文字与图片,并保持光标在交界处)。标题、序号、拖拽排序和合并都在本地分段数组上完成,数组顺序就是保存顺序;空标题用无内容的点击区渲染,点击后才挂载输入框,`↑` / `↓` 在首 / 末行时把焦点和光标移到相邻卡片。插入、发送、归档+删除使用同一个 `actionGroup()` 组件;按钮组用纯 CSS 延迟约 0.2 秒展开(收起也有短暂延迟),避免鼠标快速划过时闪烁。插入 / 发送组的新会话通过 `uiWorkspace.startSession()`(插入)或 `sessions.create()` + 会话发送(发送)完成;归档+删除组主按钮归档,悬停出现的危险按钮删除。文本区由 `autoGrow()` 在 26–240px 之间自适应,只有 `scrollHeight > 240` 时才把 `overflow-y` 切到 `auto`。图片芯片打开面板内的预览遮罩,支持打开原图与 `Esc` 关闭;非空删除走自定义二次确认,空分段跳过确认,确认后立即从分段数组移除并清理未被引用的图片。复制全部只合并未归档分段;底部快捷键提示是一个按钮,每次只渲染一条并通过 `HINT_KEYS` 轮换。侧边栏角标用 `MutationObserver` 监听 `[data-row-key^="workspace:"]` 并把未发送数量写成 `data-np-unsent` 属性,由 CSS 伪元素显示。粘贴图片用 `FileReader` 读成 base64 交给宿主保存;选择 / 拖入图片优先通过桌面桥 `__DSH_HOST_PATHS__.pathFor(file)` 拿宿主路径。插入 / 替换通过会话绑定里的 `inputActions.captureInsertion()/insertText()` 与 `setDraft()`;发送优先按繁忙策略选择 `sessions.scope(id).conversation.send()`(排队)或 `sessions.binding(id).session.prompt(..., 'steer')`(插话),steer 不可用时回退到排队。图片在插入 / 发送时以 Markdown 图片语法追加,聊天记录里的 DSH 渲染器会按工作区 cwd 解析这些路径。
133
152
  - AI 优化只改分段文字,不动标题和图片;单段优化后该卡片按钮变为「撤销优化」,批量优化后工具栏按钮变为「撤销优化」,撤销会恢复优化前的文字。优化结果按内容缓存在客户端与宿主两侧,相同内容再次优化不会重复请求;没有默认模型或模型调用失败时只提示错误,不影响笔记读写。
134
- - 浮窗坐标同时受视口和宿主标题栏约束:拖动、调整尺寸、窗口缩放和快捷键复位都走同一个 `clampUi()`,最小 `top` 取 `--dsh-frame-chrome-top`(全屏时为 0)/ `--dsh-frame-top-clearance` 再加 8px。
153
+ - 浮窗坐标同时受视口和宿主标题栏约束:拖动、调整尺寸、窗口缩放、标题栏复位按钮和快捷键复位都走同一个 `clampUi()`,最小 `top` 取 `--dsh-frame-chrome-top`(全屏时为 0)/ `--dsh-frame-top-clearance` 再加 8px。复位快捷键优先注册到 DSH 的 `shortcuts` 服务(默认 `Ctrl/Cmd + Shift + R`、`regions` 含 `editable`),注册失败时保留原始 keydown 监听,标题栏也有可点击的复位按钮。
154
+ - 发送模式与 DSH 输入框一致:读取 `sessions.list` 的 `running`,繁忙时读取 `configForms.get('ui-conversation')` 快照里的 `busyEnter`(`queue` / `steer`);空闲时始终排队。
135
155
  - 文案通过宿主 `locale` 服务注册中英文;找不到 locale 时按浏览器语言回退。
136
156
 
137
157
  ## 开发
@@ -155,14 +175,18 @@ node test/package.test.mjs
155
175
  npm test
156
176
  ```
157
177
 
158
- `test/client-smoke.mjs` 会真正执行 `apply(ctx)` 并渲染浮窗,覆盖:分段加载、编辑自动保存、自动刷新保持列表滚动位置、新分段置顶、空标题占位与点击后出现输入框、标题自动保存、序号选中、方向键跨分段切换光标、合并、单段 / 选中分段 AI 优化、优化后按钮切换为撤销、相同内容命中缓存、拖拽排序、未发送角标、侧边栏工作区角标、回车拆分、单段/多段插入与替换、单段/多段发送与自动归档、归档显示隐藏、归档恢复与删除(含撤销)、全选、折叠、最小化 / 恢复、拖动调整尺寸、标题栏下方位置限制、全局快捷键与复位快捷键、深色模式与字体 CSS、重复执行 bundle 的兼容性,以及粘贴 / 选择图片、图片芯片、删除芯片时清理磁盘文件与带图插入发送。
178
+ `test/client-smoke.mjs` 会真正执行 `apply(ctx)` 并渲染浮窗,覆盖:分段加载、编辑自动保存、自动刷新保持列表滚动位置、新分段追加到底部、空标题空白点击区与点击后出现输入框、标题自动保存、序号选中、方向键跨分段切换光标、段首 Backspace 合并上一段、空分段 Backspace / 删除按钮无确认删除、回车拆分后新卡片紧跟原卡片、文本区未到高度上限不显示滚动条、合并、单段 / 选中分段 AI 优化、优化后按钮切换为撤销、相同内容命中缓存、插入 / 发送 / 归档+删除 hover 按钮组(含延迟 CSS)与在新会话中插入 / 发送、图片面板内预览、非空删除的二次确认与不可撤销、底部快捷键轮换、复制全部排除归档、繁忙发送策略、拖拽排序、未发送角标、侧边栏工作区角标、单段/多段插入与替换、单段/多段发送与自动归档、归档显示隐藏、归档恢复与删除(含撤销)、全选、折叠、最小化 / 恢复、拖动调整尺寸、标题栏下方位置限制、全局快捷键与 `Ctrl+Shift+R` 复位、DSH `shortcuts` 命令注册(`KeyR`)、深色模式与字体 CSS、重复执行 bundle 的兼容性,以及粘贴 / 选择图片、图片芯片、删除芯片时清理磁盘文件与带图插入发送。
159
179
 
160
180
  ## 已知限制
161
181
 
162
- - 浮窗不会自动把内容推给 Agent;发送使用当前会话的排队发送,需要 Agent 可接收消息。
182
+ - 浮窗不会自动把内容推给 Agent;发送使用当前会话的发送接口,繁忙时按「设置 > 通用设置」的繁忙发送策略排队或插话。
163
183
  - 发送按钮依赖 DSH 的会话接口;接口不可用时降级为插入输入框并提示。
184
+ - 「在新会话中插入 / 发送」依赖 DSH 的 `uiWorkspace` / `sessions` 服务;不可用时插入会降级为复制,发送会提示失败。
185
+ - `Ctrl/Cmd + Shift + R` 在部分浏览器里是硬刷新快捷键;DSH 桌面端通过 `shortcuts` 先消费该组合,网页端如果仍被浏览器占用,可用标题栏的复位按钮。
164
186
  - 图片只支持 PNG / JPEG / WebP / GIF,单张最大 20 MB、每段最多 20 张;其它附件类型暂不支持。
165
187
  - AI 优化需要 DSH 里已配置默认模型;未配置时按钮会提示不可用,记笔记本身不受影响。优化缓存保存在宿主进程内,重启 DSH 或切换默认模型后,相同内容会再请求一次。
188
+ - 删除非空未归档分段不可撤销;确认后不会进入归档,只会在延迟窗口后清理未被其它分段引用的插件图片。空分段直接删除。
189
+ - 从 v4 笔记迁移到 v5 时会整体反转一次卡片顺序;如果你之前在 v4 里手动拖过顺序,这次反转会把它倒过来,之后按新顺序保存。
166
190
  - 删除图片芯片会删除插件目录里的文件;如果同一路径仍被其它分段引用,则保留文件。选择的原路径图片只删引用,不删除原图。
167
191
  - 网页版没有 `__DSH_HOST_PATHS__` 桥,选择 / 拖入的图片会像粘贴一样先复制到 `.dsh/notepad/images/`。
168
192
  - 图片在浮窗里用缩略芯片表示,不提供富文本行内图文混排;插入 / 发送后由聊天记录按 Markdown 渲染。