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 一律写「远程服务器」。
12 KiB
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 附录与日志):
- 主次颠倒 —— 实例内包名做主视觉,门户反过来;
description取自插件package.json(英文);- 官方目录本来就有中文,但导入时没取用 —— 已在同批次修掉(见 §三.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。
三、同批次的其他修复
- 中文说明(方案第一步):
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():
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 正姿,路线不同所以没暴露)。
修法(待确认):
- 短期解阻塞:把该 profile 下 561 个 root 属主项 chown 给用户(
find <profile> -user root -exec chown <uid>:<uid> {} +)。R7:>10 文件,须先确认。 - 长期修根因:
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 处,全部是渲染层,不动数据与交互逻辑):
- 新增内层网格容器:
display:grid; gridTemplateColumns:repeat(2, minmax(0,1fr)); gap:10px—— 单独包一层,不直接改外层wrap(它同时装标题 / 搜索 / 按钮 / 提示等纵向块);插件项先收集进cards[]再统一 push。 - 每项由「行」改「卡片」:
padding 12px 14px、borderRadius 12px、background: var(--dsw-alias-bg-layer-1, #fff)、常显 1px--dsw-alias-border-1边框(原来非错误项是transparent)。 - hover 反馈改按规范 §4.5:
.bp-row:hover{ border-color: brand-primary; box-shadow: 0 4px 12px rgba(47,111,237,.15) }(原为 hover 换背景色)。 - 卡片内
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,md53ccb05bc6b138231bcaf1f81d7b50549);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→ 勾选 univer390→ 点「应用更改」弹窗打开且不发请求 → 点「确认应用」才POST /api/plugins/mine/apply。 - 未做:浏览器视觉验收(按 §6.5,此类 section 渲染只能由用户确认)。