dsh-mini-remote 0.1.8 → 0.2.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/CHANGELOG.md CHANGED
@@ -2,6 +2,68 @@
2
2
 
3
3
  本文件记录每个版本改了什么。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [0.2.0] - 2026-10-08
6
+
7
+ 这一版跨了三天、50 笔改动,主体是**「进阶设置」——把电脑端那整套界面搬到手机上**,加上一颗「回到底部」的浮标,以及它俩连着带出来的一批手机端修补。
8
+
9
+ ### 新加的
10
+
11
+ - **手机上能看到电脑端那整套界面了。** 手机这张遥控页是专门为手机做的,所以电脑上那些插件贡献的设置页它进不去——改模型配置、装卸插件、翻会话记录这些事,原来只能回电脑。现在设置页多了一项「进阶设置」:打开它,下面多出一行「电脑端界面」,点「打开」,**同一份电脑端页面原样嵌在手机这半边**(不跳转、不开新标签,关掉就真停)。**它默认关着,打开要付代价**:开启等于把这个访问口令的权限,从遥控页露出的那一点扩到**整个电脑端设置面**(模型配置、凭据页、插件装卸、会话记录),所以页面上写明后果,要点两次才算开;服务端拿不到电脑端地址时整行不显示——不摆一个假的开关。
12
+
13
+ - 这四十来笔里绝大多数是把它磨到能用。值得记的是几处根因:**白屏**有两处(挂载前缀不对,加上我们自己多带了一个 token 参数);**「一直正在重新连接」**是 WebSocket 没转发;**一堆 403** 是 WebSocket 握手和普通请求的 `Origin` 得换成上游自己;**界面加载不出来**是镜像前缀下面的 POST 没转;**重启后卡在「Loading plugins」**是宿主还没就绪就被问了,该等而不是立刻回 502;一次 34 MB 的大附件照旧转发;带内容指纹的静态资源补了长期缓存。
14
+
15
+ - **首帧没字**查了好几轮,最后在真面板上量出来真凶:另一个插件贴在根元素上的「卷起」标记(`opacity:0`)。我们把那个标记摘掉,从样式上根治,删掉了中途加的盲兜底。**内容区滚不动**是我们自己的适配漏了一格 `min-height:0`;**侧栏那一列不能滚、「设置」够不着**、**侧栏中间那层被压成 0**(历史会话看不见)、**主页整片空白**、**插件市场那一节被压窄**各有各的根因,都修了;插件市场的「能滚的列表窗口」从 356 做到 498 像素。
16
+
17
+ - **这一页的适配由我们自己来**:别的插件写死了「只在窄屏生效」的样式,一律摘掉(`dsh-pocket` 是真正动官方设置面板的那一个,单独挡在门外)。官方设置面板的导航掰回竖排、字始终显示;插件市场那格的社区介绍、两个用不上的按钮先藏起来,把地方让给要用的东西。
18
+
19
+ - **怎么知道 DSH 一升级、这些适配会不会悄悄失效**:多了一份**结构依赖清单**(`tools/structure-deps.mjs`),把我们靠官方界面上的哪些「标记」活着一条条登记下来,并标明失配的后果(只是这一条不生效,还是可能误伤别处);配一个**升级自检**(`tools/structure-check.mjs`)对着活界面逐条查,中文报告、失配非零退出。清单和代码之间还钉了测试:改了代码没改清单、加了新依赖没登记,都会红。
20
+
21
+ - **多了一颗「回到底部」的圆形浮标。** 照电脑端 DSH 那颗做:聊天和「完整」两个模式里,往上翻过之后右下角浮出来,点一下回到最新那一条。**单帧模式下它一律不出现**(那一模式整屏只有一条回复,没有「回去」这回事);渐出 130 毫秒;深色浅色各适配一套——浅色下那道环深一档、抬一层更轻的投影,深色下投影本来等于零就去掉;看得见的圆比按得到的地方小一档(热区是手指的下限,不该跟着缩)。尺寸和位置是量着调的:看得见的面积先减半(38 → 27)、往左让开滚动条那条带,最后又增大一档到 31。
22
+
23
+ ### 修了的
24
+
25
+ - **「完整」模式里执行轨迹那个「进行中」不再竖着折行**(真机复现过);组头右侧的「模型原话」也走上 markdown 渲染,和别处的正文一个长相。
26
+ - **插话发送真的插话了。** 0.1.9 只做了「繁忙时是排队还是插话」那个开关,没接到发送路径上——选了插话,实际还是排队。
27
+ - **镜像页偶尔显示 `{"error":"not found"}`。** 那是我们自己的兜底 404:进阶设置关着的时候,门后面本来就不该回 JSON。
28
+ - **进阶设置关着时,那一行仍然显示。** 根因在 CSS,不在 JS。
29
+ - **`engines.dsh` 和 `peerDependencies` 声明的是同一个区间了。** 两处不一致时,npm 拿一个、宿主看另一个。
30
+
31
+ ## [0.1.9] - 2026-10-05
32
+
33
+ 这一版跨了三天、74 笔改动,主体是**设置页整屏化与插件页**、**「完整」显示模式与执行轨迹**,以及一串观感与读取速度的修补。
34
+
35
+ ### 新加的
36
+
37
+ - **设置页从「贴底抽屉」改成整屏。** 内容越加越多,那张最高只占 86% 屏高的抽屉不够用了。页眉跟着收矮(65 → 约 51 像素);底色仍是毛玻璃,因为页眉挪到了滚动区**外面**,往上滚时它不跟着跑。四个面板(设置、统一操作、子智能体、插件)的「关闭」按钮统一收小一号。
38
+
39
+ - **多了「插件」这一页。** 从设置页进去,列出官方插件和已安装的插件,分组可折叠、默认收起。内置插件按 DSH 自己的 Loader 条目表列,不再按包名——那才是「一切皆插件」的粒度。会话插件那一组的预设名走中文词条表,不再露 `standard` 这类代号;标题后面原来跟着的括号说明也去掉了。
40
+
41
+ - **显示模式多了「完整」,把执行轨迹画出来。** 原来只有两种看聊天的模式,现在多了「完整」:执行轨迹**穿插在对话里的原位**(和电脑端同一套长相),带三层折叠,可以一层层点开看「这一步读了什么文件」。已加载的轨迹可以搜——**搜不到时如实说「已加载的这批里没有」**,不说成「不存在」,因为实际只搜了已加载的那部分。跑着的那一行有一道扫光扫过去,和电脑端一样。思考正文、工具卡的「输入 / 输出」、自动审查的文案都对齐了电脑端。
42
+
43
+ - **字体大小可以调了。** 设置页里三档:小 / 标准 / 大。
44
+
45
+ - **新建会话可以选 Agent 模式。** 照电脑端的词条表,模式名走中文;自己起的名字不翻译。
46
+
47
+ - **手机上「添加文件」和「调用指令」并成了一个入口。** 原来这两件事各有各的门(回形针直接开文件选择器、打一个斜杠才弹指令菜单),手机上没人知道打斜杠还能调指令。现在是输入框旁边**一个**入口,点开是一张面板:上面「添加文件」、下面「调用指令」。选文件支持**多选**,选完直接回主页面,不必自己再按一下关闭。
48
+
49
+ - **设置页多了「繁忙时的发送行为」。** 照电脑端那一条做的,两边共用同一个值:智能体运行时按发送键,是**排队发送**还是**插话发送**。位置在权限档位下面。
50
+
51
+ - **附件按钮的图标换成了电脑端那只鲸鱼。** 填色跟着主题走,深浅两套各自适配。上传文件时它会**原地游动**——原来那个转圈是「物件在转」的语汇(齿轮、加载器),用在活物身上像标本被转着看。用户要「减少动效」时它就不动,而忙碌的表示没丢(按钮此时是禁用态)。
52
+
53
+ - **浅色主题成为默认。** 之前默认深色。
54
+
55
+ - **导航和子智能体的读取更快了。** 服务端多了一层**会话元数据缓存**,导航缓存跟着会话事件增量更新,子智能体列表和运行状态也改成增量刷新;子智能体面板首次点开会预取,不再等那一下。
56
+
57
+ ### 修了的
58
+
59
+ - **权限档位那三处观感问题。** 那一块下面缺一条分割线(照其他线的做法补上,位置和其他板块一致);那排按钮没填满它的框,最右「自动审查」那一格的背景空出一条;上缘那条线偏粗——是标签那行的下边线和按钮框的上边框**叠在一起**了,去掉前者。
60
+ - **子智能体面板的空态文字贴着屏边。** 那三处漏了左留白。
61
+ - **「调用指令」紧贴着上面的按钮。** 那条留白规则其实写着 20 像素,但被更具体的选择器盖掉了,**从来没生效过**。
62
+ - **导航栏会卡在「正在读取…」不走。** 刷新时清了数据、没清展开态。
63
+ - **「完整」模式的一串问题。** 已结束的轮次组头全写着「进行中」(一排跑完的轮在假转圈);往上滑放不出更早的一屏;正在想的片段不逐字顺出来;跑着的空组要等步骤跑完才一次性冒出来;实时帧里的旁白被画成「工具调用」空壳。
64
+ - **切会话时权限档位不跟着走**,以及权限档位那一块会因为一次读失败就整个消失。
65
+ - **设置页两处实测出来的毛病**:DSH 版本号一直显示 `unknown`、会话插件那一组的行数虚报。
66
+
5
67
  ## [0.1.8] - 2026-10-02
6
68
 
7
69
  ### 改了的
package/README.en.md CHANGED
@@ -29,7 +29,7 @@ Here is what the interface looks like — dark and light, switchable in settings
29
29
 
30
30
  <img width="290" alt="Dark: one conversation, carrying only your instruction and the conclusion" src="https://raw.githubusercontent.com/xingzhen199186/dsh-mini-remote/main/docs/screen-dark.png" /> <img width="290" alt="Light: the same conversation, in the palette taken from the promo artwork" src="https://raw.githubusercontent.com/xingzhen199186/dsh-mini-remote/main/docs/screen-light.png" />
31
31
 
32
- It is **not** a second screen for the desktop interface. Approval dialogs, file diffs and tool-call chains exist on the computer and nowhere on the phone — deliberately. It does very little.
32
+ It is **not** a second screen for the desktop interface. Approval dialogs, file diffs and tool-call chains exist on the computer and nowhere on the default page — deliberately. It does very little. (If you want the **whole** desktop interface, there is an off-by-default entry in settings — see the last paragraph of [What the phone can do](#what-the-phone-can-do).)
33
33
 
34
34
  Other phone clients copy the computer's execution process over to the phone in full. This one does the opposite: **it sends only the two lines you asked for.** That is the single biggest difference between it and them.
35
35
 
@@ -66,7 +66,7 @@ If you already have the source on disk, you can point it at the folder:
66
66
  dsh plugin --profile web add <the folder you put the source in>
67
67
  ```
68
68
 
69
- **Which DSH versions it fits.** This plugin was developed on DSH **0.1.5-rc.2** and re-checked item by item on **0.1.7-rc.2** (every interface it uses was unchanged); **versions below 0.1.5 have not been tried**. `rc` is the tag the project puts on preview builds, and DSH as a whole is still a developer preview — minor versions may break things, so versions newer than 0.1.7 are not guaranteed either. If it ever does hit an incompatibility, the usual symptom is one entry point going missing (the model line at the top, say) while everything else keeps working: the plugin is written so that a missing service switches off that one feature rather than the whole thing.
69
+ **Which DSH versions it fits.** This plugin was developed on DSH **0.1.5-rc.2** and re-checked item by item on **0.1.7-rc.2**; **0.2.0 itself was built on 0.2.0-rc.1 / 0.2.0-rc.2** (every interface it uses was unchanged, and the full check suite ran on both sides). **Versions below 0.1.5 have not been tried**, and the declared ceiling is **0.3.0**. `rc` is the tag the project puts on preview builds, and DSH as a whole is still a developer preview — minor versions may break things, so versions newer than 0.2.0 are not guaranteed either. If it ever does hit an incompatibility, the usual symptom is one entry point going missing (the model line at the top, say) while everything else keeps working: the plugin is written so that a missing service switches off that one feature rather than the whole thing. The phone-side interface adaptations also come with a dependency manifest and an **upgrade self-check** (`tools/structure-check.mjs`): run it against the live page after an upgrade and it walks every dependency entry by entry.
70
70
 
71
71
  Settings follow the same rule: the settings are declared the standard DSH way, and on the 0.1.5-era library that ability does not exist yet — the plugin **still installs and works exactly as before**, it just loses the "applies immediately" behaviour mentioned below. On the **0.1.7 generation** that ability is fully there.
72
72
 
@@ -131,7 +131,7 @@ Open the link and that's the whole interface: one input box, and the latest repl
131
131
 
132
132
  **She speaks up while she works.** On the computer, the model often mutters a line between steps ("let me look at this file first"). That line is now spoken by the whale girl through her speech bubble — the talking pose is a newly drawn sprite, the rotation pauses for it, and afterwards picks up from where it stopped. It stays **in the bubble** and never bleeds into the answer area below. Only the line it deliberately writes *for a human reader* is spoken; its own working thoughts — often English, often a whole paragraph — never reach the phone at all. Should that one narration line itself be in English, it is shown as-is, untranslated (translating would mean putting words in its mouth).
133
133
 
134
- **Two appearances, switchable anytime.** Settings has an "Appearance" row: **Dark** (the default), **Light**, and **Follow system**. Dark is deep-sea navy; the light palette is taken from the promo artwork — near-white ice blue for the ground, deep royal blue for the text, and the same royal blue on primary buttons. Your choice is stored on the phone, so it is still there next time you open the page.
134
+ **Two appearances, switchable anytime.** Settings has an "Appearance" row: **Dark**, **Light** (the default), and **Follow system**. Dark is deep-sea navy; the light palette is taken from the promo artwork — near-white ice blue for the ground, deep royal blue for the text, and the same royal blue on primary buttons. Your choice is stored on the phone, so it is still there next time you open the page.
135
135
 
136
136
  <img width="290" alt="Settings (dark): display mode and appearance" src="https://raw.githubusercontent.com/xingzhen199186/dsh-mini-remote/main/docs/settings-dark.png" /> <img width="290" alt="Settings (light): the selected states and the primary button turn royal blue too" src="https://raw.githubusercontent.com/xingzhen199186/dsh-mini-remote/main/docs/settings-light.png" />
137
137
 
@@ -139,9 +139,7 @@ Open the link and that's the whole interface: one input box, and the latest repl
139
139
 
140
140
  <img width="290" alt="Running (dark, photographed on a phone): the whale girl running, 15s elapsed" src="https://raw.githubusercontent.com/xingzhen199186/dsh-mini-remote/main/docs/running-dark-phone.jpg" /> <img width="290" alt="Running (light, photographed on a phone): the whale girl running, 7m43s elapsed, one instruction queued" src="https://raw.githubusercontent.com/xingzhen199186/dsh-mini-remote/main/docs/running-light-phone.jpg" />
141
141
 
142
- Both are phone photographs (the browser's own chrome is in frame, so they sit slightly taller than a render would).
143
-
144
- **Two display modes.** Tap the ⚙ in the top right to switch. "Single frame" (the default) keeps only the newest reply on screen — good for "I just want to see how this one turned out". "Chat" is a back-and-forth bubble list — good for several rounds of follow-up questions. Use it for a while and you'll know which you prefer.
142
+ **Three display modes.** Tap the ⚙ in the top right to switch. "Single frame" (the default) keeps only the newest reply on screen — good for "I just want to see how this one turned out". "Chat" is a back-and-forth bubble list — good for several rounds of follow-up questions. "Full" takes that same list and **puts each step's process back in its original place** (the same shape the desktop app uses), with layers you can open one by one to see what that step read — and it is searchable. Use it for a while and you'll know which you prefer.
145
143
 
146
144
  **Open a session and you see what was said in it before.** Switch to a session that has existed for a while and Chat mode lists every earlier round (the instructions you sent, the conclusion of each round); Single frame shows only the last conclusion. **Very large sessions show only the most recent stretch**: reading one of those in full — tens of thousands of events — would drag the DSH process on your computer down, so the plugin reads just the tail and says so at the top ("this is not everything, only the most recent stretch"). That line is not boilerplate; it is the truth.
147
145
 
@@ -151,7 +149,9 @@ Both are phone photographs (the browser's own chrome is in frame, so they sit sl
151
149
 
152
150
  **Three things you can change in passing.** Tap the line at the top to switch the current session's model and reasoning effort; switch the permission preset (View Only / Workspace Write / Full Access); browse the computer's folders to register a new workspace and start a session in it.
153
151
 
154
- **You're reading conclusions, not the process.** There's no tool-call chain and no file diff on the page. The one exception is the line the whale girl says out loud — it goes into her bubble, never into the answer area (see above). **System-level confirmation dialogs cannot be answered from the phone** (approving a dangerous command, for instance) — those still need the computer. What the phone can answer is the multiple-choice question the AI puts to you; the two are not the same thing. It's a remote control: the TV still has to be on for the remote to be any use.
152
+ **By default you're reading conclusions, not the process.** In Single frame and Chat there is no tool-call chain and no file diff; the one exception is the line the whale girl says out loud — it goes into her bubble, never into the answer area (see above). To read the process you switch to "Full" yourself. **System-level confirmation dialogs cannot be answered from the phone** (approving a dangerous command, for instance) — those still need the computer. What the phone can answer is the multiple-choice question the AI puts to you; the two are not the same thing. It's a remote control: the TV still has to be on for the remote to be any use.
153
+
154
+ **The whole desktop interface can be brought over too ("Advanced settings").** Settings has an **off-by-default** "Advanced settings" entry: turn it on and a "Desktop interface" row appears below it; tap "Open" and the computer's full DSH page is embedded right there on the phone — no jump, no new tab — so model configuration, plugin install and removal, session records and anything else the remote page doesn't have can be done from here. **It is the exact opposite of the positioning above** (switch it on and the whole process is on screen), which is why it starts off: turning it on spells the consequence out (the password's reach extends to the entire desktop settings surface) and takes two taps to confirm. When the plugin cannot learn the desktop address, the entry does not appear at all.
155
155
 
156
156
  ---
157
157
 
@@ -233,6 +233,12 @@ So you can spend your time more freely — instead of being stuck in that small
233
233
 
234
234
  ---
235
235
 
236
+ ## For maintainers
237
+
238
+ After upgrading DSH, run `node tools/structure-check.mjs` once. It reports entry by entry: which ones still hold, which have drifted, and whether a drifted one actually needs attention. When anything has drifted the exit code is 1, so the check can be wired into a pipeline later.
239
+
240
+ ---
241
+
236
242
  ## Credits
237
243
 
238
244
  This plugin's design draws on [dsh-pocket](https://www.npmjs.com/package/dsh-pocket) by shaobeichen — in particular the shape of the configuration page, where each connection route gets its own link, its own QR code, and its own password.
package/README.md CHANGED
@@ -29,7 +29,7 @@
29
29
 
30
30
  <img width="290" alt="深色:一段对话,手机上只留你的指令和它的结论" src="https://raw.githubusercontent.com/xingzhen199186/dsh-mini-remote/main/docs/screen-dark.png" /> <img width="290" alt="浅色:同一段对话,色调跟宣传图那张画统一" src="https://raw.githubusercontent.com/xingzhen199186/dsh-mini-remote/main/docs/screen-light.png" />
31
31
 
32
- 它**不是**:电脑端界面的备用屏。电脑上有的审批弹窗、文件 diff、工具调用链,手机上都没有——它刻意做得很少。
32
+ 它**不是**:电脑端界面的备用屏。电脑上有的审批弹窗、文件 diff、工具调用链,默认那张页面上都没有——它刻意做得很少。(想要**整个**电脑端界面,设置里有一个默认关着的入口,见[手机上能做什么](#手机上能做什么)的最后一段。)
33
33
 
34
34
  别的手机端会把电脑上的执行过程原样搬过来,它反过来:**只把你要的那两句送过去**。这是它和它们最不一样的地方。
35
35
 
@@ -66,7 +66,7 @@ dsh plugin --profile web add github:xingzhen199186/dsh-mini-remote
66
66
  dsh plugin --profile web add <你放源码的目录>
67
67
  ```
68
68
 
69
- **它适配哪些 DSH 版本。** 这个插件在 DSH **0.1.5-rc.2** 上开发,在 **0.1.7-rc.2** 上重新逐项核对过(插件用到的那些接口,一个都没变),**低于 0.1.5 的版本没试过**。`rc` 是官方给预览版编的号,DSH 目前整体还是开发者预览版,跨小版本可能有破坏性改动,所以比 0.1.7 更新的版本同样不保证。真遇上不兼容,症状一般是某一块入口不见了(比如顶栏那行模型),其余部分照常——插件是按「少了哪个服务就关掉哪块功能」写的,不会整个起不来。
69
+ **它适配哪些 DSH 版本。** 这个插件在 DSH **0.1.5-rc.2** 上开发,在 **0.1.7-rc.2** 上重新逐项核对过,**0.2.0 这一版就是在 0.2.0-rc.1 / 0.2.0-rc.2 上做出来的**(插件用到的那些接口,一个都没变,两端各跑了一遍全套检查);**低于 0.1.5 的版本没试过**,声明的上限是 **0.3.0**。`rc` 是官方给预览版编的号,DSH 目前整体还是开发者预览版,跨小版本可能有破坏性改动,所以比 0.2.0 更新的版本同样不保证。真遇上不兼容,症状一般是某一块入口不见了(比如顶栏那行模型),其余部分照常——插件是按「少了哪个服务就关掉哪块功能」写的,不会整个起不来。手机端那些界面适配另有一份清单和一个**升级自检**(`tools/structure-check.mjs`),升级完对着活界面逐条查一遍,就知道哪条被改坏了。
70
70
 
71
71
  设置也是这样处理的:设置清单按 DSH 的标准方式声明,而在 0.1.5 那一代的库还没有相应能力,插件**照样装得上、功能照旧**,只是少了下面提到的「改完立即生效」。**0.1.7 这一代**上这条能力是齐的。
72
72
 
@@ -131,7 +131,7 @@ dsh plugin --profile web add <你放源码的目录>
131
131
 
132
132
  **鲸鱼娘动画。** 电脑上的模型在步骤之间常会自言自语一句(「我先看一下这个文件」这类),这一句现在由鲸鱼娘念出来——说话的动作是新画的一张立绘,轮播会为它停一下,说完接着从停下的地方往下走。
133
133
 
134
- **两套外观,随手换。** 设置里有一行「外观」:**深色**(默认)、**浅色**、**跟随系统**。深色是深海墨蓝打底;浅色那套的色是从宣传图那张画上取的——近白的冰蓝地、深宝蓝的字,主按钮也换成同一个宝蓝。选哪套记在手机上,下次打开还是它。
134
+ **两套外观,随手换。** 设置里有一行「外观」:**深色**、**浅色**(默认)、**跟随系统**。深色是深海墨蓝打底;浅色那套的色是从宣传图那张画上取的——近白的冰蓝地、深宝蓝的字,主按钮也换成同一个宝蓝。选哪套记在手机上,下次打开还是它。
135
135
 
136
136
  <img width="290" alt="设置(深色):显示模式与外观" src="https://raw.githubusercontent.com/xingzhen199186/dsh-mini-remote/main/docs/settings-dark.png" /> <img width="290" alt="设置(浅色):选中态和主按钮都跟着换成宝蓝" src="https://raw.githubusercontent.com/xingzhen199186/dsh-mini-remote/main/docs/settings-light.png" />
137
137
 
@@ -139,9 +139,7 @@ dsh plugin --profile web add <你放源码的目录>
139
139
 
140
140
  <img width="290" alt="执行中(深色,手机实拍):鲸鱼娘正在跑、已计时 15 秒" src="https://raw.githubusercontent.com/xingzhen199186/dsh-mini-remote/main/docs/running-dark-phone.jpg" /> <img width="290" alt="执行中(浅色,手机实拍):鲸鱼娘正在跑、已计时 7 分 43 秒、队列排着一条" src="https://raw.githubusercontent.com/xingzhen199186/dsh-mini-remote/main/docs/running-light-phone.jpg" />
141
141
 
142
- 两张都是手机实拍(浏览器自带的状态栏也在画面上,所以比渲染图略高一点)。
143
-
144
- **两种显示模式。** 点右上角的 ⚙ 切换。「单帧」(默认)整屏只留最新一条回复,适合「我就想看这次的结果」;「聊天」是一来一回的气泡列表,适合连着追问好几轮。用一阵子你会知道自己偏哪个。
142
+ **三种显示模式。** 点右上角的 ⚙ 切换。「单帧」(默认)整屏只留最新一条回复,适合「我就想看这次的结果」;「聊天」是一来一回的气泡列表,适合连着追问好几轮;「完整」在聊天那一串里**把每一步过程插回原位**(和电脑端同一套长相),可以一层层点开看「这一步读了什么文件」,也能搜。用一阵子你会知道自己偏哪个。
145
143
 
146
144
  **点开一个会话,能看到它之前聊过什么。** 在左边切到一个早就存在的会话,聊天模式会把之前的每一轮都列出来(你发过的指令、每一轮的结论),单帧模式只显示最后一条结论。**特别大的会话只给最近一段**:那种几万个事件的会话,整份读下来会把电脑上的 DSH 拖垮,所以只读末尾那一小段,并在最上面写明「这不是全部,只显示了最近一段」——你看到的那句话不是客套话,是实情。
147
145
 
@@ -151,7 +149,9 @@ dsh plugin --profile web add <你放源码的目录>
151
149
 
152
150
  **顺手能改的三样。** 点顶栏那一行,换当前会话的模型和思考强度;切权限档位(仅可查看 / 工作区内修改 / 完全权限);浏览电脑上的目录,登记新的工作区和会话。
153
151
 
154
- **看的是结论,不是过程。** 页面上没有工具调用链、没有文件 diff。唯一的例外是鲸鱼娘嘴里那一句自言自语——它只进气泡,不进回答区(见上面那一段)。**系统层面的确认弹窗手机上点不了**(比如危险命令的审批),那类还得回电脑处理——手机上能答的是 AI 主动提给你的选择题,这两件事不一样。它的定位是一根遥控器:电视还得在那儿开着,遥控器才有用。
152
+ **默认看的是结论,不是过程。** 单帧和聊天这两个模式里没有工具调用链、没有文件 diff;唯一的例外是鲸鱼娘嘴里那一句自言自语——它只进气泡,不进回答区(见上面那一段)。想看过程,得自己切到「完整」模式。**系统层面的确认弹窗手机上点不了**(比如危险命令的审批),那类还得回电脑处理——手机上能答的是 AI 主动提给你的选择题,这两件事不一样。它的定位是一根遥控器:电视还得在那儿开着,遥控器才有用。
153
+
154
+ **想要整个电脑端界面,也搬得动(「进阶设置」)。** 设置页里有一项**默认关着**的「进阶设置」:打开它,下面会多出一行「电脑端界面」,点「打开」,电脑上那个完整的 DSH 页面就原样嵌在手机这半边,不跳转也不开新标签——改模型配置、装卸插件、翻会话记录这些手机页上没有的事,都能在这儿办。**它和上面那条定位正好相反**(一开,中间过程就全在屏幕上),所以默认关着:打开时页面会把后果说清楚(这个访问口令的权限会扩到整个电脑端设置面),要点两次确认;电脑端地址拿不到时,这一项不显示。
155
155
 
156
156
  ---
157
157
 
@@ -240,6 +240,12 @@ dsh plugin --profile web add <你放源码的目录>
240
240
 
241
241
  ---
242
242
 
243
+ ## 给维护者
244
+
245
+ 升级 DSH 之后,跑一次 `node tools/structure-check.mjs`。它会逐条报出来:哪些还在、哪些已经失配、失配的那条要不要管。有失配时退出码是 1,以后能直接接进流程里。
246
+
247
+ ---
248
+
243
249
  ## 致谢
244
250
 
245
251
  这个插件在设计思路上参考过 [dsh-pocket](https://www.npmjs.com/package/dsh-pocket)(作者 shaobeichen)——尤其是「每条连接路径各配一条链接、一个二维码、一个密码」这种配置形态。
package/lib/commands.js CHANGED
@@ -83,6 +83,12 @@ export function describeCommands(descriptors) {
83
83
  if (!name) continue
84
84
  const hint = typeof d?.input?.hint === 'string' && d.input.hint ? d.input.hint : null
85
85
  rows.push({
86
+ // 这条指令的**身份**:DSH 自己的 `CommandDefinitionId`(例如
87
+ // `@deepseek-ai/dsh-command-compact`)。桌面端靠它选本地化标题、图标和内置别名。
88
+ // 手机这一版还拿不到那份本地化文案——它长在浏览器端那个包里(dsh-client-ui-commands
89
+ // 自己的 zh/en 词典加 React 图标),宿主服务只给 name / description——所以界面照旧
90
+ // 显示注册名;但标识不能再丢:丢了就永远接不上那一层。
91
+ definitionId: typeof d?.definitionId === 'string' && d.definitionId ? d.definitionId : null,
86
92
  name,
87
93
  description: typeof d?.description === 'string' ? d.description : '',
88
94
  hint,
package/lib/events.js CHANGED
@@ -140,8 +140,12 @@ export function createTurnTracker() {
140
140
  return {
141
141
  /**
142
142
  * 喂入一条会话事件。
143
- * @returns {{kind:'user', text:string}|{kind:'notice', text:string, summary:string, senderSessionId:string}|{kind:'reply', text:string, reason:string, userText:string, userMessageId:string, interrupted:boolean}|null}
143
+ * @returns {{kind:'user', text:string, id?:string, turn:number|null}|{kind:'notice', text:string, summary:string, senderSessionId:string, turn:number|null}|{kind:'reply', text:string, reason:string, userText:string, userMessageId:string, interrupted:boolean, turn:number|null}|null}
144
144
  * 需要推送到手机时返回动作,否则返回 null。
145
+ *
146
+ * 每个动作都带 `turn`(这条动作发生在第几轮):完整模式要把每一轮的轨迹块
147
+ * 插回它那轮对话的原位(指令之后、回答之前),对号靠它——轮号本来就在
148
+ * turn/start 时记在 s.turn 上,这里只是随动作带出来。
145
149
  */
146
150
  feed(sessionId, event) {
147
151
  if (!event || typeof event.type !== 'string') return null
@@ -174,6 +178,7 @@ export function createTurnTracker() {
174
178
  text: notice,
175
179
  summary: typeof event.data.source.summary === 'string' ? event.data.source.summary : '',
176
180
  senderSessionId: String(event.data.source.senderSessionId ?? ''),
181
+ turn: s.turn,
177
182
  }
178
183
  }
179
184
  // 只认人发的;agent.inject() 的合成上下文(文件变更通知、skill 内容等)
@@ -187,7 +192,7 @@ export function createTurnTracker() {
187
192
  s.userMessageId = event.data?.id ?? ''
188
193
  // 带上 id:手机自己发出去的指令,注入后也会以这条事件回来,
189
194
  // 调用方要靠 id 把它认出来、不要记第二笔(见 index.js 的 injectedIds)。
190
- return { kind: 'user', text, id: event.data?.id }
195
+ return { kind: 'user', text, id: event.data?.id, turn: s.turn }
191
196
  }
192
197
 
193
198
  case 'assistant/message': {
@@ -227,7 +232,7 @@ export function createTurnTracker() {
227
232
  s.interrupted = false
228
233
  // 被取消/出错的轮次,以及卡在工具里没跑出回答的轮次,都没有什么可推的。
229
234
  if (!text) return null
230
- return { kind: 'reply', text, reason, userText, userMessageId, interrupted }
235
+ return { kind: 'reply', text, reason, userText, userMessageId, interrupted, turn: s.turn }
231
236
  }
232
237
 
233
238
  default:
package/lib/history.js CHANGED
@@ -20,7 +20,10 @@ import { foldCommandEvent } from './commands.js'
20
20
  * @returns {Array<{role:'user'|'assistant'|'command'|'notice', text?:string|null, timestamp:number|null,
21
21
  * id?:string|null, reason?:string, interrupted?:boolean,
22
22
  * commandId?:string, name?:string, args?:string, kind?:string,
23
- * summary?:string, senderSessionId?:string}>}
23
+ * summary?:string, senderSessionId?:string,
24
+ * turn?:number|null}>}
25
+ * turn=这条消息发生在第几轮(指令行没有轮号,留空)。完整模式靠它把
26
+ * 每轮的轨迹块插回对话原位。
24
27
  */
25
28
  export function replayHistory(events) {
26
29
  const tracker = createTurnTracker()
@@ -43,16 +46,18 @@ export function replayHistory(events) {
43
46
  text: action.text,
44
47
  summary: action.summary,
45
48
  senderSessionId: action.senderSessionId,
49
+ turn: action.turn ?? null,
46
50
  timestamp,
47
51
  })
48
52
  continue
49
53
  }
50
54
  if (action.kind === 'user') {
51
- out.push({ role: 'user', text: action.text, id: action.id ?? null, timestamp })
55
+ out.push({ role: 'user', text: action.text, id: action.id ?? null, turn: action.turn ?? null, timestamp })
52
56
  } else {
53
57
  out.push({
54
58
  role: 'assistant',
55
59
  text: action.text,
60
+ turn: action.turn ?? null,
56
61
  timestamp,
57
62
  reason: action.reason,
58
63
  interrupted: action.interrupted === true,