把 DSH Web 在手机浏览器上变成接近 App 的体验:侧栏改抽屉、文件预览改为底部弹层、设置弹窗近全宽,并在桌面端完全无感。
- 语言
- JavaScript
- License
- MIT
- 分支
- main
安装
$ dsh plugin --profile web add github:mexiaosqwq/dsh-web-mobile在终端中运行以上命令,通过 dsh CLI 安装此插件。可在右上角切换 Profile。 第一次用 dsh?看这篇新手教程
一句话定位
把 DSH Web 在 ≤1023px 的视口上重排为接近原生 App 的体验:侧栏变成可滑出的抽屉、文件树与预览变成底部弹层、设置弹窗近全宽并把工具栏并入分类栏;在 ≥1024px 时完全无操作。
核心能力
- 侧栏改抽屉:会话头部新增「打开目录」图标按钮,点按抽出/收起抽屉;首页/空白页等没有会话头的阶段,由左上角浮动按钮接管开关
- 文件浏览一键直达:会话头部新增「文件浏览」图标按钮,直接把 dsh-web-ui 的 aionui 资源管理器展开成底部弹层,无需先开抽屉
- 抽屉底部双胶囊:抽屉打开时底部多出「文件浏览」「导出会话日志」两个胶囊按钮;会话日志按钮在没有当前会话时自动禁用
- 状态栏与安全区适配:写入
viewport-fit=cover+theme-color,按env(safe-area-inset-top)把所有可见面顶到刘海之下;安卓系统状态栏颜色随明暗主题切换 - 预览浮层全屏:dsh-web-ui 预览浮层标题栏右侧新增一个全屏切换按钮,按下后浮层填满视口(含安全区),再次按下恢复
- 关闭交互全套:抽屉打开时点击遮罩关闭、按 Esc 关闭(弹窗打开时让位)、点击抽屉里的会话行/任务板/搜索结果收起抽屉;会话行的三点按钮被显式排除,点它打开的菜单不会被收起
- 媒体查询限宽:768–1023px(折叠屏、平板竖屏)下,弹窗与浮层改为居中并限宽至
min(100vw-32px, 720px),避免内容挤在屏幕一角 - 诊断浮条:URL 加
?mobile-nav-debug=1后,右上角出现实时显示视口、断点匹配、帧标记、浮层可见性、最近 5 条 JS 错误的悬浮面板
技术实现
- 语言: TypeScript(React 18 + DSH client SDK);客户端编译为 CommonJS 并由
scripts/build-client.mjs自定义打包器内联成lib/client.js - 关键依赖:
@deepseek-ai/cordis(宿主容器)、@deepseek-ai/dsh-client-runtime(cordis fiber + ClientContext)、@deepseek-ai/dsh-client-ui-slots(slot 注册)、react ^18.2.0(头部 / 抽屉底部 React 组件) - 架构模式: 双面插件。Host 端
src/index.ts:7暴露一个空apply(),仅用于在宿主 Loader 里登记dsh-mobile-nav行;真正的工作全部在 Browser 端src/client/index.tsx:27-97:注入一张移动端<style data-plugin>、注册三个 slot(conversation.session.header.actions、sidebar.footer.action,以及 README 与 AGENTS.md 提到的settings.general.item,但src/client/index.tsx:66-97仅注册前两个)、挂一组生命周期受管的 DOM effect - 入口文件: 客户端
src/client/index.tsx:27的apply(ctx);cordis patch 在cordis.patch.yml:4-5写入一行insert: { id: dsh-mobile-nav, name: '@dsh-external/dsh-mobile-nav' } - 核心机制:
src/client/effects/reconciler-core.ts维护零导入的脏键注册表,按动画帧合并 MutationObserver 突发,src/client/effects/phone-chrome.ts:124-165把这层 DOM-free 引擎接到单一全树观察者上;任务声明自己的脏键(如data-aionui-preview-open)后,只有真正变化的属性才会唤醒相关任务
适用场景
在手机浏览器(包括安卓 Chrome / iOS Safari)上使用 DSH Web、嫌默认三栏布局在窄屏下挤不下的用户;或需要在桌面窗口临时拉窄时仍然可用、又不想失去桌面版体验的开发者。安装一次即可同时改善首页、会话进行中、设置、文件树、预览五个场景。
前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| DSH 客户端 SDK | @deepseek-ai/dsh-client-runtime ^0.1.0-rc.6 等系列(同版本号) | package.json:48-57 声明 peerDependencies,涵盖 locale / runtime / ui-primitives / ui-slots / ui-conversation / ui-layout / ui-settings / ui-sidebar / session-log-export |
| Cordis | @deepseek-ai/cordis ^4.0.1 | package.json:47 声明,由宿主提供 |
| React | ^18.2.0 | package.json:52 声明,仅 peer |
| Node.js | 未声明 | package.json 未设置 engines;运行 pnpm verify 与 pnpm build 的最低 Node 由 @types/react 与 TS 6.x 间接决定 |
| 平台 | 浏览器 | package.json:39-44 声明 dsh.client.platform: "web",无 host 端代码,无原生模块依赖 |
| 宿主注入项 | slots / layout / locale / sessionLogDownload | src/client/index.tsx:19 的 inject = [...] 列表,缺失任意一个 cordis fiber 会拒绝加载 |
| 第三方兼容插件 | dsh-web-ui-all 0.1.14、dshmarket 1.2.2、dsh-usage-stats 0.1.2、dsh-genui 0.8.3 | README.md:50-55 明示;UI 兼容补丁写在 src/client/styles/compat.css.ts |
安装方式
dsh plugin --profile web add github:mexiaosqwq/dsh-web-mobile
配置项
| 配置 | 类型 | 说明 | 默认值 |
|---|---|---|---|
?mobile-nav-debug=1 | URL 查询参数 | 在右上角显示诊断浮条(视口、断点、帧标记、预览/资源管理器列可见性、预览/资源管理器是否打开、当前 phase、最近 5 条 JS 错误)。仅用于手机端排查,不影响功能 | 不传 = 不显示 |
本插件没有 Schema / 配置文件 / 环境变量形式的配置项(src/ 内未读取 process.env、Schema 字段或 localStorage 键,仅调试面板会读取 URL 查询参数)。
常见问题
Q: 安装后需要重启 DSH 吗?
A: 需要。README.md:63 明确写出「装完重启 dsh web」。插件在 host 启动时通过 cordis.patch.yml 注入 dsh-mobile-nav 行,运行中的 dsh web 进程不会动态加载。
Q: 桌面端会被这个插件影响吗?
A: 不会。源码里 src/client/styles/misc.css.ts:140-150 在 (min-width: 1024px) 媒体查询里把所有 [data-mobile-nav=...] 控件设为 display:none !important;同时 src/client/effects/phone-chrome.ts:23-42 的 installMobileEffect 只在 matchMedia('(max-width: 1023px)') 命中时安装副作用。>=1024px 时不创建 MutationObserver、不写 DOM、不注册事件。
Q: 预览浮层全屏按钮在哪儿?怎么用?
A: 仅在 dsh-web-ui 的预览浮层打开时出现在标题栏右侧(约 right: 36px; top: 8px)。点一下预览浮层铺满整个视口(含状态栏安全区),再点一下恢复到底部弹层尺寸。aria-label 会随状态在「全屏预览」/「退出全屏」之间切换。
Q: 点「文件浏览」按钮没反应怎么办?
A: 这通常不是按钮坏了,而是当前已经打开了预览浮层(compat.css.ts:104-115 让预览盖住资源管理器)。先在预览浮层标题栏点收起按钮(chevron),再点「文件浏览」。AGENTS.md:117 明确把这点列入「Pitfalls」。
Q: 升级后界面回到桌面版布局了怎么办?
A: 拉宽窗口或用桌面浏览器(≥1024px)就会回到桌面版;如果在手机视口下仍然是桌面版布局,先确认 dsh web 进程确实重启了,再加 ?mobile-nav-debug=1 看顶部 W 视口宽度与 mq≤1023 是否为 true。
Q: 怎么卸载?
A: dsh plugin --profile web remove dsh-web-mobile,然后重启 dsh web。本插件无 host 端进程、无持久化文件、无外部网络请求,卸载即清空。
上手难度
入门 — 不需要任何代码改动,安装重启即可生效;唯一的开关是排查用的 ?mobile-nav-debug=1 URL 参数,普通用户可以完全忽略。
已知问题与限制
- CSS 依赖
:has()选择器,要求 Chromium 105+;老旧 WebView 上的:has()规则会被静默丢弃,可能出现抽屉/弹层异常。源码在src/client/styles/layout.css.ts:1-3与compat.css.ts大量使用:has() - 当用户系统设置启用了「减少动态效果」,
@media (prefers-reduced-motion: reduce)会让浮层的滑入动画与变换过渡关闭(compat.css.ts:210-216) - 预览浮层与资源管理器存在互斥关系:同时只能开一个;预览打开时点「文件浏览」按钮看似失效是预期行为。
AGENTS.md:117把这点列为排查「按钮看似失效」的优先级检查项 - 抽屉内点击关闭逻辑使用 capture 阶段事件,并显式排除会话行的三点菜单按钮(
phone-chrome.ts:415);如果将来第三方插件的会话行按钮选择器形态变化,需要同步更新这里的选择器,否则会误收起抽屉 - 全树 reconciler 的任务在
src/client/effects/phone-chrome.ts:48-88用模块级installed标志防重复挂载;同一宿主环境下热重载插件会先卸载再重建,期间会有极短的闪烁
尽可能的使 dsh 适配竖屏等移动端设备
效果
| 会话主页(全宽) | 目录抽屉 | 设置界面 |
|---|---|---|
![]() | ![]() | ![]() |
特性
- 状态栏适配:保留系统状态栏,
viewport-fit=cover+env(safe-area-inset-top)避让刘海,theme-color跟随深/浅主题,禁用双击缩放; - 会话全宽 + 抽屉导航:网格改
1fr 0 0,侧栏变 overlay 抽屉;点会话行切换并收起抽屉,行内按钮(三点菜单)不收起; - 会话头部重排:目录按钮 / 会话名称 / 模式徽标按移动端排列,Session log 移到抽屉底部;
- 设置界面适配:官方双栏弹窗改近全宽 sheet——分类标签单行横向滚动,顶部工具栏并入标签行,手机上隐藏「打开配置文件」;
- 文件树 / 预览浮层:Explorer 与 Preview 变圆角底部浮层(滑入动画),文件行一步打开预览,浮层可一键全屏并自动还原;
- 统计栏一行滚动:轮数 / 步骤 / 耗时 / TTFT / 缓存 / token 收进单行横向滚动条;
- 输入区适配:权限胶囊与模型名不重叠,模型名完整显示,切换菜单水平居中;
- 会话行操作菜单:长按/右键会话行出现三点按钮(重命名 / Fork / 归档),弹出时抽屉保持打开;
- 平板适配:768–1023px 下弹窗与浮层限宽居中;桌面端(≥1024px)完全 no-op;
- 诊断:访问
?mobile-nav-debug=1显示悬浮诊断条(视口 / 浮层状态 / JS 错误),手机端问题取证用; - 触觉反馈:通用设置新增「点按振动」开关与强度选择(轻 / 中 / 强),仅移动端生效,桌面端自动隐藏。
更新日志
v1.5.0
新增 / 改进
- 触觉反馈:设置新增「点按振动」开关与强度选择(轻 / 中 / 强,本地存储偏好,跨标签同步),点按控件即时振动,桌面端不显示该设置行。
修复
- 抽屉关闭交互回归:移除旧 MobileNavOverlay 组件时误删的背板点击关闭、Escape 关闭、抽屉内导航点击收起、hero/blank 阶段悬浮按钮四项交互全部恢复;
- preview/explorer 互斥对称:打开 explorer 前先清 preview 标记,收起/关闭路径对称,预览浮层不再误开或残留标记;
- dispose 还原完整:设置工具栏、统计条、aionui sheet 标记在退出移动端布局时回到官方位置,桌面端无残留;
- 预览全屏按钮 aria-label 同步真实动作(「全屏预览」/「退出全屏」)。
内部
- reconciler 热路径优化:抽出零依赖 DOM-free 引擎
reconciler-core(node:test 5 例覆盖生命周期 / 脏路由 / 合并 / 错误隔离),MutationObserver 按脏键路由,8 个任务赋 scopes,流式 flush 从 8/8 降至 5/8; - CSS 模块自包含、effect 分层清理;
- CDP 回归门禁
smoke:cdp:覆盖抽屉 / 桌面 no-op / gitgraph 集成。
兼容插件
- dsh-web-ui 全家桶——0.1.14
- dshmarket——1.2.2
- dsh-usage-stats——0.1.2
- dsh-genui——0.8.3
安装
dsh plugin --profile web add github:mexiaosqwq/dsh-web-mobile
仓库自带构建产物,一条命令直接安装,无 allowBuilds 拦截。装完重启 dsh web。
本地开发:dsh plugin --profile web add link:/path/to/dsh-web-mobile
构建
pnpm install
pnpm build
lib/ 与源码同步入库,改动源码后重新构建再提交。
验证
pnpm verify类型检查;dsh --profile web --dump-config应出现插件层;- 移动端(390px):抽屉开合 / 遮罩 / Escape、设置弹窗适配、会话行三点菜单弹出时抽屉保持、文件/预览浮层;
- 桌面端(≥1024px):与未安装时一致;
- 回归门禁:
DSH_PROBE_SESSION_ID=... pnpm smoke:cdp(需先启动目标 profile,连接默认http://127.0.0.1:3080/)。


