- Accueil
- Plugins
- Améliorations UI
- dsh-plugin-layout-organizer
dsh-plugin-layout-organizer
var-lin/dsh-plugin-layout-organizer
Drag-and-drop reordering, official/third-party dividers and a stash panel for the buttons DSH renders in the sidebar, the sidebar footer, the conversation tabs, the composer dock and Settings. Only display position changes; the plugin registry is untouched.
Installer
dsh plugin --profile web add github:var-lin/dsh-plugin-layout-organizerREADME
插件布局整理
English: README.en.md
一个 DSH 客户端插件:帮你把插件按钮拖到想要的位置、在官方与第三方之间画一条分隔线、把不常用的收进「按钮收纳包」。它只改变按钮的显示位置——不卸载、不启停任何插件。
适配 DSH
>=0.1.7-rc.2:0.1.7-rc.2起的每个0.1.x、0.2.x构建(含全部 rc 与0.2.1正式版)都用同一份代码,不需要为版本切换装不同包。
它能做什么
DSH 把插件按钮投放到五处槽位,本插件对这五处一视同仁:
| 位置 | 槽位 | 说明 |
|---|---|---|
| 侧边栏顶部按钮 | sidebar.panellist | 侧边栏「插件 / 定时任务 / SSH / MCP 连接器」那一列 |
| 侧边栏底部按钮 | sidebar.footer.action | 侧边栏底部、设置入口上方的图标按钮 |
| 对话标签按钮 | conversation.view | 对话页顶部「对话 / 轨迹 / 上下文 / 编队」那一排 |
| 对话输入框停靠行按钮 | conversation.composer.dock | 输入框下方那一行 |
| 设置导航按钮 | settings.section | 设置弹窗左侧的导航行 |
1. 拖拽排序
- 按住任意可拖按钮拖动,松手即保存;拖动中按钮跟随指针,经过的行让开缺口标示落点,松手不会触发按钮本身。
- 第三方按钮始终可拖;官方按钮默认锁定,打开「允许拖动官方按钮」后即可拖动。
- 默认遵循分组主序:官方整体在前、第三方整体在后,组内顺序完全由拖拽决定。
- 「允许官方和第三方混合排序」默认开启(挂在「允许拖动官方按钮」下的子开关):开启后保存的顺序原样生效,第三方按钮可以落到两条官方按钮之间。
- 本插件自己的设置入口(设置导航里的「插件布局整理」那一行)同样可拖、可收纳。
2. 官方 / 第三方分隔线
- 默认关闭。打开「显示官方 / 第三方分隔线」总开关后,可在三个位置分别开关:侧边栏顶部、对话标签、设置导航;侧边栏底部与输入框停靠行永远不画——单列 / 单行里两类按钮本来就交错,插入一条线会被读成凭空多出来的一组。
- 线画在最后一条官方按钮与第一条第三方按钮之间的空隙正中,横排竖排自动适配方向。
- 弹窗盖住时自动隐藏;设置导航里的线画在弹窗内部(
z-index1010),其余在 body 级(z-index90),不会压过官方弹窗。 - 行被卸载、被祖先隐藏或裁剪,或混合排序下两类按钮真正交错时(一条直线描述不了多个交界),整条线自动收起。
- 纯视觉提示,不影响排序规则。
3. 按钮收纳包
- 在任意可收纳按钮上点右键,弹出与 DSH 官方菜单同样式的「上移 / 下移 / 收纳」菜单(同款半透明底、细描边、圆角与图标列);也可以在设置页行尾点「收纳」。
- 被收纳的按钮从界面消失,但插件仍在正常运行——收纳只是视觉隐藏,随时可恢复。
- 打开方式:快捷键(桌面端默认
Ctrl/Cmd + Shift + O;网页端 Windows / macOS 为Ctrl/Cmd + Shift + Alt + O,Linux 为Ctrl/Cmd + Shift + ,;均可在设置里重绑);屏幕右缘、顶栏正下方的悬浮启动器(可拖到任意位置,松手吸附到较近的左 / 右边缘并保留纵向位置);设置页「按钮收纳包」区域。 - 面板固定尺寸(宽
min(520px, 92vw)、高min(720px, 84vh)):标题、搜索、排序与「全部恢复」钉在顶部,只有列表区滚动,条目再多也不会把面板撑高。 - 顶部同样有搜索框:按名称、id 或所属位置实时过滤,输入框左侧放大镜、右侧 × 图标清空;无匹配时列表显示提示文案。
- 条目可直接点击:点「插件市场」自动打开设置并跳到该页;点侧边栏按钮切换面板;点底部图标或对话标签触发原按钮。界面上本来就点不动的行(例如插件塞进输入框停靠行的只读统计条)在这里也是灰的、点不动——它本来就没有对应页面,点击不会导航、也不会顺手把收纳包关掉。
- 三种排序:按页面 / 按分类 / 按名称,另有独立的「全部恢复」(两步确认);单行的「恢复」一点即恢复。
- 每行按「圆点 + 名称 + 所属位置 + 恢复」排布,名称过长时打点省略,恢复按钮不换行。
4. 设置页
设置 → 插件布局整理(本插件在插件管理器的详情面板里也有同一设置页):
- 顶部分页导航:药丸式分段控件,配色照抄 DSH 自家的
SegmentedControl——整条是浅灰圆角槽(--dsw-alias-interactive-bg-hover),选中项是浮起的药丸(--dsw-alias-bg-layer-1加--dsw-elevation-soft阴影、加粗近黑字),未选中项只有灰字、悬停才转近黑。两组——位置管理(收纳包入口与五张位置卡片)与通用设置(各项开关与快捷键),默认停在位置管理。点击只渲染当前那一组,滚动位置与搜索词在切换时保留;搜索期间收纳包入口卡片让位。 - 通用:「允许拖动官方按钮」总开关(子开关:混合排序)、「显示官方 / 第三方分隔线」总开关(子开关:三个位置)、悬浮启动器开关、「重置所有配置」(两步确认:首点武装,三秒内再点执行,超时复原;清空顺序、分类、收纳与全部开关)。总开关关闭时子行灰显但仍可点击——点灰子行等于「我要用这一路」,会点亮该路并连带顶起总开关。配置还是出厂值时这个按钮是灰的:按下去什么也不会变,留着能点的样子只会让人以为它坏了。
- 标题行:大标题右侧显示当前版本号(如
v1.2.1);再往右是 导出配置(整个持久化状态写成 JSON 文件)、导入配置(版本不符的整体拒绝,不会半套用)、GitHub 仓库、问题反馈(打开本仓库的 Issues 页面)。放得下时四个按钮与标题同行靠右;窗口太窄放不下时整组掉到第二行、从左边开始与标题左边缘齐,不会甩到右侧留一片空档。 - 快捷键:录制、恢复默认;冲突或不可用的组合有文字警告。
- 按钮收纳包:显示当前数量,一键打开。面板里按位置或按种类分组时,每个分组标题右侧有一个「恢复本组默认」(把这一组覆盖到的每一处位置的顺序与收纳一起清掉;按名称排序没有分组标题,也就没有这个按钮),分组里无事可做时按钮灰置。它是次级动作,字号比标题小、也不跟标题一样加粗。
- 搜索:位置管理页顶部有搜索框,按按钮名称或 id 实时过滤;卡片与行都不匹配时整张卡片让位,收纳包入口卡片在搜索期间也一并让位,清空(输入框右侧的 ×)即恢复全部。收纳包面板里也有一模一样的搜索行。
- 五张位置卡片:该位置的每个按钮,支持拖拽、上移 / 下移、官方 / 第三方改判、收纳与恢复;每张卡片标题右侧有自己的「恢复本组默认顺序」(两步确认),同样是次级动作、不加粗。分组行的折叠 / 展开状态跨刷新记住。卡片既没有保存顺序、也没有收纳行时,「恢复本组默认顺序」是灰的——无事可做时不该留着能点的样子。
- 宿主服务缺失时进入降级模式(例如没有快捷键服务时收纳包只剩悬浮启动器),原因显示在设置页顶部。
安装
本包是标准 DSH bundle,用内置插件管理器安装:
dsh plugin --profile <你的 profile> add dsh-plugin-layout-organizer
或让 agent 调用 plugin_manager(action=install_bundle,target 指向本目录)。
装好后刷新页面。
开发
需要 Node.js >=24(package.json 的 engines.node;构建依赖 Node 自带的 module.stripTypeScriptTypes,CI 与发版工作流也都用 Node 24)。
node build.mjs # 按 BUILD_ORDER 把 src/ 拼成 lib/client.js,并内联 assets/icons/ 图标
node --check lib/client.js
node test/smoke.mjs # 无头冒烟测试(伪造 DOM / React / ctx)
node test/navigate.mjs # 「设置」两步路由的行为测试
node test/accept.mjs # 源码形态验收探针
(等价于 npm run build / npm run check / npm test。)
持续集成
开 PR 或推送 master 会触发 .github/workflows/ci.yml,跑一遍与发版相同的闸门:构建 → 语法检查 → 校验已提交的 lib/client.js 与源码一致 → 三个测试,最后再加一次类型检查(npx -y --package typescript@6.0.3 tsc -p tsconfig.json,版本与 tools/ts-check.mjs 用的编辑器内置 TS 一致,按需下载,不写进 package.json、不产生锁文件);工作流只读,不发布也不建 Release。screenshots/ 由插件市场从仓库直接读取,改动要一并提交。
发版
推送 v* tag 即可,.github/workflows/publish.yml 会自动构建、校验构建产物与源码一致、跑测试、发布到 npm(Trusted Publishing / OIDC,无需 token),并用 tools/release-notes.mjs 从 CHANGELOG.md 抽出对应段落创建同名 GitHub Release:
git tag vX.Y.Z && git push origin vX.Y.Z
工作流是幂等的:npm 上已有该版本就跳过发布,Release 已存在就改写说明,可安全重跑。发版前记得三件事——package.json 升号、CHANGELOG.md 新增版本段与底部链接定义行、tag 名与版本号一致(CI 会校验,不一致直接失败)。
源码组织
客户端包只能 require 启动图里已注册的种子词或包,不能按相对路径在运行时分块加载,因此本项目采用模块化源码 + 构建期拼接:
src/按职责拆成types/、shared/、core/、drag/、ui/;build.mjs持有显式BUILD_ORDER清单并按序拼接;- 清单本身就是加载顺序声明——后置模块依赖前置模块的顶层
var/function,所以显式列出而不是目录遍历推断(types/globals.d.ts仅供类型检查,不参与拼接); - TypeScript 语法由 Node 自带的
module.stripTypeScriptTypes(transform模式)擦除——没有打包器,也没有要装的依赖; - 产物是单个
lib/client.js,包裹在window.__ModuleLoader__.load({ id, factory })里; assets/icons/存放从 iconfont 下载的 13 个图标 SVG,是构建输入:源码不手写 path,只写@@svg:file.svg@@占位符(shared/icons.ts六个菜单图标 + 四个标题行动作图标 + 两个搜索行图标;core/dom-project.ts设置行自身图标为@@svg:layout.svg@16@@)。build.mjs归一化每个素材——viewBox取自源文件、宽高默认 14(占位符可指定,标题行四个动作图标为 16、搜索行两个为 13 与 12)、写死的颜色改写为currentColor、补aria-hidden/focusable——再把<svg>内联进lib/client.js。动作素材的画布留白差别较大,styles.ts按素材名(data-ico)做光学校正,让一排按钮的图标看起来等高。layout.svg同时是 package.json"icon"指向的包图标;该目录在files白名单里,装包后仍可重新构建。
| 模块 | 职责 |
|---|---|
types/state.ts | DSH 服务(槽位 / locale / 布局 / 快捷键)与本插件状态的 TypeScript 形状,构建期擦除 |
types/globals.d.ts | 构建前言注入的全局声明(React、h、各 hook),仅供类型检查,不参与拼接 |
shared/constants.ts | 插件 id、存储键、五处槽位表、官方 id 白名单、DOM 标记名、z-index 常量等 |
shared/icons.ts | 六个右键菜单图标 + 四个标题行动作图标 + 两个搜索行图标的 @@svg:…@@ 构建占位符,构建时从 assets/icons/ 归一化内联 |
shared/locale.ts | 中英文案字典 |
core/store.ts | 状态形状(v5)、持久化、纯查询与命令(顺序、分组、收纳) |
core/dom-rows.ts | 各槽位的行发现、账本读取、行打标与配对、几何计算 |
core/dom-line.ts | body 级官方 / 第三方分隔线的创建、定位与隐藏 |
core/dom-menu.ts | 仿 DSH 官方样式的右键菜单及其窗口级事件委托 |
core/dom-project.ts | 投影:写标记与 style.order、收纳隐藏、分隔线调度、设置行自身图标 |
core/dom-navigate.ts | 重新打开被收纳的按钮,含「设置」两步路由 |
core/organizer.ts | 调度中枢:rAF 合帧、MutationObserver、槽位订阅 |
drag/drag-rows.ts | 界面行(DSH 各槽位按钮)的指针拖拽控制器 |
drag/drag-list.ts | 本插件设置页列表的指针拖拽控制器 |
ui/styles.ts | 样式注入与全部 CSS 规则 |
ui/kit.ts | React 基础控件(Icon / Switch / Field / Card / Chip / Btn / RowItem / ConfirmBtn) |
ui/search.ts | 搜索框与匹配判定(设置页与收纳包共用) |
ui/config-actions.ts | 配置导出 / 导入(版本校验)与仓库、Issues 地址 |
ui/scope-card.ts | 单张位置卡片:行列表、上下移、改判、收纳与恢复本组 |
ui/shortcut-card.ts | 快捷键编辑器(录制、冲突提示、恢复默认) |
ui/settings-page.ts | 设置页外壳:标题行、顶部分页导航、两组内容装配 |
ui/stash.ts | 按钮收纳包面板(固定尺寸 + 搜索 + 列表滚动)与悬浮启动器 |
main.ts | 接线层:样式、locale、存储、槽位注册、快捷键、清理 |
已知边界
- 排序与隐藏发生在 DOM 层,插件注册表不会被修改。 DSH 在
register()时一次性按priority → order排序槽位,事后改order不会重排;本插件直接给真实 DOM 行写style.order与标记属性,因此依赖官方 UI 结构,DSH 大版本升级后可能需要适配。 - 没有权威的「官方 / 第三方」字段。 槽位条目记录的
registrant不可靠(多个第三方包冒用官方标记),本插件内置官方 id 白名单,并允许在设置页手动改判任意行。 - 「收纳」不是卸载。 被收纳的按钮只是隐藏,对应插件照常运行。
- 输入框停靠行按「屏幕上真实存在的行」列条目。 台账里存在、但组件当轮渲染成空的记录(供应商未登录时的统计读数)不会出现在设置页与收纳包里,也不会被按位置错认成别的行;它们按正证据认领——官方药丸认
data-composer-stats(旧宿主)或data-composer-stat="activity"/"usage"(0.2.1起把一条拆成了两条),token-saver 认.st-strip。官方读数(旧宿主的stats、新宿主的activity与usage)与 7% 用量百分比(context-meter)各自收纳、互不牵连,用量百分比分类归官方。
许可证
MIT
Plugins associés
dsh-web (dsh-task-board)
zhu1090093659/dsh-web
dsh-web (dsh-web-all)
zhu1090093659/dsh-web
dsh-web-ui (dsh-task-board)
zhu1090093659/dsh-web-ui
dsh-web-ui (dsh-web-ui-all)
zhu1090093659/dsh-web-ui