Files
dsh_ai1net_server/dsh-server-docs/04-调整方案/67-功能管理section按UI规范重做.md
T
admin 5ad755116e chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进
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 一律写「远程服务器」。
2026-09-15 18:47:13 +08:00

140 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 67 · 「功能管理」section 按 UI 规范重做(v0.2.4)
- 日期:2026-09-12
- 触发:用户指出实例内「设置 → 功能管理」页「UI 交互太简陋、没参考 UI 规范、插件没重点显示中文说明」;核对成立后按方案执行
- 结论一句话:**把信息层次反过来(用途说明做主视觉)、字号与组件对齐 `06-工作台UI规范.md`、空态补层次、加行 hover** —— 并顺带发现一个会阻塞用户启用插件的平台 bug。
- 状态:✅ 已完成并部署(admin 已生效);⚠️ **guest 侧升级被一个平台 bug 阻塞**(见 §五)
> **TL;DR**|**结论**:规范第 5 行明确「含其 client 面 section」,本页在约束内,逐项核对确认「简陋」成立;主视觉原本是包名(说明只作 11px 灰字),与门户 plugins 页相反 —— 已反转。
> **关键**:执行中发现 `node_modules` 有 **561 个 root 属主文件**,根因是**门户候选池的启用路径以 root 跑 pnpm**,导致该用户此后任何 `pnpm add` 都 EACCES。
> **状态**:✅ UI 已上线(admin 验证);⏸ root 属主修复待确认(R7:561 > 10)
---
## 一、核对结论(只读,详见 2026-09-12 日志)
`06-工作台UI规范.md` 第 5 行适用范围明确含「**其 client 面 section**」→ 本页无豁免。逐项对照 `poc/business-plugins/lib/client.js`:
| 规范 | 改前 |
|---|---|
| 正文 15-16px / 次要 13-14px | 容器 13px;副行与徽章 **11px**(低于阶梯下限) |
| 空态:图标 + 标题 17px/600 + 说明 14px | **一行灰字** |
| `.btn` 15px / `.btn-sm` 14px | 12px / `5px 10px` |
| `.badge` `2px 8px` r10 13px | `1px 7px` r9 11px |
| 列表行 hover 反馈 | 无 |
| 第 7 行**强制**加载 `impeccable` + `taste-skill` | 二者在本机**不存在**(用户级技能目录只有 BT-* / dsh-*)→ 以规范文档为准(规范自称实测基线、优先级高于技能) |
**中文说明缺失的三层根因**(另见档案 66 附录与日志):
1. 主次颠倒 —— 实例内包名做主视觉,门户反过来;
2. `description` 取自插件 `package.json`(英文);
3. **官方目录本来就有中文,但导入时没取用** —— 已在同批次修掉(见 §三.1)。
## 二、改动(v0.2.4)
`poc/business-plugins/lib/client.js` + `package.json`(版本 0.2.3 → 0.2.4,同名同版本改内容必须升版本号,否则 pnpm 缓存复用旧包):
| 项 | 改前 | 改后 |
|---|---|---|
| **主视觉** | 包名 + 版本 13px | **用途说明 14px**(无说明时回退包名) |
| 副行 | 说明 11px 灰字 | **包名 + 版本 12px**(有说明才渲染) |
| 容器基准 | 13px | 14px(section 语境取 14/13/12 三档,不照搬页面级 16px) |
| 徽章 | 11px / `1px 7px` / r9 | 12px / `2px 8px` / r10 |
| 按钮 | 12px / `5px 10px` | 次要 13px / `5px 12px`;主按钮 14px / `7px 16px` |
| 空态 / 加载态 | 一行灰字 | 「标题 15px/500 + 说明 13px」层次(规范 §4.9,padding 按 section 收窄) |
| 列表行 hover | 无 | 注入式 CSS `.bp-row:hover`(规范 §4.3 / §5;内联样式表达不了 `:hover`) |
| 按钮 hover | 无 | `.bp-btn:hover` 边框/文字转主色 |
| 无匹配态 | 复用空态文案 | 独立文案(居中,13px) |
| 布局 | gap 8px / padding `6px 8px` | gap 10px / padding `8px 10px`(规范 §2.4) |
| 保留 | —— | `--dsw-*` design token(跟随 dsh 主题,**不**回退规范硬编码色)、i18n、探活刷新、rejected 行内提示 |
新增 locale key:`loadingDesc`、`emptyTitle`、`noMatch`(zh/en 同步,key 集一致)。搜索占位符改为「搜索插件名或用途」。
**交付**:`npm pack` → `business-plugins-0.2.4.tgz`(8951 B)→ `/opt/dsh/artifacts/` → `node scripts/ensure-biz-plugins.cjs --all --restart`。
## 三、同批次的其他修复
1. **中文说明(方案第一步)**:`src/web/routes/whitelist.ts` 导入入库改用目录条目中文(`entry.description`,建索引时已按 `zh ?? en` 归一),并回填已入库的 3 条 → 门户「已投放插件」表与实例内列表均显示中文。已部署验证。
## 四、部署结果
| 对象 | 结果 |
|---|---|
| **admin**(uid 114801) | ✅ 0.2.3 → 0.2.4 升级成功;bundles=6 含该包;当时无运行实例,下次访问即为新 UI |
| **guest**(uid 100002) | ❌ `EACCES: permission denied, open '.../node_modules/.bin/modlens'` |
## 五、⚠️ 执行中发现的平台 bug:候选池启用路径污染属主
**现象**:guest 无法升级/启用任何插件 —— 以用户身份跑 `pnpm add` 时撞 `EACCES`。
**实测污染规模**:
| 用户 | profile 下 root 属主项 | 分布 |
|---|---|---|
| guest | **561** | `.pnpm` 555 / `@liustack` 2 / `.bin` 2 / `dsh-univer-office` 1 / 一个 `.bak` |
| admin | 5 | 全在 `.pnpm` |
**根因**:门户候选池的启停路径 `src/web/routes/business-plugins.ts` 的 `install()` / `uninstall()`:
```ts
execFileSync('pnpm', ['add', 'file:' + plugin.tgzPath, ...], { cwd: dir, env: pnpmEnv(user.id) })
```
**没有 `setpriv` 降权** → 以平台进程身份(root)执行 pnpm → 装出来的文件属主是 root。此后用户自己的 `pnpm add/remove` 必然 EACCES。
**证据吻合**:`audit_log` 记录 guest 在 01:58 通过候选池启用 `@liustack/modlens` + `dsh-univer-office` —— 正是这批 `.pnpm`/`@liustack`/`dsh-univer-office` 项的来源。
**与既有档案的关系**:档案 43 已处理过「平台 root 污染用户工作区」并加了属主自愈,但**候选池这条路径未被覆盖**(`ensure-biz-plugins.cjs` 等平台脚本用的是 `setpriv` 正姿,路线不同所以没暴露)。
**修法(待确认)**:
1. **短期解阻塞**:把该 profile 下 561 个 root 属主项 chown 给用户(`find <profile> -user root -exec chown <uid>:<uid> {} +`)。**R7:>10 文件,须先确认**。
2. **长期修根因**:`install()` / `uninstall()` 改为 `setpriv --reuid <uid> --regid <uid> --clear-groups env HOME=<ws> pnpm …`,与平台脚本姿势对齐;并考虑给 `apply` 路径补一次属主自愈。
---
## 追加(2026-09-12 22:2x):v0.2.6 —— 插件列表改为**卡片形式,一行 2 个**
- **触发**:用户「把设置中功能管理的插件列表改为卡片形式 一行放2个卡片 好看些」(2026-09-12 22:13)。
- **落点**:`poc/business-plugins/lib/client.js`(client bundle)。
- **改动**(4 处,全部是渲染层,不动数据与交互逻辑):
1. **新增内层网格容器**:`display:grid; gridTemplateColumns:repeat(2, minmax(0,1fr)); gap:10px` —— **单独包一层**,**不直接改外层 `wrap`**(它同时装标题 / 搜索 / 按钮 / 提示等纵向块);插件项先收集进 `cards[]` 再统一 push。
2. **每项由「行」改「卡片」**:`padding 12px 14px`、`borderRadius 12px`、`background: var(--dsw-alias-bg-layer-1, #fff)`、**常显 1px `--dsw-alias-border-1` 边框**(原来非错误项是 `transparent`)。
3. **hover 反馈改按规范 §4.5**:`.bp-row:hover{ border-color: brand-primary; box-shadow: 0 4px 12px rgba(47,111,237,.15) }`(原为 hover 换背景色)。
4. **卡片内 `alignItems: flex-start`** —— 复选框与徽章顶部对齐,长说明不再把行高撑歪。
- **规范依据**:`06-工作台UI规范` §4.5(卡片 r12 + hover 上浮 / 主色扩散影)、§2.4(信息卡 `14px 16px`)、§2.3(大卡片 r12);颜色走 dsh 官方 `--dsw-*` token(本 section 嵌在 dsh 面板内,不照搬门户亮色基线)。
- **部署**:版本 **0.2.5 → 0.2.6**;产物 `business-plugins-0.2.6.tgz` → `/opt/dsh/artifacts/` → `node scripts/ensure-biz-plugins.cjs --all`(**未加 `--restart`**,不主动打断在线实例)→ admin(`cce6d1cd…`)与 guest(`4092b965…`)均已确认装到 **0.2.6**、且包内含改动(`gridTemplateColumns` 命中 1)。
- **生效条件**:client bundle 在**实例启动时**加载 ⇒ **用户需重启自己的实例**(实例页重启,或等下次冷启动)才看得到。
- **未做**:**未做浏览器渲染验证**(本机无 Chromium;按规范 §6.5,此类 section 级渲染只能由用户硬刷新确认)⇒ **待用户目视验收**。
---
## v0.2.7 / v0.2.8 追加(2026-09-13,用户要求)
> 按「档案只增不改」追加。记录规范之外的两块新增交互:**内存预估** 与 **页内弹窗**。
### 触发(用户原话)
- 「先不改挡位,先优化设置中 功能插件卡片的样式,把预估所占内存数值也写上去(卡片可以增加高度便于UI布局),在卡片列表上方 增加一个内存占用状态条(显示当前内存占用量,用户点击启用卡片时增加预估占用量,显示是否超出最大内存占用,让用户知道插件启用和内存占用预估状态)」
- 「启用插件内存预估值超过上限 点击确认按钮弹窗需要有警告(还有所有弹窗改为页面弹窗 参考mcn工作台弹窗实现)」
### v0.2.7 · 内存预估(卡片 + 状态条)
- 内存模型常量:`MEM_LIMIT_MIB=384`(档案 58)/ `MEM_BASE_MIB=285`(空载基线,smaps 实测)/ `MEM_TABLE={dsh-univer-office:64, dsh-plugin-mcn-suite:39}`(隔离 cgroup 实测 rss 增量)/ `MEM_FALLBACK_MIB=2`(轻量兜底)/ `MEM_TIGHT_RATIO=0.85`。
- 卡片:`padding 14/16` + `minHeight 112`;信息列第三行加「预估内存 ≈ N MiB」徽章,**≥60 红 / ≥30 黄 / 其余次要色**。
- 列表上方「内存预估」状态条:`当前 X MiB → 勾选后 Y MiB / 上限 384` + 三态词 + **双色进度条**(实心 = 服务端当前已启用那批 `savedIds`;浅色 = 本次勾选增量)+ 口径小字「预估(实测基准,非实时读数)」;超限时整条红边 + 警告行。
- 实现要点:`load()` 时快照服务端已启用集合 `savedIds` —— 否则 `p.enabled` 被 toggle 就地改掉后无法区分「当前 / 勾选后」。
### v0.2.8 · 全部弹窗改页内弹窗
- **弃用 `window.confirm`**;`applyChanges()` 拆为 `askApply()`(开弹窗)+ `doApply()`(真提交)。
- 弹窗**照 MCN 工作台范式**(`dsh-plugin-mcn/lib/client.js`):`fixed inset:0` 遮罩(`rgba(0,0,0,.35)` / `zIndex 2000` / 居中)+ 面板(`radius 12` / `maxHeight 82vh` / `padding 18·22` / 阴影)+ **点遮罩关闭** + 面板 `stopPropagation` + ✕;三段结构:标题 / 正文(明说重启后果)/ 按钮行。
- **超限时**:弹窗内内存块转**红底红边** + 红色标题「⚠ 本次勾选将超出单实例内存上限」+ 说明;**确认键转危险色**;**未点确认不发请求**。
- 全文已无原生弹窗调用(`window.confirm/alert/prompt` 仅存于注释)。
### 口径(重要)
- 状态条是**预估值**(客户端拿不到 cgroup 实测值)。做**实时读数**需平台加只读路由(读 `memory.usage_in_bytes` / `MemoryMax`)→ 平台代码改动 + build + 重启服务,**尚未做**。
### 部署与验证
- 产物:`business-plugins-0.2.8.tgz`(14,358 B,md5 `3ccb05bc6b138231bcaf1f81d7b50549`);**0.2.7 未单独部署**(0.2.8 含其全部改动)。
- 部署:`scp` → `/opt/dsh/artifacts/` → `node scripts/ensure-biz-plugins.cjs --all`(admin/guest 0.2.6 → 0.2.8)→ `--restart`(优雅停 scope,下次访问自动拉起)。
- **线上端到端自证**:临时会话 `enter` → 实例页 200 → 抠出 combo bundle URL(含 `@dsh-local/business-plugins/client.js`,`rev=27340d41a454`)→ 拉取 **4,585,899 B** → 命中 `mem.title`×4 / `__confirm`×1 / `confirm.overTitle`×3 / `MEM_LIMIT_MIB`×5 /「预估内存」×5。
- 仿真验证(临时 harness,用完即删):初始 `326/326` → 勾选 univer `390` → 点「应用更改」弹窗打开且**不发请求** → 点「确认应用」才 `POST /api/plugins/mine/apply`。
- **未做**:浏览器视觉验收(按 §6.5,此类 section 渲染只能由用户确认)。