dsh-mobile 0.5.2 → 0.5.3

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,17 @@
2
2
 
3
3
  Notable changes to DSH Mobile are recorded here. GitHub Releases remain the source for downloadable packages and complete generated commit notes.
4
4
 
5
+ ## 0.5.3 - 2026-10-01
6
+
7
+ - Add a default-enabled, separately switchable question-card component: long questions, options, and footer actions share one bounded scroll area, while collapsed titles show up to two lines. Keep touch Enter as a newline without intercepting desktop shortcuts or IME composition; Android applies it only with an active software keyboard and no hardware keyboard. Answers remain in DSH's existing draft store rather than browser localStorage. Thanks @ayiejosh for [PR #133](https://github.com/saya-ch/dsh-mobile/pull/133).
8
+ - Fix large blank gaps after folded process sections by using DSH's message-flow spacing, which skips its hidden search-expandable sections. Thanks @xhwxt for [PR #134](https://github.com/saya-ch/dsh-mobile/pull/134).
9
+ - Let composer-dock entries wrap when community plugins add statistics or actions. Keep static statistics readable, limit context-ring ordering to the dock's direct children, and prevent adjacent entries from overlapping.
10
+ - During DSH voice input, request an available screen wake lock while the page is visible and dismiss mobile text focus only in the active composer. Release locks when capture ends, the page is hidden, or the plugin unloads; keep keyboard handling available without the wake-lock API. Thanks @ayiejosh for [PR #136](https://github.com/saya-ch/dsh-mobile/pull/136).
11
+ - Include the question-card component's built runtime in npm packages and add packed-installation and browser layout checks, covering long cards, keyboard modes, component disablement, folded sections, and additional composer-dock entries.
12
+ - Expand settings-save troubleshooting for Host/Origin rejection, a missing root Include, and file or lock errors, based on [#132](https://github.com/saya-ch/dsh-mobile/issues/132). This is diagnostic guidance, not a fix for DSH's reported duplicate-module problem; connection diagnostics do not attempt a settings write.
13
+ - Synchronize Android version metadata to 0.5.3 (build 73). Native behavior and the pairing protocol are unchanged from 0.5.2; existing paired devices do not need to pair again.
14
+ - Update the build and test development tools to tsdown 0.23 and Vitest 5 after Windows, Linux, Android, and DSH browser-startup checks.
15
+
5
16
  ## 0.5.2 - 2026-09-29
6
17
 
7
18
  - Give each DSH Mobile installation a stable FRP proxy name on a shared frps and report proxy-name or entry-port conflicts directly instead of waiting for a generic timeout (thanks @liudasheng for [PR #130](https://github.com/saya-ch/dsh-mobile/pull/130)). Keep that name through first-time setup and restarts, and use it in copied attach instructions.
package/CONTRIBUTORS.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  GitHub 的 [Contributors 图表](https://github.com/saya-ch/dsh-mobile/graphs/contributors) 按进入默认分支的提交自动统计,无法手动加入仅提交 issue 或 PR 提案的成员。本页按实际参与方式感谢社区成员;仓库维护者与自动依赖更新仍可在 GitHub 历史记录中查看。
4
4
 
5
- GitHub's Contributors graph counts commits on the default branch; issue reports and PR proposals cannot be manually added to it. This page credits community members by how they contributed, through 2026-09-29. The repository maintainer and automated dependency updates remain visible in GitHub's history.
5
+ GitHub's Contributors graph counts commits on the default branch; issue reports and PR proposals cannot be manually added to it. This page credits community members by how they contributed, through 2026-10-01. The repository maintainer and automated dependency updates remain visible in GitHub's history.
6
6
 
7
7
  ## 已合并 PR / Merged PRs
8
8
 
@@ -10,7 +10,7 @@ GitHub's Contributors graph counts commits on the default branch; issue reports
10
10
  | --- | --- |
11
11
  | [@1624318455](https://github.com/1624318455) | [#93](https://github.com/saya-ch/dsh-mobile/pull/93) |
12
12
  | [@abworks-dev](https://github.com/abworks-dev) | [#91](https://github.com/saya-ch/dsh-mobile/pull/91), [#98](https://github.com/saya-ch/dsh-mobile/pull/98), [#99](https://github.com/saya-ch/dsh-mobile/pull/99) |
13
- | [@ayiejosh](https://github.com/ayiejosh) | [#109](https://github.com/saya-ch/dsh-mobile/pull/109), [#110](https://github.com/saya-ch/dsh-mobile/pull/110), [#116](https://github.com/saya-ch/dsh-mobile/pull/116), [#117](https://github.com/saya-ch/dsh-mobile/pull/117), [#119](https://github.com/saya-ch/dsh-mobile/pull/119), [#124](https://github.com/saya-ch/dsh-mobile/pull/124), [#125](https://github.com/saya-ch/dsh-mobile/pull/125) |
13
+ | [@ayiejosh](https://github.com/ayiejosh) | [#109](https://github.com/saya-ch/dsh-mobile/pull/109), [#110](https://github.com/saya-ch/dsh-mobile/pull/110), [#116](https://github.com/saya-ch/dsh-mobile/pull/116), [#117](https://github.com/saya-ch/dsh-mobile/pull/117), [#119](https://github.com/saya-ch/dsh-mobile/pull/119), [#124](https://github.com/saya-ch/dsh-mobile/pull/124), [#125](https://github.com/saya-ch/dsh-mobile/pull/125), [#133](https://github.com/saya-ch/dsh-mobile/pull/133), [#136](https://github.com/saya-ch/dsh-mobile/pull/136) |
14
14
  | [@chintoleung](https://github.com/chintoleung) | [#104](https://github.com/saya-ch/dsh-mobile/pull/104) |
15
15
  | [@idoall](https://github.com/idoall) | [#61](https://github.com/saya-ch/dsh-mobile/pull/61), [#66](https://github.com/saya-ch/dsh-mobile/pull/66), [#67](https://github.com/saya-ch/dsh-mobile/pull/67), [#77](https://github.com/saya-ch/dsh-mobile/pull/77) |
16
16
  | [@JackRushante](https://github.com/JackRushante) | [#16](https://github.com/saya-ch/dsh-mobile/pull/16) |
@@ -19,7 +19,7 @@ GitHub's Contributors graph counts commits on the default branch; issue reports
19
19
  | [@qzyqmzn](https://github.com/qzyqmzn) | [#38](https://github.com/saya-ch/dsh-mobile/pull/38), [#75](https://github.com/saya-ch/dsh-mobile/pull/75) |
20
20
  | [@Sanksu](https://github.com/Sanksu) | [#118](https://github.com/saya-ch/dsh-mobile/pull/118) |
21
21
  | [@StrawberryAO](https://github.com/StrawberryAO) | [#34](https://github.com/saya-ch/dsh-mobile/pull/34), [#40](https://github.com/saya-ch/dsh-mobile/pull/40) |
22
- | [@xhwxt](https://github.com/xhwxt) | [#105](https://github.com/saya-ch/dsh-mobile/pull/105) |
22
+ | [@xhwxt](https://github.com/xhwxt) | [#105](https://github.com/saya-ch/dsh-mobile/pull/105), [#134](https://github.com/saya-ch/dsh-mobile/pull/134) |
23
23
  | [@xingleiwu](https://github.com/xingleiwu) | [#79](https://github.com/saya-ch/dsh-mobile/pull/79), [#84](https://github.com/saya-ch/dsh-mobile/pull/84), [#85](https://github.com/saya-ch/dsh-mobile/pull/85), [#86](https://github.com/saya-ch/dsh-mobile/pull/86) |
24
24
 
25
25
  ## 已吸收但未直接合并的 PR / Incorporated without a direct merge
@@ -45,6 +45,7 @@ GitHub's Contributors graph counts commits on the default branch; issue reports
45
45
  | [@chmzs](https://github.com/chmzs) | [#73](https://github.com/saya-ch/dsh-mobile/issues/73) |
46
46
  | [@gjzbbs](https://github.com/gjzbbs) | [#123](https://github.com/saya-ch/dsh-mobile/issues/123) |
47
47
  | [@hairyf](https://github.com/hairyf) | [#114](https://github.com/saya-ch/dsh-mobile/issues/114) |
48
+ | [@hpsks416](https://github.com/hpsks416) | [#132](https://github.com/saya-ch/dsh-mobile/issues/132) |
48
49
  | [@idoall](https://github.com/idoall) | [#42](https://github.com/saya-ch/dsh-mobile/issues/42), [#45](https://github.com/saya-ch/dsh-mobile/issues/45), [#46](https://github.com/saya-ch/dsh-mobile/issues/46), [#47](https://github.com/saya-ch/dsh-mobile/issues/47), [#62](https://github.com/saya-ch/dsh-mobile/issues/62), [#64](https://github.com/saya-ch/dsh-mobile/issues/64) |
49
50
  | [@IvyC-zz](https://github.com/IvyC-zz) | [#58](https://github.com/saya-ch/dsh-mobile/issues/58), [#78](https://github.com/saya-ch/dsh-mobile/issues/78) |
50
51
  | [@jillkayat](https://github.com/jillkayat) | [#10](https://github.com/saya-ch/dsh-mobile/issues/10) |
package/README.en.md CHANGED
@@ -31,14 +31,14 @@
31
31
 
32
32
  > DSH Mobile is a DeepSeek Harness community plugin; the native app supports Android only.
33
33
  >
34
- > **Current version: 0.5.2**. It gives installations stable, distinct proxy names on shared frps servers, reports name or port conflicts directly, and removes the DSH prerelease peer-version gate. [Release notes](CHANGELOG.md#052---2026-09-29).
34
+ > **Current version: 0.5.3**. Improve question cards, folded-process spacing, and composer layout with community plugins; refine focus and screen wake-lock handling during dictation, and expand troubleshooting for failed settings saves. [Release notes](CHANGELOG.md#053---2026-10-01).
35
35
  >
36
- > **Upgrade reminder**: the plugin and Android app can both be updated to 0.5.2. This APK only synchronizes version metadata; the 0.5.1 app and existing pairings do not need reinstalling or re-pairing. [Compatibility notes](#compatibility).
36
+ > **Upgrade reminder**: the plugin and Android app can both be updated to 0.5.3. This APK only synchronizes version metadata; existing devices do not need re-pairing. [Compatibility notes](#compatibility).
37
37
 
38
38
  <p align="center">
39
- <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.5.2/dsh-mobile-android-v0.5.2.apk"><img src="assets/brand/app-icon-rounded.svg" alt="DSH Mobile Android app icon" width="72" height="72"></a><br>
40
- <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.5.2/dsh-mobile-android-v0.5.2.apk"><strong>Download Android app 0.5.2</strong></a><br>
41
- <sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.5.2">Release notes and checksums</a></sub>
39
+ <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.5.3/dsh-mobile-android-v0.5.3.apk"><img src="assets/brand/app-icon-rounded.svg" alt="DSH Mobile Android app icon" width="72" height="72"></a><br>
40
+ <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.5.3/dsh-mobile-android-v0.5.3.apk"><strong>Download Android app 0.5.3</strong></a><br>
41
+ <sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.5.3">Release notes and checksums</a></sub>
42
42
  </p>
43
43
 
44
44
  DSH Mobile is a DeepSeek Harness plugin that lets a mobile browser or the Android app connect over a protected LAN or an optional Tailscale Funnel, cpolar, cloudflared, self-hosted FRP, or own reverse-proxy remote path. Both routes reach the same sessions, Workspaces, messages, and tools. The computer manages their switches and pairing authorizations separately; the Android app lists paired computers together. The plugin does not modify DeepSeek Harness source.
@@ -212,6 +212,8 @@ Revoking a device permanently deletes its durable record and token digest instea
212
212
 
213
213
  ## Third-party plugin compatibility
214
214
 
215
+ Version 0.5.3 includes the independently switchable `dsh-mobile-question-fixes` component, enabled by default. Disable it in DSH's plugin component list to restore the stock card immediately. Long questions, options, and footer actions share one bounded scroll area, and collapsed titles show up to two lines. Touch Enter retains its newline in phone browsers; the Android app applies this behavior only when its software keyboard is open and no hardware keyboard is connected. DSH continues to own answer drafts, with no additional copy in browser localStorage.
216
+
215
217
  The mobile adaptation keeps DSH's existing Workspace, task-management, terminal, and file-panel entry points instead of isolating third-party plugin content in a separate page. The wide-layout screenshot below shows the Android app in a wide viewport. The app adapts to the available width: phones use drawers and overlays, while wide screens use side-by-side panels; both layouts expose the same features and connection methods. DSH still loads third-party plugins itself—the mobile layer only adapts layout and access, without modifying DeepSeek Harness source.
216
218
 
217
219
  Compatibility and WebSocket rules:
@@ -291,6 +293,10 @@ See [SECURITY.md](SECURITY.md).
291
293
 
292
294
  ## Troubleshooting
293
295
 
296
+ - **The testing notice cannot be confirmed or settings cannot be saved**: open DSH directly at its local address on the computer, then inspect the terminal log or the failed settings request in the browser's Network panel. Connection diagnostics do not test settings writes. Keep the existing configuration and `settings.yaml.imported`, and investigate the specific error; the same UI message can have different causes. See [DSH #860](https://github.com/deepseek-ai/deepseek-harness/discussions/860).
297
+ - **A settings request returns HTTP `403`**: first inspect DSH's Host/Origin trust checks and any proxy response. Check whether an iframe, proxy, or browser extension changes the request's origin, and verify direct local access. Keep the trust checks enabled.
298
+ - **The error contains `profile reload requires the root Include entry`**: this error occurs while DSH reloads the profile. [Issue #132](https://github.com/saya-ch/dsh-mobile/issues/132) reports it after two copies of `dsh-app-boot` were loaded, but the message alone does not establish that cause. Compare the versions and installation paths used by the running DSH command and the active profile; keep the configuration and follow the [official DSH documentation](https://deepseek-harness.github.io/deepseek-harness/) to repair the dependencies. If it still fails, report the versions, module paths, and full sanitized error upstream.
299
+ - **The settings error contains `EACCES`, `EPERM`, or a lock-file failure**: inspect the reported file's permissions and any file-operation tools injected into the launch environment. Lock cleanup can fail after a write, so check what was actually saved.
294
300
  - **The phone shows another plugin’s pairing-code page**: `dsh-remote-web-ui` and DSH Mobile have separate remote channels and pairing codes; they cannot be mixed. If DSH Mobile diagnostics warn about a competing remote plugin, turn off that plugin’s remote access on the computer, refresh, and scan the current DSH Mobile QR code. Merely installing the other plugin without enabling its remote channel does not trigger this warning.
295
301
  - **Remote diagnostics say “reachable through the computer's proxy”**: the check tries a direct request first, then an HTTP proxy from the DSH process environment only if direct access fails; `NO_PROXY` may exclude the target. This proves only that the computer completed an HTTPS probe through the proxy, not that the phone or actual tunnel can connect. Test from the phone's mobile network. If both paths fail, diagnostics continue to report the endpoint unreachable instead of treating an offline route as ready.
296
302
  - **LAN access stops after switching networks**: if the log reports `saved LAN interface "XXX" is not connected`, the saved adapter is unavailable. DSH keeps running while Mobile Access stays dormant and retries when the adapter returns. If you have moved to a different adapter, configure LAN again or re-run `setup`. If mobile access is not needed, disable `mobile-access` in `cordis.patch.yml`.
@@ -316,6 +322,7 @@ On macOS, local network, self-hosted FRP, and the own reverse proxy work; the th
316
322
 
317
323
  | DSH Mobile plugin | Verified DeepSeek Harness version |
318
324
  | --- | --- |
325
+ | `0.5.3` | `0.1.7-alpha.2`, `0.1.7-rc.1`, `0.1.7-rc.2`, `0.2.0-rc.1`, and `0.2.0-rc.2` (source contract, npm installation, isolated pairing, mobile-page boot, and WebSocket Workspace baseline) |
319
326
  | `0.5.2` | `0.1.7-alpha.2`, `0.1.7-rc.1`, `0.1.7-rc.2`, and `0.2.0-rc.1` (carried forward from earlier verification); `0.2.0-rc.2` (source contract, isolated pairing, mobile-page boot, and WebSocket Workspace baseline) |
320
327
  | `0.5.1` | `0.1.7-alpha.2`, `0.1.7-rc.1`, `0.1.7-rc.2`, and `0.2.0-rc.1`; official Desktop `0.1.7-rc.2` (administration surface) |
321
328
  | `0.5.0` | `0.1.7-alpha.2`, `0.1.7-rc.1`, and `0.1.7-rc.2`; official Desktop `0.1.7-rc.2` (administration surface) |
@@ -368,6 +375,8 @@ npm ci
368
375
  npm run verify
369
376
  ```
370
377
 
378
+ After building question-card or mobile-layout changes, run `npx playwright install chromium --only-shell`, `npm run smoke:question-fixes`, and `npm run smoke:native-layout` to check scrolling, keyboard behavior, and geometry using the built client.
379
+
371
380
  The real browser-startup smoke uses a temporary DSH home, OS-assigned loopback ports, and Chromium pairing. It neither reads an existing user profile nor sends a model request. Development-branch CI tests DSH `0.1.7-alpha.2`, `0.1.7-rc.1`, `0.1.7-rc.2`, `0.2.0-rc.1`, and `0.2.0-rc.2`; install DSH `0.2.0-rc.2` in a separate directory so it does not replace the plugin's development dependencies:
372
381
 
373
382
  ```powershell
package/README.md CHANGED
@@ -31,14 +31,14 @@
31
31
 
32
32
  > DSH Mobile 是 DeepSeek Harness 社区插件,原生 App 仅支持 Android。
33
33
  >
34
- > **当前版本:0.5.2**。共享 frps 时使用稳定的独立代理名,并明确提示代理名或端口冲突;解除 DSH 预发布版的依赖版本拦截。[更新记录](CHANGELOG.md#052---2026-09-29)。
34
+ > **当前版本:0.5.3**。优化提问卡片、折叠过程间距和第三方插件输入栏布局,改善语音输入期间的焦点与常亮处理,并补充设置保存故障排查。[更新记录](CHANGELOG.md#053---2026-10-01)。
35
35
  >
36
- > **升级提醒**:插件与 Android App 可同步更新至 0.5.2。App 本次只同步版本信息;0.5.1 App 与现有配对无需重装或重新配对。[兼容说明](#兼容性)。
36
+ > **升级提醒**:插件与 Android App 可同步更新至 0.5.3。App 本次仅同步版本信息,现有设备无需重新配对。[兼容说明](#兼容性)。
37
37
 
38
38
  <p align="center">
39
- <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.5.2/dsh-mobile-android-v0.5.2.apk"><img src="assets/brand/app-icon-rounded.svg" alt="DSH Mobile 安卓应用图标" width="72" height="72"></a><br>
40
- <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.5.2/dsh-mobile-android-v0.5.2.apk"><strong>下载 Android App 0.5.2</strong></a><br>
41
- <sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.5.2">版本说明与校验文件</a></sub>
39
+ <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.5.3/dsh-mobile-android-v0.5.3.apk"><img src="assets/brand/app-icon-rounded.svg" alt="DSH Mobile 安卓应用图标" width="72" height="72"></a><br>
40
+ <a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.5.3/dsh-mobile-android-v0.5.3.apk"><strong>下载 Android App 0.5.3</strong></a><br>
41
+ <sub><a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.5.3">版本说明与校验文件</a></sub>
42
42
  </p>
43
43
 
44
44
  DSH Mobile 是一个 DeepSeek Harness 插件,让手机浏览器或 Android App 通过局域网,或可选的 Tailscale Funnel、cpolar、cloudflared、自建 FRP 或自有反向代理远程通道连接电脑,继续使用同一份会话、工作区、消息和工具。电脑端分别启停局域网与远程访问、分别管理配对授权;Android App 统一显示已配对电脑。插件不修改 DeepSeek Harness 源码。
@@ -212,6 +212,8 @@ Android App 用一个“已配对设备”列表同时显示多台电脑:局
212
212
 
213
213
  ## 第三方插件适配
214
214
 
215
+ 0.5.3 包含默认启用、可独立启停的 `dsh-mobile-question-fixes` 提问卡片组件。在 DSH 插件详情的组件列表中关闭它,可立即恢复原卡片样式。长问题、选项和底部按钮在有限高度的卡片内共同滚动,收起时标题最多显示两行;手机浏览器的触屏 Enter 保留换行,Android App 则仅在软键盘已打开且未连接实体键盘时使用该行为。答案草稿仍由 DSH 管理,不另存到浏览器 localStorage。
216
+
215
217
  移动适配保持 DSH 原有的工作区、任务管理、终端和文件面板入口,不会把第三方插件内容隔离成另一套页面。下面的宽屏截图展示 Android App 在宽屏下的布局。App 会根据屏幕宽度自适应:手机使用抽屉和浮层,宽屏使用并排面板;两种布局共享相同的功能和连接方式。第三方插件仍由 DSH 自己加载,移动层负责适配布局与连接,不修改 DeepSeek Harness 源码。
216
218
 
217
219
  兼容条件与 WebSocket 放行规则:
@@ -292,6 +294,10 @@ flowchart LR
292
294
 
293
295
  ## 故障排查
294
296
 
297
+ - **内测声明无法确认或设置无法保存**:先在电脑端直接打开 DSH 的本机地址,查看终端日志或浏览器 Network 中失败的设置请求。连接诊断不检查设置写入;保留现有配置及 `settings.yaml.imported`,按具体错误排查。相同界面提示可能有不同原因,见 [DSH #860](https://github.com/deepseek-ai/deepseek-harness/discussions/860)。
298
+ - **设置请求返回 HTTP `403`**:先检查 DSH 的 Host/Origin 信任校验及代理响应。检查 iframe、代理和浏览器扩展是否改变请求来源,使用本机直接访问验证;保持信任检查启用。
299
+ - **错误包含 `profile reload requires the root Include entry`**:该错误发生在 DSH 重载 profile 时。[Issue #132](https://github.com/saya-ch/dsh-mobile/issues/132) 报告过加载两份 `dsh-app-boot` 导致此错误,但单凭提示不能确认原因。核对实际运行的 DSH 命令与当前 profile 的版本和安装路径,保留配置并按 [DSH 官方文档](https://deepseek-harness.github.io/deepseek-harness/)修复依赖;仍失败时将版本、模块路径及脱敏后的完整错误提交上游。
300
+ - **设置错误包含 `EACCES`、`EPERM` 或锁文件操作失败**:检查报错文件的权限及启动环境注入的文件操作工具。锁清理失败也可能发生在写入之后,先核对实际保存结果。
295
301
  - **手机出现另一插件的“输入配对码”页面**:`dsh-remote-web-ui` 与 DSH Mobile 各有独立的远程通道和配对码,不能交叉使用。若电脑端连接诊断提示“第三方远程插件”,先关闭该插件的远程访问并刷新,再扫描 DSH Mobile 当前二维码。仅安装该插件而未启用它的远程接管,不会触发这条诊断。
296
302
  - **远程诊断显示“电脑经代理可访问”**:诊断先直连,失败后才尝试 DSH 进程环境变量中的 HTTP 代理;`NO_PROXY` 可排除目标。此结果只说明电脑经代理完成 HTTPS 探测,不代表手机或实际隧道可用。请用手机流量实际连接;若直连和代理都失败,报告仍为不可达,不会把离线误报为就绪。
297
303
  - **切换网络后局域网访问不可用**:如果日志提示 `saved LAN interface "XXX" is not connected`,表示保存的网卡当前未连接。DSH 会继续运行,移动访问暂时休眠,原网卡恢复后会自动重试;若已改用新网卡,请重新完成局域网配置或重跑 `setup`。暂时不用移动访问时,可以在 `cordis.patch.yml` 中禁用 `mobile-access`。
@@ -317,6 +323,7 @@ macOS 上局域网、自建 FRP 与自有反向代理可用;三个托管组件
317
323
 
318
324
  | DSH Mobile 插件 | 验证支持的 DeepSeek Harness 版本 |
319
325
  | ----------------------------------------- | -------------------------------------------------------------- |
326
+ | `0.5.3` | `0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2`、`0.2.0-rc.1`、`0.2.0-rc.2`(源码契约、npm 打包安装、隔离配对、移动页面启动及 WebSocket 工作区读取) |
320
327
  | `0.5.2` | `0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2`、`0.2.0-rc.1`(延续既有验证);`0.2.0-rc.2`(源码契约、隔离配对、移动页面启动和 WebSocket 工作区读取) |
321
328
  | `0.5.1` | `0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2`、`0.2.0-rc.1`;官方 Desktop `0.1.7-rc.2`(桌面管理入口) |
322
329
  | `0.5.0` | `0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2`;官方 Desktop `0.1.7-rc.2`(桌面管理入口) |
@@ -369,6 +376,8 @@ npm ci
369
376
  npm run verify
370
377
  ```
371
378
 
379
+ 修改提问卡片或移动布局时,完成构建后运行 `npx playwright install chromium --only-shell`、`npm run smoke:question-fixes` 和 `npm run smoke:native-layout`,检查实际客户端产物的滚动、键盘与布局行为。
380
+
372
381
  真实启动冒烟另用临时 DSH Home、随机回环端口和 Chromium 配对,不访问现有用户配置,也不发送模型请求。开发分支的 CI 分别测试 DSH `0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2`、`0.2.0-rc.1` 与 `0.2.0-rc.2`;本机可把 DSH `0.2.0-rc.2` 装在独立目录,避免替换插件的开发依赖:
373
382
 
374
383
  ```powershell
package/cordis.patch.yml CHANGED
@@ -29,3 +29,9 @@
29
29
  allowedCidrs: [127.0.0.0/8]
30
30
  tls:
31
31
  mode: disabled
32
+
33
+ # Keep long question titles and answer options in one card scrollport.
34
+ # The component switch restores the upstream layout immediately.
35
+ - id: dsh-ui-fixes
36
+ name: dsh-mobile-question-fixes
37
+
package/lib/client.js CHANGED
@@ -2735,7 +2735,9 @@ window.__ModuleLoader__.load({
2735
2735
  [data-dsh-mobile-history-loader] { position:relative !important; min-height:1px !important; }
2736
2736
  [data-dsh-mobile-history-loader] button:not(:disabled) { position:absolute !important; width:1px !important; height:1px !important; margin:-1px !important; padding:0 !important; clip-path:inset(50%) !important; opacity:0 !important; overflow:hidden !important; pointer-events:none !important; }
2737
2737
  [data-dsh-mobile-history-loader] button:disabled { min-height:28px !important; padding:4px 12px !important; }
2738
- [data-dsh-mobile-message-column] { box-sizing:border-box !important; width:100% !important; max-width:none !important; margin:0 !important; padding:0 !important; gap:10px !important; }
2738
+ /* DSH's sibling margins exclude hidden and empty Chat Node Seats. Keep that
2739
+ spacing rule so searchable hidden seats contribute no folded-turn gaps. */
2740
+ [data-dsh-mobile-message-column] { box-sizing:border-box !important; width:100% !important; max-width:none !important; margin:0 !important; padding:0 !important; gap:0 !important; --dsh-chat-flow-gap:10px; }
2739
2741
  [data-dsh-mobile-message-column] > * { width:100% !important; max-width:100% !important; }
2740
2742
  [data-dsh-mobile-message-column] [data-disclosure-row] { box-sizing:border-box !important; display:grid !important; grid-template-columns:16px minmax(0,1fr) !important; grid-auto-rows:auto !important; align-items:center !important; column-gap:6px !important; width:100% !important; height:auto !important; min-height:40px !important; padding:4px 0 !important; }
2741
2743
  [data-dsh-mobile-message-column] [data-disclosure-row] > [class*="_leading"] { grid-column:1 !important; grid-row:1 !important; margin-right:0 !important; }
@@ -2757,12 +2759,27 @@ window.__ModuleLoader__.load({
2757
2759
  /* The context ring is a composer-metadata _root too, so the metadata rule
2758
2760
  above stretched it across the whole dock. It cannot shrink, which left the
2759
2761
  session statistics beside it a few pixels wide, every number clipped to an
2760
- ellipsis. Put the numbers in a wrapping left column and the ring in a right
2761
- column it cannot grow out of: a phone then shows every number in full, and
2762
- a wide screen keeps the ring beside them on one line. */
2762
+ ellipsis. Keep the ring at its own size and let it hold the row the numbers
2763
+ are on. */
2763
2764
  [data-dsh-mobile-center] [data-composer-card] ~ * [class*="_root"]:has(> [class*="_trigger"][aria-haspopup="dialog"]) { width:auto !important; max-width:none !important; flex:0 0 auto !important; }
2764
- [data-dsh-mobile-center] [class*="_dock"]:has([data-composer-stats]) { display:grid !important; grid-template-columns:minmax(0,1fr) max-content !important; align-items:center !important; column-gap:12px !important; }
2765
- [data-dsh-mobile-center] [data-composer-card] ~ * [class*="_root"][data-composer-stats] { box-sizing:border-box !important; width:auto !important; max-width:none !important; flex:0 1 auto !important; flex-wrap:wrap !important; justify-content:flex-start !important; row-gap:2px !important; }
2765
+ /* Slot entries participate in this flex layout through display:contents.
2766
+ Keep core statistics and the meter together; extra entries use their own
2767
+ rows instead of taking the statistics' remaining width on phones. */
2768
+ [data-dsh-mobile-center] [class*="_dock"]:has([data-composer-stats]) { display:flex !important; flex-wrap:wrap !important; justify-content:flex-start !important; align-items:center !important; column-gap:12px !important; row-gap:2px !important; width:100% !important; max-width:100% !important; }
2769
+ /* Dock row gaps replace the single-row metadata's negative margin. */
2770
+ [data-dsh-mobile-center] [class*="_dock"]:has([data-composer-stats]) > *,
2771
+ [data-dsh-mobile-center] [class*="_dock"]:has([data-composer-stats]) [data-slot="conversation.composer.dock"] > * { margin-bottom:0 !important; }
2772
+ [data-dsh-mobile-center] [class*="_dock"]:has([data-composer-stats]) [data-slot="conversation.composer.dock"] > :not([data-composer-stats]) { flex:0 1 100% !important; min-width:0 !important; max-width:100% !important; }
2773
+ [data-dsh-mobile-center] [class*="_dock"]:has([data-composer-stats]) [data-composer-stats] { order:-2 !important; }
2774
+ [data-dsh-mobile-center] [class*="_dock"]:has([data-composer-stats]) > [class*="_root"]:has(> [class*="_trigger"][aria-haspopup="dialog"]) { order:-1 !important; }
2775
+ /* Interactive pills expose complete values in their labels and dialogs.
2776
+ Static compact or untimed pills have no disclosure, so they may wrap. */
2777
+ [data-dsh-mobile-center] [data-composer-card] ~ * [class*="_root"][data-composer-stats] { box-sizing:border-box !important; width:auto !important; max-width:none !important; flex:1 1 0 !important; flex-wrap:nowrap !important; justify-content:flex-start !important; overflow:hidden !important; row-gap:2px !important; }
2778
+ [data-dsh-mobile-center] [data-composer-stats] > * { min-width:0 !important; }
2779
+ [data-dsh-mobile-center] [data-composer-stats] [class*="_pill"] { display:block !important; min-width:0 !important; white-space:nowrap !important; overflow:hidden !important; text-overflow:ellipsis !important; }
2780
+ [data-dsh-mobile-center] [data-composer-stats] [class*="_pill"] svg { display:inline-block !important; vertical-align:-2px !important; margin-right:6px !important; }
2781
+ [data-dsh-mobile-center] [data-composer-stats]:has(span[class*="_pill"]) { flex-wrap:wrap !important; overflow:visible !important; }
2782
+ [data-dsh-mobile-center] [data-composer-stats] span[class*="_pill"] { flex:0 1 auto !important; max-width:100% !important; white-space:normal !important; overflow:visible !important; overflow-wrap:anywhere !important; text-overflow:clip !important; }
2766
2783
  /* Message runtime details are inline on desktop. Give the clock/runtime
2767
2784
  label its own wrapping row on narrow screens so TTFT and throughput do
2768
2785
  not push the action buttons or clip at the viewport edge. */
@@ -3797,6 +3814,107 @@ window.__ModuleLoader__.load({
3797
3814
  };
3798
3815
  }
3799
3816
  //#endregion
3817
+ //#region src/voice-session.ts
3818
+ /** Screen wake-lock ownership and mobile composer focus during DSH dictation. */
3819
+ /** Voice phases during which the user is dictating. */
3820
+ const DICTATING_PHASES = [
3821
+ "requesting",
3822
+ "recording",
3823
+ "transcribing"
3824
+ ];
3825
+ /** Whether DSH's published voice phase requires an active dictation surface. */
3826
+ function phaseIsDictating(phase) {
3827
+ return typeof phase === "string" && DICTATING_PHASES.includes(phase);
3828
+ }
3829
+ /** The wake lock API, or undefined where the browser does not offer it. */
3830
+ function wakeLockApi() {
3831
+ const api = navigator.wakeLock;
3832
+ return api === void 0 ? void 0 : api;
3833
+ }
3834
+ /**
3835
+ * Hold an available screen wake lock while DSH dictation is active and visible.
3836
+ * @param allowKeyboardDismissal Whether a mobile composer may lose text focus when capture starts.
3837
+ * @returns A disposer that releases held and subsequently returned wake locks.
3838
+ */
3839
+ function installVoiceSession(allowKeyboardDismissal = true) {
3840
+ const api = wakeLockApi();
3841
+ if (api === void 0 && !allowKeyboardDismissal) return () => {};
3842
+ let sentinel = null;
3843
+ let pending = false;
3844
+ let disposed = false;
3845
+ let generation = 0;
3846
+ let lastRow = null;
3847
+ let lastVisible = !document.hidden;
3848
+ const currentRow = () => Array.from(document.querySelectorAll("[data-voice-activity]")).find((row) => phaseIsDictating(row.getAttribute("data-voice-activity"))) ?? null;
3849
+ const releaseLock = (lock) => {
3850
+ if (!lock.released) lock.release().catch((_error) => {});
3851
+ };
3852
+ const release = () => {
3853
+ const held = sentinel;
3854
+ sentinel = null;
3855
+ if (held !== null) releaseLock(held);
3856
+ };
3857
+ const acquireIfWanted = () => {
3858
+ if (api === void 0 || disposed || pending || sentinel !== null || document.hidden) return;
3859
+ const row = currentRow();
3860
+ if (row === null) return;
3861
+ const requestedGeneration = generation;
3862
+ pending = true;
3863
+ api.request("screen").then((lock) => {
3864
+ lock.addEventListener("release", () => {
3865
+ if (sentinel === lock) sentinel = null;
3866
+ });
3867
+ if (disposed || document.hidden || requestedGeneration !== generation || currentRow() !== row) releaseLock(lock);
3868
+ else if (!lock.released) sentinel = lock;
3869
+ }, (_error) => {}).finally(() => {
3870
+ pending = false;
3871
+ if (!disposed && requestedGeneration !== generation) acquireIfWanted();
3872
+ });
3873
+ };
3874
+ const dismissKeyboard = (row) => {
3875
+ if (!allowKeyboardDismissal) return;
3876
+ if (!(window.__DSH_MOBILE_NATIVE__ !== void 0 ? window.__DSH_MOBILE_KEYBOARD_STATE__?.noHardwareKeyboard === true : window.matchMedia("(pointer:coarse) and (hover:none)").matches)) return;
3877
+ const active = document.activeElement;
3878
+ const composer = row.closest("[data-composer-card]");
3879
+ if (active instanceof HTMLElement && composer?.contains(active) && active.closest("[data-composer-input]") !== null && (active.isContentEditable || active instanceof HTMLTextAreaElement || active instanceof HTMLInputElement)) active.blur();
3880
+ };
3881
+ const apply = () => {
3882
+ const row = currentRow();
3883
+ const visible = !document.hidden;
3884
+ const changed = row !== lastRow || visible !== lastVisible;
3885
+ if (changed) {
3886
+ generation++;
3887
+ release();
3888
+ if (row !== null && row !== lastRow && visible) dismissKeyboard(row);
3889
+ }
3890
+ lastRow = row;
3891
+ lastVisible = visible;
3892
+ if (changed) acquireIfWanted();
3893
+ };
3894
+ const containsVoiceRow = (node) => node instanceof Element && (node.matches("[data-voice-activity]") || node.querySelector("[data-voice-activity]") !== null);
3895
+ const observer = new MutationObserver((records) => {
3896
+ if (records.some((record) => record.type === "attributes" || [...record.addedNodes, ...record.removedNodes].some(containsVoiceRow))) apply();
3897
+ });
3898
+ observer.observe(document.documentElement, {
3899
+ subtree: true,
3900
+ childList: true,
3901
+ attributes: true,
3902
+ attributeFilter: ["data-voice-activity"]
3903
+ });
3904
+ const onVisibility = () => {
3905
+ apply();
3906
+ };
3907
+ document.addEventListener("visibilitychange", onVisibility);
3908
+ apply();
3909
+ return () => {
3910
+ disposed = true;
3911
+ generation++;
3912
+ observer.disconnect();
3913
+ document.removeEventListener("visibilitychange", onVisibility);
3914
+ release();
3915
+ };
3916
+ }
3917
+ //#endregion
3800
3918
  //#region src/local-admin-host.ts
3801
3919
  /** Browser-safe desktop-admin Host checks. Do not import Node APIs here. */
3802
3920
  const DESKTOP_ADMIN_HEADER = "x-dsh-mobile-desktop-admin";
@@ -8769,6 +8887,7 @@ window.__ModuleLoader__.load({
8769
8887
  style.dataset.plugin = "dsh-mobile";
8770
8888
  style.textContent = desktopAdmin ? CONTROL_STYLES : NATIVE_MOBILE_STYLES;
8771
8889
  document.head.append(style);
8890
+ const removeVoiceSession = installVoiceSession(!desktopAdmin);
8772
8891
  if (!desktopAdmin) {
8773
8892
  const removeSettingsAction = ctx.slots.inject("settings.general.item", () => {
8774
8893
  const removeSwitchComputer = ctx.slots.register({
@@ -8795,6 +8914,7 @@ window.__ModuleLoader__.load({
8795
8914
  removeSettingsAction();
8796
8915
  removeCustom();
8797
8916
  removeSurface();
8917
+ removeVoiceSession();
8798
8918
  style.remove();
8799
8919
  };
8800
8920
  }
@@ -8840,6 +8960,7 @@ window.__ModuleLoader__.load({
8840
8960
  });
8841
8961
  return () => {
8842
8962
  removeControl();
8963
+ removeVoiceSession();
8843
8964
  style.remove();
8844
8965
  };
8845
8966
  }, "dsh-mobile: stock mobile adaptation and local control");