dsh-settings-nav-order
Verified@fish-under-sea/dsh-settings-nav-order · v0.1.5 · MIT · Web UI
DeepSeek Harness Web 设置导航顺序自定义:用「设置 → 设置导航顺序」里的上移/下移把左边这列菜单排成你要的样子,顺序存在浏览器 localStorage 并同步落盘到 $DSH_HOME/dsh-settings-nav-order/state.json(随 Git 同步插件跨机复原),不改任何第三方插件的代码。
Install
dsh plugin add @fish-under-sea/dsh-settings-nav-order Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Published to npm without a public repository. Inspect the package contents before installing.
Tags
Readme
解决什么问题
DSH 设置面板左侧那列菜单(通用设置 / 模型 / 内置插件 / Agent 预设 / 已归档会话 / Git 同步 / …)的顺序写死在每个插件自己的 settings.section 注册里(一个 order 数字),GUI 既没有重排入口,也没有把不用的项收起来的办法。想把自己常用的项挪到上面、或把一年也用不到一次的项藏掉,只能去改别人的插件包——而应用内包在 asar 里(升级即丢),profile 下的包也会被插件更新覆盖。
本插件不碰任何第三方代码:排序靠给导航按钮打 CSS order,隐藏靠打 display:none。DOM 顺序、各插件的代码、React 的 key 全都不用动。
效果
- 按住
⋮⋮把手拖动即可排序,也可点↑/↓精细调整。 - 点「隐藏」把不用的项从左栏收起——插件本身照常工作。
- 保存后立即生效;已隐藏的项在面板里显示成灰色删除线,点「显示」找回。
- 关掉「启用手动排序与隐藏」或点「恢复默认」,立刻回到各插件自己的样子。
- 偏好能跨机复原(0.1.2 起):保存时顺手落成宿主文件
$DSH_HOME/dsh-settings-nav-order/state.json,随dsh-git-sync进配置仓;新机器打开设置页即自动回填,不必手工重排。
安装
# 推荐:随聚合包一起装(见仓库根 README)
dsh plugin --profile <profile> add @fish-under-sea/dsh-settings-nav-order
<profile> 填 DSH 的 profile 名:桌面端 desktop,Web 端 web。
要改源码时才用 link:<本目录的绝对路径>。
装完必须重启 DSH:cordis.patch.yml 的插件行只在启动时展开,刷新页面不够。
使用
- 打开「设置」,在左侧菜单里点最下面的「设置导航顺序」。
- 排序:按住每行左边的
⋮⋮把手拖动;想精细调整也可以点↑/↓。 - 隐藏:在不想看到的项上点「隐藏」——它就从左侧菜单里消失了,插件本身照常工作。
- 点「保存」。左侧菜单立刻变样。
补充:
- 已经隐藏的行在面板里显示成灰色带删除线,点「显示」即可找回。
- 关掉「启用手动排序与隐藏」并保存 = 完全用各插件自己的样子。
- 点「恢复默认」= 清掉保存的顺序与隐藏,同样回到原样。
- 「设置导航顺序」这一页不能隐藏自己:它是唯一能取消隐藏的入口,藏了就再也进不来了。
配置
没有需要你填的配置项 —— 插件行里不需要任何 config,路径是固定的。
数据分两半,各有各的职责:
| 半区 | 位置 | 职责 |
|---|---|---|
| 浏览器半区 | localStorage 键 dsh-settings-nav-order/v1 |
工作副本:决定当次渲染,读写都不经网络 |
| 浏览器半区 | localStorage 键 dsh-settings-nav-order/v1.synced |
指纹:上一次与宿主对齐成功的那份原文,用来区分「本地没动过」与「本地有还没推上去的改动」 |
| 宿主半区 | $DSH_HOME/dsh-settings-nav-order/state.json |
镜像:保存时写入、启动时回填;dsh-git-sync 的白名单收录它 |
三处都是同一份形状:
{ "enabled": true, "order": [{ "name": "模型", "index": 0 }], "hidden": [{ "name": "Web 插件", "index": 0 }] }
- 形状守卫只接受
{enabled, order, hidden}三键,多余字段丢弃。 enabled只有显式false才算停用;其余情况(缺失、null、非布尔)一律视为启用。order/hidden每项是{name, index}——同名项用「同名第几个」区分,菜单增删后旧配置不会把别的项挤错位。
为什么需要宿主半区
这份偏好真正生效的地方是浏览器 localStorage,而 dsh-git-sync 在宿主进程里够不着它——宿主半区把它落成文件,同步插件才能按相对路径搬运。少了这条,换机后设置菜单的顺序与隐藏项复原不了。
跨机复原(0.1.2 起)
- 在本机改好顺序 / 隐藏项并点保存——插件顺手把快照推给宿主,落成上面那个
state.json; - 设置 → Git 同步 → 一键同步(需要
dsh-git-sync≥ 0.2.3,它的白名单含这个文件); - 新机器把配置仓还原到本机后,打开设置页即自动恢复顺序与隐藏项,不必手工重排。
启动对账(reconcile)分四种情形,逐条对应一次真实场景:
| 情形 | 动作 |
|---|---|
| 宿主还没有快照(第一次同步、新机器、文件被清掉) | 把本地偏好推上去(升级前就存在的顺序因此也能进仓库) |
| 本地没有配置(换机、清过站点数据) | 采用宿主快照——复原走的就是这条 |
| 本地与指纹逐字相同(本地没动过) | 采用宿主里更新的那份(另一台机器改过) |
| 本地与指纹不同(有未同步的改动) | 保留本地并推给宿主,绝不静默丢掉你刚做的调整 |
指纹键存的是「上一次与宿主对齐成功的那份原文」,逐字比较,不做语义等价。这个键本身丢了最坏也只是多重推一次。
任何一步失败(宿主没起、路由缺失、返回 500、body 不是 JSON)都只是「这次不对账」:本地顺序照旧,下次启动会自己重试。宿主回的快照形状不对时也一样——只会被当成「没有快照」。
保存后的回执(不假装成功)
点「保存」或「恢复默认」之后,面板中间会多一行说明这次宿主文件的结果:
| 回执 | 含义 |
|---|---|
| 宿主文件:正在写入… | 请求已发出,还没回来 |
| 宿主文件:已同步——Git 同步会带上这份偏好,换机可复原。 | 文件已写成功,指纹已记 |
| 宿主文件:未同步——本地已生效;宿主通道恢复后,下次打开设置页会自动重推。 | 写失败(宿主没起 / 磁盘满 / 权限变化),本地照常生效,不记指纹,下次启动自动重推 |
宿主侧写失败时还会在 DSH 宿主日志里留一条 dsh-settings-nav-order: 写入偏好文件失败(…)——浏览器半边对失败是静默重试的,宿主再不吭声就只剩「顺序总是复原不了」这种无从查起的现象。读到一个坏掉的文件时同样留痕(按「没有快照」处理,不会拿它覆盖本地)。
浏览器的启动对账也分两类失败:宿主不可达 / 返回 500 / body 不是 JSON 属于预期路径(宿主还没重启就是这个样子),一律不记,免得每次打开页面都刷控制台;只有在「响应已经拿到、后面却抛异常」时才在浏览器控制台留一条——那已经不是环境问题,而是代码缺陷,不该被无声吞掉。
宿主半边只读写这一个固定路径(state.json):原子写(先写 .tmp-<pid> 再 rename,避免半截 JSON 被同步进仓库)、形状守卫(只接受 {enabled, order, hidden},坏数据拒收)、请求体上限 64 KB、同源护栏(拒绝 sec-fetch-site: cross-site),且不接受调用方指定路径——否则那就是一个任意写接口。
原子写的只读目标回退是「先清只读位再改名」,不先删原文件——后者一旦第二次改名再失败(占用、瞬时 I/O 错),用户原来的偏好就真的没了。
兼容与边界
- 不改 DOM 顺序、不动第三方插件代码:容器是 flex column,给子元素打
style.order只改视觉顺序;隐藏只是display:none,节点不删,取消隐藏立刻回来。 - 拖拽自己实现:Pointer Events +
setPointerCapture+ 各行中线算落点(dropIndex+adjustDrop),不引第三方库,触摸屏同样能拖。箭头与拖拽最终都走同一个moveTo,两种操作的结果必然一致。 - 按
[class*="navList"]子串匹配:真实 DOM 里的类名是 CSS Modules 的哈希名(ZiQlkq_navList/ZiQlkq_navLabel),精确的.navList选择器匹配不到;按「类名里含原名」匹配,DSH 换哈希前缀也不受影响。 - 同名项用「同名第几个」区分:菜单项没有 id 也没有 data 属性,只能靠标签文本识别;配置里存
{name, index}而不是裸名字,菜单增删之后旧配置不会把别的项挤错位。 - MutationObserver 观察
document.body子树:设置面板由 portal 挂在document.body上,面板一出现就重排;回调按帧合并(requestAnimationFrame),聊天区流式输出不会把它变成性能负担。 - 防自锁:「设置导航顺序」自身永远不许被隐藏——它是唯一能取消隐藏的入口。
- 失败退回原样:面板读不到、样式打不上、配置坏掉(坏 JSON、字段类型不对、隐私模式下
localStorage抛异常),结果都只是顺序 / 隐藏没生效——不会白屏,也不会少项。 - 隐藏只收入口、不卸载插件:被隐藏的插件功能照常工作;要彻底停用它请去插件管理页。
- 界面语言会影响识别:菜单项靠显示出来的文字认,切到英文界面后,配置里记的中文名暂时不匹配,其余项不受影响;切回中文即恢复。
- 跟着浏览器 profile 走:换浏览器、清站点数据、隐私模式都读不到本地副本,这时按各插件自己的样子显示——宿主文件仍在,打开设置页会自动回填(除非本地还有没推上去的改动,那种情况以本地为准)。
- 新装 / 卸载插件:配置里已经不存在的菜单项当成陈旧项忽略;新装插件新增的菜单项排在已保存顺序的后面,默认也不会被隐藏。
开发与测试
node test/client.test.mjs # 浏览器半边:装载 → 计算顺序 → 落到元素上 → 设置页交互 → 宿主对账 → 保存回执
node test/host.test.mjs # 宿主半边:state.json 的读 / 写 / 拒收 / 中断 / 护栏 / 失败留痕
零依赖:用手写的假 DOM + 假 React 跑完整条链路,客户端 53 个用例;宿主半边用假的 ctx.webServer + 假 req / res,17 个用例(DSH_HOME 指向临时目录,不碰真实 home)。共 70 个用例全部通过。
跨机复原那段的对账规则(采用宿主 / 保留本地 / 推上去 / 失败重试)都有对应用例,宿主不可达与坏数据的每种姿势也都有;「保存后如实回报同步结果」「不许谎报已保存」「意外错误留痕而预期失败不刷屏」也各有用例。
拖拽的指针跟手效果假 DOM 测不了(假元素没有布局),测试覆盖的是它依赖的纯函数(moveTo / dropIndex / adjustDrop)以及与箭头的结果一致性;手感请以实机为准。
不要用
node --test test/:测试运行器会派生子进程并捕获管道输出,在受限沙箱里会以EPERM失败。直接运行测试文件即可。
与聚合包的关系
本包是
@fish-under-sea/dsh-fish聚合包的成员之一;单独安装只影响这一项。跨机复原依赖
dsh-git-sync(≥ 0.2.3):本插件的宿主半边把偏好落成$DSH_HOME/dsh-settings-nav-order/state.json,dsh-git-sync的白名单按这条相对路径收录它;同步时本插件只管写文件,搬运的事交给同步插件,职责互不缠绕。
许可
MIT © Fish-under-sea