# codex-ui
**让 DSH Web 呈现 Codex 的外观:窗口边缘、侧栏分界、模型选择器与推理功率轨、输入区、主题色**
[English](README.md) · [更新日志](CHANGELOG.zh-CN.md) · [MIT](LICENSE)
[](LICENSE)
[](https://github.com/deepseek-ai/deepseek-harness)
[](https://nodejs.org/)
[](https://github.com/rinDBeans/codex-ui/actions/workflows/ci.yml)
> codex-ui 是社区维护的 DeepSeek Harness(DSH)界面插件,并非 DeepSeek AI 官方产品。
DSH Web 的界面元素按 Codex 复刻:窗口边缘的阴影与发丝线、侧栏分界线、模型与推理等级菜单、输入区结构、浅色与深色主题色。取值来自 Codex 实机截图与取色面板,逐像素测量,测量过程写在 README 的「实测值」一节,参考图存在 `assets/reference/`。
## 宿主兼容性
本工作区对照 DSH `0.1.7-rc.1`(npm 全局安装)与 `0.1.7-rc.2`(Windows 桌面壳的 `app.asar`)开发;
0.7.0 的全套验收在 npm 发布的 `@deepseek-ai/dsh@0.1.7-rc.2` 上跑(夹具读它的 `node_modules` 与由它打包的 `app.asar` 各一遍,
真 GUI 用它起的 `dsh web`,见「宿主与浏览器」)。夹具直接读宿主 shipped 的 CSS 与渲染代码,宿主升级后若结构变化,夹具断言会失败。
## 功能
| 编号 | 内容 |
|---|---|
| ⑳ | **模型选择器 B 面**(默认开):自建组件顶替 composer 的模型位 —— 模型列表(按提供方分组、带说明)+ Codex 推理等级**功率轨**(24px 轨 / 4px 档位点 / 28px 白拇指,真拖拽、←/→/Home/End,档位来自数据);往返期间乐观显示新档并转圈、列表不清空(乐观值优先取宿主的 `pending`,宿主没给就用席位自己记的那一笔 —— 0.1.7-rc.1 就没有这个字段);**滑杆形态对齐 dsh-claude-style**(槽 26px/8px 圆角、填充 26% 墨、旋钮 16×30、槽下 更快/更强 一行),**顶档换成紫色点阵**(5 行 hash 散相方块,亮 #8b7ad0 / 暗 #9d8ce0,`prefers-reduced-motion` 与脚本的 `data-reduced-motion` 都会让它停);触发器档位文字模糊交叉淡入。设置里可关 |
| ⑫ | 模型选择器 A 面(关掉 ⑳ 时的宿主原生菜单):不透明白菜单(圆角 18px)、行高 28px、行圆角 13px(同心:18 − 内衬 5)、勾选列常驻、pending 转圈 |
| ⑬ | 输入区顶栏消隐、面板按钮保留官方图标 |
| ⑬c | 顶栏两格**放开**:注册在两个槽里的条目恢复渲染(子代理后代计数 / jobs roster / 预设徽标 / 在应用中打开);视图页签仍隐去。条目本身条件渲染,所以常态顶栏与放开前一致 |
| ⑬d | **「轨迹」视图的退出出口**:⑬ 隐去页签条之后,「轨迹」只剩入口没有出口 —— 工具卡展开后的 Inspect 走 `openView('trajectory', callId)`,回来的路却只有那条页签条。轨迹视图显示时在视图区左下角浮一个「← 对话」,点击就是**点那一格页签本身**(与用户手点同一条 `selectView` 回调,不绕宿主内部 API)。结构在 `src/client/trajectory-exit.js`,外观在 `skins/codex-ink/trajectory-exit.css`。「对话是哪一格」先按 `aria-selected` 标定、再按页签文字兜底,两者都没有就**不出按钮**;页签条哪天重新可见本层自动让位。任何一步与宿主结构不符都只 warn 并跳过 |
| ⑭ | 输入卡:圆角(卡片 20/25,上栏条 = 卡片同值)、阴影、几何、工具条、hero 布局;贴在上沿叠放的后续消息队列并入同一族(顶部圆角 = `--dsw-radius-card`,且不吃 40px 背景模糊) |
| ⑯ | 右栏展开选择组件:无描边无底色、行高 52px、图标 20px、快捷键灰底 pill |
| ⑰ | composer 底部控件:加号默认无底色框、悬停才填;模型与权限控件同套悬停胶囊,且与加号一样是**全圆角**(Codex 参考图实测 R = h/2) |
| ② | 侧栏配色对齐 Codex 亮色侧栏;侧栏列对齐工作区列表行,行状态按宿主自己的语义映射 —— 当前会话(`aria-selected`)单独给一层填充,与悬停区分开(**实测 0.08 vs 0.04**,宿主原本两者同色) |
| ②c | 会话窗口边缘:0.5px 发丝线加 24px 全向环境影 |
| ②d | 右栏面板:左沿只留发丝线、影只往上泄;压掉 dockkit 的 1px 深边框;滚动区跟随皮肤滚动条约定(`::-webkit-scrollbar`,与菜单同一组令牌) |
| ②e | 右分界线拖拽柄:悬停时中段最深、两端淡出的渐变 |
| ⑱ | 插件管理 → codex-ui 组合包页的设置卡:主题 / 强调色 / 背景 / 前景 / UI 字体 / 代码字体 / 半透明侧边栏 / Codex 模型选择器 / 对比度 |
| ㉑ | **设置模态框全页 Codex 化**:设置对话框换成分组侧栏(「← 返回应用」一行、按文字过滤宿主条目的搜索框、分组标题)、内容区页头、每个子页面的白卡细边与贴底保存栏。结构在 `src/client/settings-modal.js`(只用宿主锚点,宿主节点绝不移动或克隆;隐藏态靠耐久的 `data-*` 属性),外观在 `skins/codex-ink/settings-modal.css`。任何一步与宿主结构不符都只 warn 并跳过 —— 这一层会降级,绝不抛 |
## 界面
浅色:会话窗口边缘与右栏。

输入区底栏:默认无框,悬停出胶囊。

模型选择器 B 面(真 `dsh web` 实例截图):模型列表 + 推理等级功率轨,亮 / 暗。

关掉 B 面时,宿主原生菜单(A 面)的 pending 状态。

## 安装
标准的 DSH 插件安装,不需要本仓库的任何脚本:
```powershell
dsh plugin --profile web add link:<仓库绝对路径> # 加 link 依赖,并把包名写进 dsh.profile.bundles
dsh web
dsh plugin --profile web remove codex-ui # 卸载:依赖与 bundles 条目一起去掉
```
不想动日常 profile,先建一个试用 profile:
```powershell
dsh codex --from-default-profile web --dump-config # 由 web 模板建出 codex profile
dsh plugin --profile codex add link:<仓库绝对路径>
dsh codex --port 3099 --no-open
```
桌面应用的 profile 由应用独占,CLI 会拒绝写入:在应用里打开侧栏「插件」→「添加插件」,填仓库绝对路径
(对话框接受包名、Git 地址、压缩包或本地绝对路径)。`@deepseek-ai/schemastery` 声明在 `peerDependencies` 里,由宿主提供。
`link:` 指向仓库本身:改完样式或组件跑一次 `npm run build`,刷新页面即生效;改了 `index.js` 的 `Config` 要重启 `dsh web`
(宿主半在启动时加载)。
**从 0.6.0 及更早版本迁移**:`install-plugin.mjs` 已删除。它装出来的是 `profiles//vendor/codex-ui` 副本加
`node_modules/codex-ui` junction,注册方式是 `dsh.profile.bundles` 里的包名或 profile `cordis.patch.yml` 里手写的 `- insert:`。
换成标准安装前先把这些删掉 —— 同一个 id 注册两次,市场校验会判 fail 并把插件停用。
同一份样式正本也可由皮肤加载器收录:
```powershell
node scripts/install-skin.mjs # 与 $DSH_HOME/skins/codex-ink 逐文件 SHA256 比对
node scripts/install-skin.mjs --write # 有漂移则覆盖
```
## 开发
```powershell
npm run build # 由 skins/codex-ink 与 src/client 重新生成 theme.css 与 client.js
node scripts/build.mjs --check # 只比对产物是否过期,不落盘
```
构建零依赖,只有 `scripts/build.mjs` 一份实现:九份样式去注释、作用域化到 `html[data-codex-ui]`,写出 `theme.css`;
`src/client/` 的 ES 模块按依赖顺序打成一个经典脚本 `client.js`(DSH 的 `__ModuleLoader__.load` 形态,样式内联在里面,
`react` 等宿主包走 loader 给的 `require`)。只认两种 import(`import { a, b as c } from '…'`、`import * as ns from '…'`)
和三种 export(`export const | function | class`),其余写法构建直接报错,不会悄悄打错。
加功能:`src/client/` 下加一个模块导出 `installXxx(ctx)`,在 `src/client/index.js` 的 `apply` 里加一行;
要新样式就在 `skins/codex-ink/` 加一份 CSS,登记进 `scripts/build.mjs` 的 `SKIN_PARTS`。必需的宿主服务写进 `inject`
(缺一个插件就不激活),可有可无的走 `ctx.inject([...], cb)` 子作用域,参照模型选择器。
## 目录
| 路径 | 内容 |
|---|---|
| `index.js` `cordis.patch.yml` `package.json` | 插件宿主半(`Config` 设置结构)与清单 |
| `src/client/index.js` | 浏览器半入口:`inject` 与 `apply`,按序装配下面各项 |
| `src/client/stylesheet.js` | 注入皮肤样式(`style[data-plugin]`,卸载与热更新时由宿主收走) |
| `src/client/settings.js` `theme-preview.js` `settings-card.js` | 设置:覆盖层 `