dsh-composer-ux 0.13.1 → 0.13.2
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/CHANGELOG.md +32 -0
- package/README.en.md +2 -1
- package/README.md +42 -5
- package/lib/client.js +94 -19
- package/lib/client.js.map +2 -2
- package/lib/index.js +18 -12
- package/lib/index.js.map +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
4
4
|
|
|
5
|
+
## [0.13.2] — 2026-09-30(✕ 改成「收起」:结果不再被删;另给一个点两次的「丢弃」;结果框不再像浮窗)
|
|
6
|
+
|
|
7
|
+
起因(用户原话):**「快捷指令面板和提示词优化面板在同一页面。现在它提示词优化是悬浮的特别是我点叉关闭之后居然恢复不了」**。
|
|
8
|
+
|
|
9
|
+
### 1. ✕ = 收起,不再是丢弃
|
|
10
|
+
|
|
11
|
+
0.12.0 起那个 ✕ 的语义是「丢弃」(tooltip 也这么写),用户真点了一次就发现**恢复不了** —— 一次真花了钱的
|
|
12
|
+
优化结果不该因为点一下 ✕ 就没了。现在:
|
|
13
|
+
|
|
14
|
+
- ✕ 只把结果**收起**(内容照旧在内存与磁盘上),面板切回「快捷指令」那半;
|
|
15
|
+
- 切换按钮会写「上次的结果在这儿」,点它就能再展开;
|
|
16
|
+
- **新跑一轮(✨ / 重新优化)自动展开**,不会被收起状态挡住;
|
|
17
|
+
- **收起状态跨重启记住**:状态信封加了可选的 `hidden` 字段,**不升版本号、向后兼容** ——
|
|
18
|
+
没收起时不写该字段(文件形状与 0.13.0/0.13.1 完全一致,降级回旧版也认得),老文件没有该字段 = 没收起。
|
|
19
|
+
|
|
20
|
+
### 2. 新增「丢弃」(点两次)
|
|
21
|
+
|
|
22
|
+
放在结果框标题行、✕ 左边,样式弱化:**第一次点只换成「点第二次丢弃」**(4 秒后自己复位),
|
|
23
|
+
第二次点才真丢(清状态 + 删磁盘那份)。与「插入输入框」覆盖确认是同一套做法,不弹模态框。
|
|
24
|
+
|
|
25
|
+
### 3. 结果框不再像"浮窗"
|
|
26
|
+
|
|
27
|
+
去掉它自己的背景与圆角边框(只留一条细分隔线)—— 用户说的「提示词优化是悬浮的」指的就是
|
|
28
|
+
面板里又叠了一张独立卡片。现在它看起来是面板里的一段。
|
|
29
|
+
|
|
30
|
+
### 内部
|
|
31
|
+
|
|
32
|
+
- 新增变异 5 条(EE ✕ 又变回丢弃 / EF 丢弃不真清 / EG 收起也抢面板 / EH 收起不存盘 / EI 恢复不读收起),
|
|
33
|
+
**137/137 全部咬住**;`optimize-state.mjs` 加 §5b(收起落盘/兼容/非布尔值防御),
|
|
34
|
+
`panel-split-render.mjs` 加 §6(收起时不渲染结果框本体、不抢面板、切换按钮提示正确)。
|
|
35
|
+
- 测试从 **2190 条** 涨到 **2214 条**(22 套件,全绿)。
|
|
36
|
+
|
|
5
37
|
## [0.13.1] — 2026-09-30(面板分两半:优化提示词 / 快捷指令二选一;修「展开后看不到 插入/重新优化/复制」)
|
|
6
38
|
|
|
7
39
|
起因(用户原话):**「现在是点击提示词优化按钮才能展开优化输入框,我想要点开快捷指令分为两个部分:一部分是优化提示词部分,一部分是快捷指令部分。」**
|
package/README.en.md
CHANGED
|
@@ -188,7 +188,8 @@ edited in settings; the JSON output contract is always appended and cannot be ov
|
|
|
188
188
|
| Read-only tool fence (including symlinks) / every cap / out-of-bounds rejection | `node test/optimize-tools.mjs` (66 checks against a **real temporary directory**) |
|
|
189
189
|
| Tool-loop message shape / round cap / exceptions must degrade | same file, section 6 (a fake `llm.stream` drives the whole loop; `role:'tool'` and `toolCallId` are asserted) |
|
|
190
190
|
| Tool path: off by default, fires only when enabled, **prose after lookups falls back**, no cwd ⇒ no tool at all | `test/quick-commands.mjs` 5g (13 checks) |
|
|
191
|
-
| Mutation guards really bite, and **the *named* test must be the one going red** | `npm run test:mutations`: **
|
|
191
|
+
| Mutation guards really bite, and **the *named* test must be the one going red** | `npm run test:mutations`: **137/137 bite** |
|
|
192
|
+
| 0.13.2: `✕` collapses (result kept) + a two-click 丢弃 | `node test/panel-split-render.mjs` §6, mutations EE–EI |
|
|
192
193
|
| Panel split + pinned result-box buttons (0.13.1) | `node test/panel-split-render.mjs` (20 checks, real `react-dom/server` render) |
|
|
193
194
|
| Release gates | `npm run gates` (tag target / packed file list / docs drift) |
|
|
194
195
|
| Three-platform CI | GitHub Actions: ubuntu / windows / macos × Node 20 |
|
package/README.md
CHANGED
|
@@ -134,7 +134,7 @@ dsh plugin --profile web add <你克隆或解压出来的目录>
|
|
|
134
134
|
- **逐条流式,而且每条都已通过校验**:宿主边收边扫,把**已经闭合、且引文在你原话里逐字存在**的条目一条条推过来;对不上的走 `dropped` 如实记账。所以框里出现的每一条都是"成品里可能出现的那条",不会先闪一下再消失。成品要等整段输出到齐、逐条校验装配完才存在,由最后的 `done` 一次给出。
|
|
135
135
|
- **「插入输入框」由你决定何时写回**:覆盖全文(`Ctrl+Z` 可还原),斜杠命令前缀(`/goal`)原样拼回。**若你在优化期间改过输入框**,第一次点只提示、第二次点才覆盖 —— 不静默吃掉你刚写的内容。
|
|
136
136
|
- **「重新优化」= 用框里那一轮的原文再跑一轮**(不是拿输入框现在的内文:那时你多半已经把成品插进去了);你手改过框里的成品时会先提示一次。
|
|
137
|
-
- **「取消」/ `Esc` 在跑的时候 = 中止并保留已生成的部分**:条目流水留着,成品如实留空(取消时它本来就不存在)。空闲时 `Esc` =
|
|
137
|
+
- **「取消」/ `Esc` 在跑的时候 = 中止并保留已生成的部分**:条目流水留着,成品如实留空(取消时它本来就不存在)。空闲时 `Esc` = 关面板;结果框的「✕」**只收起**(0.13.2 起结果留着,点面板那一半就能再展开);输入框里已插入的内容不受影响;**面板关掉不会清空结果框** —— 一次优化是真花了钱的,不该因为你点了一下别处就没了。
|
|
138
138
|
- **0.12.0 起默认携带会话上下文**(设置里可关):优化时会把当前会话最近的往来
|
|
139
139
|
(**你的话与工作 AI 的话各取最近 4 条**、共约 1600 字)一并交给模型 —— "它那个也顺手改一下"
|
|
140
140
|
这种离开上文就没意义的短句,只有带上上文才能猜对"它"指谁。三条边界写死在提示词里:
|
|
@@ -151,6 +151,40 @@ dsh plugin --profile web add <你克隆或解压出来的目录>
|
|
|
151
151
|
(条目多 + 成品多行)就把列表挤没、再把自己底部的**「插入输入框 / 重新优化 / 复制」顶出面板边界被裁掉**。
|
|
152
152
|
现在两半互斥、各自内部滚,而且结果框拆成「可滚内容区 + **钉在底部的按钮行**」—— 面板再矮也不裁那排按钮。
|
|
153
153
|
- **优化只从「快捷指令」面板进**(0.12.0 起):工具行只有一枚「快捷指令」按钮,点开面板再点「✨ 优化提示词」。
|
|
154
|
+
|
|
155
|
+
#### 0.13.2:✕ = 收起(结果留着),另给一个「丢弃」
|
|
156
|
+
|
|
157
|
+
> 起因(用户原话):**「现在它提示词优化是悬浮的特别是我点叉关闭之后居然恢复不了」**。
|
|
158
|
+
|
|
159
|
+
- **✕ 改成「收起」**:结果不再被删,只是标记成"先别显示";面板切回「快捷指令」那半,切换按钮写
|
|
160
|
+
「上次的结果在这儿」,点它就能再展开(按钮 tooltip 明确写「结果留着」)。
|
|
161
|
+
- **新增「丢弃」**(结果框标题行里、✕ 左边,样式弱化):**点两次才真丢**(第一次换成「点第二次丢弃」,
|
|
162
|
+
4 秒后自己复位),丢掉时才清状态 + 删磁盘那份。
|
|
163
|
+
- **收起状态跨重启记住**(用户选的 A):状态信封加了一个可选的 `hidden`,**不升版本号** ——
|
|
164
|
+
没收起时不写这个字段,文件形状与 0.13.0/0.13.1 完全一致,降级回旧版也认得;老文件没有该字段 = 没收起。
|
|
165
|
+
- **新跑一轮(✨ / 重新优化)会自动把框显示出来**,不会被收起状态挡住。
|
|
166
|
+
- **结果框不再画成一张独立卡片**(去掉自己的背景与圆角边框,只留一条细分隔线)——
|
|
167
|
+
这就是用户说的"悬浮"观感:它现在看起来是面板里的一段,而不是浮在上面的小窗。
|
|
168
|
+
|
|
169
|
+
#### 实机(0.13.1,2026-09-30 重启后当场核过)
|
|
170
|
+
|
|
171
|
+
**① 中部是「快捷指令」那半(默认)**:分类标签 + 条目列表 + 底部计数;标题行始终有「快捷指令 + 普通/高级/极端 + ✨ 优化提示词」,
|
|
172
|
+
中间的切换按钮是 `▸ 优化提示词`,右侧写明另一半的状态(这里写的是「上次的结果在这儿」):
|
|
173
|
+
|
|
174
|
+

|
|
175
|
+
|
|
176
|
+
**② 点切换按钮 → 换成「优化提示词」那半**(列表收起,`▾` + 「点此回到快捷指令」):结果框里是条目流水、可编辑成品与记账行,
|
|
177
|
+
**底部那排「插入输入框 / 重新优化 / 复制」完整可见** —— 这正是 0.13.1 修的 bug:以前结果框一长,这排按钮会被面板裁掉:
|
|
178
|
+
|
|
179
|
+

|
|
180
|
+
|
|
181
|
+
**③ 面板已经到了自己的高度上限(约 60vh),按钮依然在**:成品输入框右侧出现自己的滚动条,内容区滚而按钮不动 ——
|
|
182
|
+
这就是"两半互斥 + 结果框拆成可滚区 + 钉底按钮行"的现场效果:
|
|
183
|
+
|
|
184
|
+

|
|
185
|
+
|
|
186
|
+
> 说明:面板里那份结果是**用户自己跑的一次优化**(`go/deepseek-flash`,用时 9.7 秒,7 条补全),不是我为了截图新花的调用;
|
|
187
|
+
> 截图时也没有改动它(没点「插入输入框」、没点 ✕,只切换了两半、开关了面板)。
|
|
154
188
|
0.11.1 曾试过在工具行加一枚独立的「✨ 优化」按钮(点一下 = 开面板 + 立刻开跑),用户看到实机后明确否掉 ——
|
|
155
189
|
「我让你集合到快捷指令里的提示词优化按钮里」,于是 0.12.0 删掉它,回到单一入口。
|
|
156
190
|
- **秒表**:面板主按钮与结果框都显示已等待秒数,结束时报「用时 x.x 秒」。**边界如实说**:阶段只能到"等待模型响应" —— 更细的进度由逐条流水本身给出(它就是"它在干活"的证据)。
|
|
@@ -466,7 +500,7 @@ Windows 上 DSH 给模型的终端工具是 **PowerShell**(工具名 `pwsh`)
|
|
|
466
500
|
| 工具循环的消息形状 / 轮次封顶 / 异常必须降级 | 同上第 6 节(假 llm 驱动整条循环,逐条检查 `role:'tool'` 与 `toolCallId`) |
|
|
467
501
|
| 工具路径:默认关、开了才派、**查完吐散文必须回落**、拿不到工作目录就不派 | `test/quick-commands.mjs` 5g 节(13 条,真路由 + 假 llm) |
|
|
468
502
|
| 工具读到的内容不进台账 | 同 5g 节:断言台账里只有工具名与次数、没有读到的文件内容 |
|
|
469
|
-
| 变异守卫真的会咬人,且**必须是指定的那条测试变红** | `npm run test:mutations`:**
|
|
503
|
+
| 变异守卫真的会咬人,且**必须是指定的那条测试变红** | `npm run test:mutations`:**137 条全部咬住**(判据是 `red.some(line => line.includes(item.expect))`,不是"有红就行") |
|
|
470
504
|
| 发布门禁三条判据 | `npm run gates` 全过;`check:tag` 实测 `v0.12.0` 的 tag 名与所指提交里的版本一致 |
|
|
471
505
|
| 三平台 CI | GitHub Actions:ubuntu / windows / macos × Node 20(`npm ci → typecheck → build → test → check:pack → check:docs`) |
|
|
472
506
|
|
|
@@ -475,7 +509,10 @@ Windows 上 DSH 给模型的终端工具是 **PowerShell**(工具名 `pwsh`)
|
|
|
475
509
|
| 缺口 | 说明 |
|
|
476
510
|
|---|---|
|
|
477
511
|
| **效果(最重要的一条)** | 我们**没有任何受控评估** —— 没有留出集、没有 A/B、没有成本基线。"这样优化之后模型做得更好"在本仓里没有一条证据,只是设计意图。上游花钱做过一次,结论是"没测出差别",所以这一格必须留空。 |
|
|
478
|
-
| **0.13.0 的真机(实机)行为** | ①重启恢复、②台账落盘、⑤只读工具这三块**只在测试与假 llm 里跑过**,没有在真实 DSH
|
|
512
|
+
| **0.13.0 的真机(实机)行为** | ①重启恢复、②台账落盘、⑤只读工具这三块**只在测试与假 llm 里跑过**,没有在真实 DSH 里点过(0.12.0 那 5 张截图也不覆盖新功能)。 |
|
|
513
|
+
| 0.13.2 的「✕ = 收起 / 丢弃 / 收起跨重启」 | `test/panel-split-render.mjs` 第 6 节(**真渲染**:收起时不渲染结果框本体、自动切换不抢面板)+ 变异 EE/EF/EG/EH/EI → **真机待重启后验**(本人 2026-09-30 报的就是这条) |
|
|
514
|
+
| 0.13.1 的面板分半 | ✅ **有真机证据**:2026-09-30 08:14 重启后当场验过「默认哪半、切换按钮换半、跑完自动切到优化半、**到了高度上限按钮仍在**」
|
|
515
|
+
(三张截图见上面「实机(0.13.1)」);但**没验**"没有结果框时首次打开"的默认态(那要开一次干净的会话),这条只有渲染测试罩着。 |
|
|
479
516
|
| 只读工具的收益与成本 | 真实项目里派工具能省多少来回、多花多少 token 与时间,**没有数据**。默认关闭正是因为这份不确定。 |
|
|
480
517
|
| "工具内容不会被当成引文"在真实模型上的表现 | 结构上成立(`quote` 必须逐字命中用户原话,否则逐条丢弃,有测试)。但模型会不会因此产出更差的结果,**没测**。 |
|
|
481
518
|
| **原子写的原子性本身** | 单线程测试观测不到"写到一半";只有"不留临时文件"这条卫生断言,**没有变异证明**(已在提交信息里写明,不假装验过)。 |
|
|
@@ -596,9 +633,9 @@ node build.mjs # 产出 lib/index.js + lib/client.js
|
|
|
596
633
|
# ⚠️ 宿主半会**内联** schemastery / cosmokit:
|
|
597
634
|
# 优先用 DSH 检出里的 vendor 副本,检出不在时退到 node_modules
|
|
598
635
|
# 里同版本的 npm 包(两者逐字节相同)——CI 上走的就是退路
|
|
599
|
-
npm test # 22 个套件;当前
|
|
636
|
+
npm test # 22 个套件;当前 2214 passed, 0 failed(2026-09-29 实测;CI 三平台同样全绿)
|
|
600
637
|
# 走 scripts/run-tests.mjs:顺带把"多少套件/多少条"记进 test/.last-run.json
|
|
601
|
-
npm run test:mutations # 手动跑:变异测试,证明那套护栏真的在咬人(
|
|
638
|
+
npm run test:mutations # 手动跑:变异测试,证明那套护栏真的在咬人(137 条,须单独跑)
|
|
602
639
|
# 同样记录结果,供下面的文档门禁核对
|
|
603
640
|
npm run gates # 发版门禁三条一起跑:tag 指向 / 包内容 / 文档数字
|
|
604
641
|
npm run check:tag # ① tag 名里的版本 == 该 tag 所指提交里的 package.json 版本
|
package/lib/client.js
CHANGED
|
@@ -2902,10 +2902,10 @@ var dockBox = {
|
|
|
2902
2902
|
display: "flex",
|
|
2903
2903
|
flexDirection: "column",
|
|
2904
2904
|
gap: 6,
|
|
2905
|
-
padding: "8px
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2905
|
+
padding: "8px 12px 8px",
|
|
2906
|
+
// 0.13.2:**不再画成一张独立卡片**(用户原话「提示词优化是悬浮的」)。
|
|
2907
|
+
// 去掉自己的背景与圆角边框,只留一条细分隔线 —— 它就是面板里的一段,而不是浮在上面的小窗。
|
|
2908
|
+
borderTop: "0.5px solid var(--dsw-alias-border-l2)"
|
|
2909
2909
|
};
|
|
2910
2910
|
var dockBody = {
|
|
2911
2911
|
flex: "1 1 auto",
|
|
@@ -2918,6 +2918,22 @@ var dockBody = {
|
|
|
2918
2918
|
var dockButtonsPinned = {
|
|
2919
2919
|
flex: "0 0 auto"
|
|
2920
2920
|
};
|
|
2921
|
+
var dockButtonGhost = {
|
|
2922
|
+
border: 0,
|
|
2923
|
+
background: "transparent",
|
|
2924
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
2925
|
+
borderRadius: 999,
|
|
2926
|
+
padding: "3px 8px",
|
|
2927
|
+
fontSize: 11,
|
|
2928
|
+
lineHeight: 1.6,
|
|
2929
|
+
cursor: "pointer",
|
|
2930
|
+
whiteSpace: "nowrap"
|
|
2931
|
+
};
|
|
2932
|
+
var dockButtonWarn = {
|
|
2933
|
+
...dockButtonGhost,
|
|
2934
|
+
color: "var(--dsw-alias-label-error, #d33)",
|
|
2935
|
+
border: "0.5px solid var(--dsw-alias-label-error, #d33)"
|
|
2936
|
+
};
|
|
2921
2937
|
var dockHead = {
|
|
2922
2938
|
display: "flex",
|
|
2923
2939
|
alignItems: "center",
|
|
@@ -6537,6 +6553,7 @@ async function copyText(text) {
|
|
|
6537
6553
|
}
|
|
6538
6554
|
function OptimizeDock({ state: state2, seconds, actions }) {
|
|
6539
6555
|
const [copied, setCopied] = (0, import_react13.useState)(false);
|
|
6556
|
+
const [discardArmed, setDiscardArmed] = (0, import_react13.useState)(false);
|
|
6540
6557
|
const running = state2.phase === "running";
|
|
6541
6558
|
const hasText = state2.text.trim() !== "";
|
|
6542
6559
|
const canInsert = state2.phase === "done" && hasText;
|
|
@@ -6553,6 +6570,29 @@ function OptimizeDock({ state: state2, seconds, actions }) {
|
|
|
6553
6570
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: dockHead, children: [
|
|
6554
6571
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: dockPhase, role: "status", children: dockPhaseText(state2) }),
|
|
6555
6572
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: dockMeta, children: running ? `\u5DF2\u7B49\u5F85 ${String(seconds)}s` : state2.elapsedMs > 0 ? `\u7528\u65F6 ${(state2.elapsedMs / 1e3).toFixed(1)} \u79D2` : "" }),
|
|
6573
|
+
!running && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
6574
|
+
"button",
|
|
6575
|
+
{
|
|
6576
|
+
type: "button",
|
|
6577
|
+
style: discardArmed ? dockButtonWarn : dockButtonGhost,
|
|
6578
|
+
title: discardArmed ? "\u518D\u70B9\u4E00\u6B21\u5C31\u771F\u7684\u4E22\u6389\u8FD9\u4EFD\u7ED3\u679C\uFF08\u4E0D\u53EF\u6062\u590D\uFF09" : "\u4E22\u5F03\u8FD9\u4EFD\u7ED3\u679C\uFF08\u4E0D\u53EF\u6062\u590D\uFF1B\u53EA\u60F3\u6536\u8D77\u6765\u5C31\u70B9\u53F3\u8FB9\u7684 \u2715\uFF09",
|
|
6579
|
+
onMouseDown: (event) => {
|
|
6580
|
+
event.preventDefault();
|
|
6581
|
+
},
|
|
6582
|
+
onClick: () => {
|
|
6583
|
+
if (!discardArmed) {
|
|
6584
|
+
setDiscardArmed(true);
|
|
6585
|
+
setTimeout(() => {
|
|
6586
|
+
setDiscardArmed(false);
|
|
6587
|
+
}, 4e3);
|
|
6588
|
+
return;
|
|
6589
|
+
}
|
|
6590
|
+
setDiscardArmed(false);
|
|
6591
|
+
actions.discard();
|
|
6592
|
+
},
|
|
6593
|
+
children: discardArmed ? "\u70B9\u7B2C\u4E8C\u6B21\u4E22\u5F03" : "\u4E22\u5F03"
|
|
6594
|
+
}
|
|
6595
|
+
),
|
|
6556
6596
|
running ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
6557
6597
|
"button",
|
|
6558
6598
|
{
|
|
@@ -6570,8 +6610,8 @@ function OptimizeDock({ state: state2, seconds, actions }) {
|
|
|
6570
6610
|
{
|
|
6571
6611
|
type: "button",
|
|
6572
6612
|
style: dockButton,
|
|
6573
|
-
"aria-label": "\
|
|
6574
|
-
title: "\u6536\u8D77\u7ED3\u679C\u6846\
|
|
6613
|
+
"aria-label": "\u6536\u8D77\u7ED3\u679C\u6846",
|
|
6614
|
+
title: "\u6536\u8D77\u7ED3\u679C\u6846\uFF080.13.2 \u8D77\u53EA\u662F\u6536\u8D77\uFF1A\u7ED3\u679C\u7559\u7740\uFF0C\u70B9\u300C\u4F18\u5316\u63D0\u793A\u8BCD\u300D\u90A3\u4E00\u534A\u5C31\u80FD\u518D\u770B\u5230\uFF1B\u60F3\u5220\u6389\u7528\u65C1\u8FB9\u7684\u300C\u4E22\u5F03\u300D\uFF09",
|
|
6575
6615
|
onMouseDown: (event) => {
|
|
6576
6616
|
event.preventDefault();
|
|
6577
6617
|
},
|
|
@@ -6663,6 +6703,7 @@ function QuickCommandsPanel({
|
|
|
6663
6703
|
useBook,
|
|
6664
6704
|
useBookStatus,
|
|
6665
6705
|
useDock,
|
|
6706
|
+
useDockHidden,
|
|
6666
6707
|
actions
|
|
6667
6708
|
}) {
|
|
6668
6709
|
const settings = useLive((item) => item);
|
|
@@ -6673,6 +6714,7 @@ function QuickCommandsPanel({
|
|
|
6673
6714
|
const book = useBook((item) => item);
|
|
6674
6715
|
const status = useBookStatus((item) => item);
|
|
6675
6716
|
const dock = useDock((item) => item);
|
|
6717
|
+
const dockHidden = useDockHidden((item) => item);
|
|
6676
6718
|
const ref = (0, import_react14.useRef)(null);
|
|
6677
6719
|
const [position, setPosition] = (0, import_react14.useState)(null);
|
|
6678
6720
|
const [activeId, setActiveId] = (0, import_react14.useState)("");
|
|
@@ -6723,10 +6765,11 @@ function QuickCommandsPanel({
|
|
|
6723
6765
|
window.removeEventListener("resize", actions.close);
|
|
6724
6766
|
};
|
|
6725
6767
|
}, [anchor === null, actions, dockRunning]);
|
|
6768
|
+
const dockVisible = dockPresent && !dockHidden;
|
|
6726
6769
|
const prevDockPresent = (0, import_react14.useRef)(false);
|
|
6727
|
-
if (prevDockPresent.current !==
|
|
6728
|
-
prevDockPresent.current =
|
|
6729
|
-
const next = sectionForDock(
|
|
6770
|
+
if (prevDockPresent.current !== dockVisible) {
|
|
6771
|
+
prevDockPresent.current = dockVisible;
|
|
6772
|
+
const next = sectionForDock(dockVisible, section);
|
|
6730
6773
|
if (next !== section) setSection(next);
|
|
6731
6774
|
}
|
|
6732
6775
|
if (anchor === null) return null;
|
|
@@ -6793,7 +6836,9 @@ function QuickCommandsPanel({
|
|
|
6793
6836
|
event.preventDefault();
|
|
6794
6837
|
},
|
|
6795
6838
|
onClick: () => {
|
|
6796
|
-
|
|
6839
|
+
const next = toggleSection(section);
|
|
6840
|
+
if (next === "optimize") actions.dockShow();
|
|
6841
|
+
setSection(next);
|
|
6797
6842
|
},
|
|
6798
6843
|
children: [
|
|
6799
6844
|
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: quickSectionToggleArrow, "aria-hidden": true, children: section === "optimize" ? "\u25BE" : "\u25B8" }),
|
|
@@ -6817,6 +6862,10 @@ function QuickCommandsPanel({
|
|
|
6817
6862
|
actions.dockClose();
|
|
6818
6863
|
setSection(DEFAULT_PANEL_SECTION);
|
|
6819
6864
|
},
|
|
6865
|
+
discard: () => {
|
|
6866
|
+
actions.dockDiscard();
|
|
6867
|
+
setSection(DEFAULT_PANEL_SECTION);
|
|
6868
|
+
},
|
|
6820
6869
|
edit: actions.dockEdit
|
|
6821
6870
|
}
|
|
6822
6871
|
}
|
|
@@ -6953,12 +7002,13 @@ async function request(method, body) {
|
|
|
6953
7002
|
try {
|
|
6954
7003
|
parsed = JSON.parse(raw);
|
|
6955
7004
|
} catch {
|
|
6956
|
-
return { ok: false, state: null, keep: true, error: `\u670D\u52A1\u7AEF\u8FD4\u56DE\u7684\u4E0D\u662F JSON\uFF08HTTP ${String(response.status)}\uFF09` };
|
|
7005
|
+
return { ok: false, state: null, hidden: false, keep: true, error: `\u670D\u52A1\u7AEF\u8FD4\u56DE\u7684\u4E0D\u662F JSON\uFF08HTTP ${String(response.status)}\uFF09` };
|
|
6957
7006
|
}
|
|
6958
7007
|
const row2 = typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
6959
7008
|
return {
|
|
6960
7009
|
ok: row2.ok === true,
|
|
6961
7010
|
state: sanitizeDockSnapshot(row2.state),
|
|
7011
|
+
hidden: row2.hidden === true,
|
|
6962
7012
|
keep: row2.keep !== false,
|
|
6963
7013
|
...textOf(row2.file) === void 0 ? {} : { file: textOf(row2.file) },
|
|
6964
7014
|
...textOf(row2.error) === void 0 ? {} : { error: textOf(row2.error) },
|
|
@@ -6966,14 +7016,14 @@ async function request(method, body) {
|
|
|
6966
7016
|
...row2.tooBig === true ? { tooBig: true } : {}
|
|
6967
7017
|
};
|
|
6968
7018
|
} catch (error) {
|
|
6969
|
-
return { ok: false, state: null, keep: true, error: error instanceof Error ? error.message : String(error) };
|
|
7019
|
+
return { ok: false, state: null, hidden: false, keep: true, error: error instanceof Error ? error.message : String(error) };
|
|
6970
7020
|
}
|
|
6971
7021
|
}
|
|
6972
7022
|
function loadDockState() {
|
|
6973
7023
|
return request("GET");
|
|
6974
7024
|
}
|
|
6975
|
-
function saveDockState(state2) {
|
|
6976
|
-
return request("POST", { state: state2 });
|
|
7025
|
+
function saveDockState(state2, hidden = false) {
|
|
7026
|
+
return request("POST", { state: state2, hidden: hidden === true });
|
|
6977
7027
|
}
|
|
6978
7028
|
function clearDockStateOnHost() {
|
|
6979
7029
|
return saveDockState(null);
|
|
@@ -6998,11 +7048,12 @@ function apply(ctx) {
|
|
|
6998
7048
|
const optimizing = (0, import_dsh_client_store.createSnapshotStore)(false);
|
|
6999
7049
|
const optimizeStartedAt = (0, import_dsh_client_store.createSnapshotStore)(0);
|
|
7000
7050
|
const dock = (0, import_dsh_client_store.createSnapshotStore)(null);
|
|
7051
|
+
const dockHidden = (0, import_dsh_client_store.createSnapshotStore)(false);
|
|
7001
7052
|
const keepDockOn = () => live.getSnapshot().optimizeKeepDock === true;
|
|
7002
7053
|
const DOCK_SAVE_DEBOUNCE_MS = 1200;
|
|
7003
7054
|
const persistDock = () => {
|
|
7004
7055
|
if (!keepDockOn()) return;
|
|
7005
|
-
void saveDockState(dock.getSnapshot());
|
|
7056
|
+
void saveDockState(dock.getSnapshot(), dockHidden.getSnapshot());
|
|
7006
7057
|
};
|
|
7007
7058
|
const clearPersistedDock = () => {
|
|
7008
7059
|
void clearDockStateOnHost();
|
|
@@ -7030,6 +7081,7 @@ function apply(ctx) {
|
|
|
7030
7081
|
let alive = true;
|
|
7031
7082
|
void loadDockState().then((reply) => {
|
|
7032
7083
|
if (!alive || !reply.ok || reply.state === null) return;
|
|
7084
|
+
dockHidden.set(reply.hidden === true);
|
|
7033
7085
|
if (dock.getSnapshot() === null) dock.set(dockReducer(null, { type: "restore", snapshot: reply.state }));
|
|
7034
7086
|
});
|
|
7035
7087
|
return () => {
|
|
@@ -7038,16 +7090,19 @@ function apply(ctx) {
|
|
|
7038
7090
|
}, "composer-ux: restore optimize dock");
|
|
7039
7091
|
ctx.effect(() => {
|
|
7040
7092
|
let timer = null;
|
|
7041
|
-
const
|
|
7093
|
+
const schedule = () => {
|
|
7042
7094
|
if (timer !== null) clearTimeout(timer);
|
|
7043
7095
|
timer = setTimeout(() => {
|
|
7044
7096
|
timer = null;
|
|
7045
7097
|
persistDock();
|
|
7046
7098
|
}, DOCK_SAVE_DEBOUNCE_MS);
|
|
7047
|
-
}
|
|
7099
|
+
};
|
|
7100
|
+
const unsubscribe = dock.subscribe(schedule);
|
|
7101
|
+
const unsubscribeHidden = dockHidden.subscribe(schedule);
|
|
7048
7102
|
return () => {
|
|
7049
7103
|
if (timer !== null) clearTimeout(timer);
|
|
7050
7104
|
unsubscribe();
|
|
7105
|
+
unsubscribeHidden();
|
|
7051
7106
|
};
|
|
7052
7107
|
}, "composer-ux: persist optimize dock");
|
|
7053
7108
|
};
|
|
@@ -7256,6 +7311,7 @@ function apply(ctx) {
|
|
|
7256
7311
|
optimizeAbort?.abort();
|
|
7257
7312
|
const controller = new AbortController();
|
|
7258
7313
|
optimizeAbort = controller;
|
|
7314
|
+
dockHidden.set(false);
|
|
7259
7315
|
dock.set(dockReducer(dock.getSnapshot(), {
|
|
7260
7316
|
type: "start",
|
|
7261
7317
|
source: input.source,
|
|
@@ -7382,9 +7438,25 @@ function apply(ctx) {
|
|
|
7382
7438
|
dock.set(dockReducer(state2, { type: "cancel", at: Date.now() }));
|
|
7383
7439
|
note2("\u5DF2\u53D6\u6D88\u8FD9\u4E00\u8F6E\uFF1A\u4E0A\u9762\u662F\u5DF2\u7ECF\u751F\u6210\u7684\u90E8\u5206");
|
|
7384
7440
|
},
|
|
7385
|
-
/**
|
|
7441
|
+
/**
|
|
7442
|
+
* 收起结果框(0.13.2 起**不再丢内容**)。
|
|
7443
|
+
*
|
|
7444
|
+
* 内容一直在 dock 里(也一直在盘上),只是标记成「先别显示」;面板那一半的切换按钮会写
|
|
7445
|
+
* 「上次的结果在这儿」,点它就能再展开。想真丢掉用 `dockDiscard`。
|
|
7446
|
+
*/
|
|
7386
7447
|
dockClose: () => {
|
|
7448
|
+
dockHidden.set(true);
|
|
7449
|
+
note2("\u5DF2\u6536\u8D77\u7ED3\u679C\u6846 \u2014\u2014 \u7ED3\u679C\u7559\u7740\uFF0C\u70B9\u300C\u4F18\u5316\u63D0\u793A\u8BCD\u300D\u90A3\u4E00\u534A\u5C31\u80FD\u518D\u770B\u5230");
|
|
7450
|
+
},
|
|
7451
|
+
/** 丢弃结果框(真的清掉,磁盘那份也删)。 */
|
|
7452
|
+
dockDiscard: () => {
|
|
7387
7453
|
dock.set(dockReducer(dock.getSnapshot(), { type: "clear" }));
|
|
7454
|
+
dockHidden.set(false);
|
|
7455
|
+
note2("\u5DF2\u4E22\u5F03\u8FD9\u4EFD\u7ED3\u679C");
|
|
7456
|
+
},
|
|
7457
|
+
/** 展开结果框(收起状态的对面)。 */
|
|
7458
|
+
dockShow: () => {
|
|
7459
|
+
dockHidden.set(false);
|
|
7388
7460
|
},
|
|
7389
7461
|
/** 用户在框里手改了成品。 */
|
|
7390
7462
|
dockEdit: (text) => {
|
|
@@ -7423,6 +7495,7 @@ function apply(ctx) {
|
|
|
7423
7495
|
clearDockState: () => {
|
|
7424
7496
|
clearPersistedDock();
|
|
7425
7497
|
dock.set(null);
|
|
7498
|
+
dockHidden.set(false);
|
|
7426
7499
|
}
|
|
7427
7500
|
}
|
|
7428
7501
|
})
|
|
@@ -7464,7 +7537,7 @@ function apply(ctx) {
|
|
|
7464
7537
|
name: "shell.overlay",
|
|
7465
7538
|
id: "composer-ux-quick-panel",
|
|
7466
7539
|
inject: () => ({
|
|
7467
|
-
hooks: { live, panel, busy: optimizing, startedAt: optimizeStartedAt, notice: panelNotice, book, bookStatus, dock },
|
|
7540
|
+
hooks: { live, panel, busy: optimizing, startedAt: optimizeStartedAt, notice: panelNotice, book, bookStatus, dock, dockHidden },
|
|
7468
7541
|
actions: {
|
|
7469
7542
|
toggle: quickActions.toggle,
|
|
7470
7543
|
close: quickActions.close,
|
|
@@ -7474,6 +7547,8 @@ function apply(ctx) {
|
|
|
7474
7547
|
dockRetry: quickActions.dockRetry,
|
|
7475
7548
|
dockCancel: quickActions.dockCancel,
|
|
7476
7549
|
dockClose: quickActions.dockClose,
|
|
7550
|
+
dockDiscard: quickActions.dockDiscard,
|
|
7551
|
+
dockShow: quickActions.dockShow,
|
|
7477
7552
|
dockEdit: quickActions.dockEdit,
|
|
7478
7553
|
setTier: quickActions.setTier,
|
|
7479
7554
|
setInsertMode: quickActions.setInsertMode,
|