DSH Phone UI
DeepSeek Harness 移动端 UI 适配插件(纯 Client 为主,host 半仅注册设置)。
窄屏(< 768px)下把 DSH 界面适配成手机友好布局,复刻 DeepSeek 网页版的做法。
所有区域改动都是独立 feature,可在 DSH 设置页(设置 → 插件 → 插件配置 → dsh-phone-ui)单独开关,改动即时生效、无需刷新页面。
功能
| 区域(feature) | 默认 | 说明 |
|---|
侧边栏全宽抽屉(sidebar) | 开 | 窄屏下侧边栏改为全宽抽屉:默认隐藏(0 宽),聊天区全宽;点左上角汉堡滑入(280px 覆盖层),点遮罩 / 收起按钮关闭 |
顶栏复刻 DeepSeek 网页版(topbar) | 开 | 窄屏下左汉堡、左对齐标题(不设宽度上限)、标题下模式徽章、Session log 仅图标 |
桌面端(≥768px)不受任何 feature 影响。
架构
lib/index.js host(node)半:注册设置命名空间 phone-ui(schema 由 feature 注册表生成)
lib/client.js 浏览器半主入口:读设置 → 按开关挂载/卸载各 feature(构建生成)
lib/features/index.js feature 注册表:新增区域在这里登记一行
lib/features/sidebar.js 侧边栏抽屉:样式 + 汉堡/遮罩 UI
lib/features/topbar.js 顶栏样式(纯 CSS)
client.js 动态插件形态(形态 A)源码(构建生成)
lib/snippet.js 形态 A 的字符串导出(构建生成)
scripts/build.mjs 构建:内联 features → 生成 client.js / lib/client.js / lib/snippet.js
cordis.patch.yml bundle 补丁层:把插件插入浏览器名册
- 一个 npm 包 = 一个插件:主入口(
lib/client.js)读取 phone-ui 设置,只挂载开启的 feature;每个 feature 挂载独立 try/catch,一个区域出错不影响其他区域。
- 设置开关:host 半用
settings.register('phone-ui', ...) 注册(持久化到 settings.yaml);浏览器半用 settingsScope 订阅,开关一拨即实时生效/失效。设置系统缺席时自动降级为全部开启。
如何新增一个页面区域
- 复制
lib/features/sidebar.js 为 lib/features/<key>.js,改成你的样式 / UI:
key:唯一标识(同时成为设置项的字段名);
title / description:设置页卡片里显示的文案;
defaultEnabled:默认开关;
css:该区域的样式;
mount(ctx, api):挂载逻辑,必须返回卸载函数;只允许使用 api(api.React、api.insertStyle(tagId, css)、api.registerOverlay(id, component)、api.toggleSidebar()),不要直接依赖浏览器全局或环境符号。
- 在
lib/features/index.js 顶部 import 并加入 features 数组(一行)——key / 标题 / 默认开关会自动进入设置 schema 和设置页卡片。
pnpm build 重新生成两个形态,发布即可。
两种使用形态
形态 A:动态 Cordis 插件(代码片段)
把 client.js 内容作为 cordis_define 的 code.client 函数体,运行时加载,临时、进程内(DSH 重启即失效)。适合快速试用。
import { codeClient } from 'dsh-phone-ui/snippet';
// cordis_define({ ..., code: { client: codeClient } })
- 定义插件:
idPrefix 建议用 mnav(3–6 位小写英文字母)。
- 运行:首次
mode: "run";同插件升级 mode: "update"。
- 动态形态没有持久设置页:默认全部开启;想临时关掉某个区域,改
client.js 顶部的 ENABLED 对象(构建后生效)。
形态 B:正式 client 插件(持久、可安装)
作为 npm 包经 dsh plugin 装进 profile,随 profile 长期存续、跨重启可用。
dsh plugin --profile <name> add dsh-phone-ui # 首次安装
dsh plugin --profile <name> update dsh-phone-ui # 升级到新版本
本包通过 package.json 的 dsh.client 声明浏览器半(exports["./client"] → lib/client.js),通过 dsh.bundle.patch(cordis.patch.yml)把自己插入浏览器插件名册;host 半(lib/index.js)注册设置命名空间。设置开关仅在形态 B 下可用。
注意:正式插件路径会随 DSH 的 client 服务/槽位 API 演进。首次启用前建议先用 cordis_inspect_list 核对当前 DSH 版本的 slots / layout / settingsScope 服务与 shell.overlay、settings.plugin.item 槽位;若类名哈希(见「关键选择器」)随 DSH 升级变化,CSS 选择器需同步更新。
文件
| 文件 | 作用 |
|---|
lib/features/*.js | feature 定义(单一事实来源,样式/UI 改这里) |
lib/index.js | 正式插件 host 半:注册 phone-ui 设置命名空间 |
lib/client.js | 正式插件浏览器半(构建生成:features 内联 + 设置开关 + 设置卡片) |
client.js | 动态插件源码(构建生成,code.client 函数体) |
lib/snippet.js | 形态 A 的字符串导出(构建生成) |
cordis.patch.yml | bundle 补丁层:把 client 插件插入名册 |
package.json | 项目元信息 + dsh.client / dsh.bundle 声明 |
实现原理
- 背景:DSH 三列 grid(
sidebar / center / details)由 dsh-client-ui-layout 渲染,用内联 grid-template-columns。关闭的侧边栏在源码 computeColumns 里被硬编码成 56px(sidebar === 0 ? 56 : clamp(...)),断点 SIDEBAR_AUTO_COLLAPSE = 1024,因此窄屏下侧边栏不会归零。
- 本插件:
< 768px 用 !important 把三列强制为 0px 1fr 0px,把侧边栏抽离 grid 流改为 position: absolute 离屏抽屉;同时显式给 center/details 指定 grid-column: 2 / 3,避免侧边栏脱离流后自动重排(初版 bug)。
- 开合状态:完全复用 DSH 自身的
layout.toggleSidebar()(窄屏下翻转 narrowExpanded),CSS 以 frame 上的 data-sidebar-collapsed 属性作为开合开关。因此汉堡 / 遮罩 / 侧边栏内部收起按钮三个入口行为一致,无额外状态、无同步问题。
- UI 注册:
shell.overlay(list 槽,自有 id)注册汉堡按钮 + 遮罩层,不替换任何既有条目。
- 设置开关:host 半
settings.register('phone-ui', z.object({...})) 注册命名空间(settings.yaml 持久化);浏览器半 settingsScope.bind({ namespace: 'phone-ui' }) 订阅,按 value.<key> !== false 判断是否挂载对应 feature,拨开关即实时挂载/卸载。
- 颜色:全部使用主题变量(
--dsw-alias-*、--ds-*),自动适配明暗主题。
关键选择器(当前 DSH 版本)
| 选择器 | 作用 |
|---|
.pI_x6G_frame | 三列 grid 框架 |
.pI_x6G_sidebarCol / _centerCol / _detailsCol | 侧 / 中 / 详情三列 |
[data-sidebar-collapsed] | 侧边栏折叠标记(frame 属性) |
.wSkVaW_header / _titleCluster / _crumbs / _crumb | 会话顶栏 |
.wSkVaW_headerUtilities | 顶栏右侧工具区 |
.SVAs4q_label | 模式徽章(Agent preset) |
.nL4_yW_sessionLogButton | Session log 按钮 |
这些类名是当前版本的构建产物哈希,升级 DSH 后可能变化。更稳的备选选择器:用 div:has(> [data-shell-overlay]) 定位 frame;用 [data-shell-overlay] 定位浮层。
断点
| 区间 | 行为 |
|---|
< 768px | 抽屉 + 手机顶栏(本插件生效) |
768 – 1024px | 保留原 56px 图标栏 |
> 1024px | 原 280px 侧边栏 |
断点常量:@media (max-width: 767px) 中的 767px,改它即可调整阈值。
开发
pnpm build # 改完 lib/features/ 或主入口骨架后重新生成 client.js / lib/client.js / lib/snippet.js
主入口骨架(client.js 与 lib/client.js 中 BEGIN/END FEATURES 标记之外的部分)可直接手改;标记区间由构建脚本从 lib/features/ 重新内联,不要手改。
版本历史
1.1.0:拆分为 feature 模块(lib/features/)+ 设置开关(host 半注册 phone-ui 命名空间,设置页独立开关每个区域,实时生效)。
1.0.0:正式 client 插件(形态 B)+ 代码片段导出(形态 A)。
- 动态插件阶段:
pkg-1 初版抽屉(bug:侧边栏脱离 grid 流后 center 顶到第 1 列变 0 宽);pkg-2 显式 grid-column: 2 / 3 修复;pkg-3 顶栏适配初版(居中 + 标题 220px 上限 + 顶栏 94px,被否);pkg-4 左对齐、标题不设上限、顶栏回落 82px。
License
MIT