1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
+ ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
8.1 KiB
8.1 KiB
31 · 插件管理页双 Tab + 说明为主视觉 + 目录字段用全
- 日期:2026-09-11
- 触发:用户提问「导入到候选池是什么意思」+「dsh 官方推荐插件 和 手动添加和管理插件 能否分两个 tab 展示」+「官方插件优先展示应该是插件说明或中文名称,需要一眼知道这个插件是干什么的」
- 状态:✅ 已实施并视觉验证(commit
103471e)
一、需求与背景
三个问题指向同一件事:档案 29 交付的插件管理页,信息层级与术语都没站在「看得懂」的角度设计。
- 术语费解:「导入到候选池」中的「候选池」是内部叫法,admin 本人看不懂 —— 说明页面缺少对「投放 ≠ 生效」的解释。
- IA 混在一起:官方目录挑选、手工上传、已投放管理三件事挤在一页,纵向下堆。
- 信息层级错:表格首列是插件名(
dsh-market/dsh-market这类技术标识),说明被挤到最后一列且单行截断 —— 看不出插件是干什么的。
二、调研:官方目录还有哪些字段没用
拉 plugins.json 逐字段核对后发现,档案 29 只用了 7 个字段,漏了 3 个:
| 字段 | 覆盖 | 是否用上 | 说明 |
|---|---|---|---|
description.zh |
3408/3408(100%) | 部分(末列截断) | 中文说明,平均 83 字 |
category + 顶层 categories |
100%(23 类) | ❌ 只显示原始 id(ui) |
顶层 categories = { id: { en, zh } },官方只在这里给分类中文名(如 ui→UI 增强、tools→工具与能力) |
page |
3408/3408 | ❌ 未用 | 官方详情页 awesome-dsh-plugin.com/p/<owner>/<name>/(完整介绍 + 截图 + 评论) |
screenshots |
594/3408(17.4%);可导入子集 421/1792(23.5%) | ❌ 未用 | 截图 URL 数组 |
added |
3408 | ❌ 未用 | 收录日期(最早 2026-08-13 / 最新 2026-09-08)—— 跨度过窄,做「新收录」标记无意义,故未用 |
stars / downloads |
1586 / 1653(可导入子集) | ✅ | — |
tags / keywords |
0(不存在) | — | 上游没有标签体系,分类是唯一 taxonomy(23 类) |
三、实现(commit 103471e)
A. 双 Tab(web/portal.html → #/plugins)
- 样式按
06-工作台UI规范.md§4.4 下划线式(无背景无圆角,border-bottom: 2px,active 蓝字 + 蓝下划线,margin-bottom: -1px压住分隔线)。 - 类名用
.pg-tabs/.pg-tab,刻意避开全局.tab—— 06 明确警告.tab被顶栏(胶囊式)与数据页(下划线式)二次定义,复用会拿到不可预期的样式。 - Tab:「官方推荐插件」 / 「手动添加 / 管理」(后者带已投放计数徽章)。
- 深链:切 tab 用
history.replaceState写#/plugins/<tab>(不触发hashchange,因此不会重跑 router);初始 tab 读 hash 后缀。现有parseHash()取parts[0],#/plugins/manual依旧路由到 plugins,无需改路由。
B. 说明为主视觉(用户核心诉求)
白名单表格首列由「插件名」改成 「插件说明」:
| 行 | 内容 | 样式 |
|---|---|---|
| 主行 | 中文说明(2 行截断,-webkit-line-clamp: 2,title 悬停看全文;为空时回退显示插件名并加粗) |
14px / 正文色 |
| 副行 | 插件名 · npm包名 @ 版本 · ★N |
12px / dim |
分类列改显示中文分类名;新增 「详情 ↗」 外链列指向官方 page;保留 下载量(热度是排序主信号),★ 下移到副行。
C. 容器宽度
6 列信息量下表格横向溢出 74px。修法:给插件页单独放宽内容容器 —— router() 给 #view 打 page-<name> 类名,#view.page-plugins { max-width: 1200px },其他页仍 960 不受影响。实测 scrollWidth/clientWidth = 1105/1105(无溢出)。
D. 术语落到页面
「已投放插件」卡片加说明:
投放 ≠ 生效。 这里只是把插件收录到平台,对任何用户都不生效;用户要真正用上,需在自己实例的「设置 → 功能插件」里勾选启用 —— 启用时平台才会把插件装进他的环境,重启实例后生效。
按钮文案 导入到候选池 → 导入到平台,确认弹窗同步改口径。
E. 后端(src/web/routes/whitelist.ts)
- 解析顶层
categories→categoryLabels(id→中文名),随缓存留存;条目新增categoryLabel/hasShots。 GET /api/plugins/whitelist的categories由string[]改为[{id, label, count}](按条目数降序),供前端下拉直接用中文 + 计数。CACHE_VERSION2 → 3(新增字段必须 bump,否则 TTL 内旧格式缓存被复用 —— 档案 29 踩过这个坑)。
四、改动文件
| 文件 | 改动 |
|---|---|
src/web/routes/whitelist.ts |
解析分类中文名映射;条目加 categoryLabel/hasShots;categories 改对象数组;CACHE_VERSION → 3 |
web/portal.html |
双 Tab + .pg-tab 样式;白名单表格列重排;page-plugins 容器放宽;router 打页面类名;「投放 ≠ 生效」说明与文案 |
五、验证(独立 headless Chrome,2026-09-11)
方法:起独立 headless Chrome(--headless=new --remote-debugging-port=9223 --user-data-dir=<独立 profile>),CDP 直连(BU_CDP_URL),注入临时 admin session cookie(poc-ui,用后即删)。不碰用户日常浏览器、无授权弹窗。
| 用例 | 结果 |
|---|---|
后端 GET whitelist?onlyImportable=1&category=ui |
200;categories[0..5] = UI 增强(565) / 工具与能力(457) / 开发与运行时(262) / 会话与消息(215) / 工作流与自动化(207) / 用量与计费(187);条目带 categoryLabel/page/hasShots |
| Tab 渲染与切换 | tabs=2;点「手动添加 / 管理」→ URL #/plugins/manual、pane 互斥切换正确 |
| 深链 | 直接访问 #/plugins/official 落在正确 tab |
| 表格布局(1440×1000) | scrollWidth/clientWidth = 1105/1105 → 无横向溢出;6 列表头齐全 |
| 首行内容 | 说明为主 + 副行 dsh-market · dshmarket @ 1.45.0 · ★ 3408 + 分类「插件市场与管理」+「npm 预构建」+ 383506 + 「详情 ↗」 |
| 静态文件即时生效 | 改 portal.html 无需重启(服务启动时间未变);后端改动才 build + restart |
| 内联 JS 语法 | node --check 通过 |
截图:p-official.png / p-manual.png(本机临时目录,未入库)。
六、事故/踩坑记录
browser-harness反复弹 Chrome 授权(用户明确抱怨):根因是我每次都用python -m browser_harness.run直接起进程,绕过了 wrapper 脚本 —— wrapper 会设BH_RUNTIME_DIR_SHARED=1让 daemon 常驻复用;绕过它则每次新建连接 → Chrome 每次弹窗。修法:起独立 Chrome 实例 + 固定调试端口 +BU_CDP_URL直连,此后零弹窗。Emulation.setDeviceMetricsOverride按 target 生效:先new_tab再设覆盖才对;设在旧标签上、再开新标签 = 覆盖丢失(截图只有 758×482 宽)。- hash 残留影响验证:页面停在上次的
#/plugins/manual,重载后#wlTbody不存在 → 元素查询返回 null(表现为0/0)。验证脚本必须显式导航到目标 tab。 - 移动
.bak前先查是否被 git 跟踪(档案 29 教训复用)。 - 后端字段增删必须 bump
CACHE_VERSION(档案 29 教训复用)—— 本次主动执行,未再踩。
七、后续(未做,按需)
- 截图预览:
screenshots覆盖仅 17.4%(可导入子集 23.5%),现已置hasShots但未展示;若要「图看效果」,可在「详情」列改为悬停出缩略图(覆盖率不高,需评估是否值得)。 - 描述完整展示:现在 2 行截断(约 60 字 / 平均 83 字)+ 悬停看全文。若要求「不悬停也全看到」,可加行高(3 行 clamp)或再放宽容器。
- 「候选池」术语:已把按钮/说明改成「平台投放」口径,但内部代码与 API 仍叫 business_plugins / 候选池;是否统一改名待定。