主人报障:「为什么中文搜索接口……下拉菜单里全给的英文名?你甚至不愿意带个括号 把中文名写上去」。根因不是漏写文案,而是整条链路没有「显示名」的位置:字段 声明只有 optionsFrom,候选动作回的就是 KnownNames() 的纯 id 数组。 - 后端:候选改为对象数组 {value,label};label 表与接入位表同源(键用 NameSearXNG 等常量),未知 id 原样回。value 逐字等于 KnownNames()。 - 前端:下拉/多选「显示 label、写 value」;表格单元格按 value→label 索引显示, 查不到回落显示原值。多选从裸输入框改成「候选开关 + 落盘值输入框」。 解码器本就兼容裸字符串/{value,label}/{id} 三形状 ⇒ 其他插件零影响。 中文名逐字:SearXNG 本地兜底(searxng)/ 智谱 BigModel(zhipu)/ 阿里云 IQS 信息查询(aliyun_iqs)/ 博查 Bocha(bocha)/ 火山引擎·豆包搜索(volcengine)/ 百度千帆 AI 搜索(baidu_qianfan)。 验收(主会话复跑 + 亲自变异):go build ./... + vet 绿;search 包测试绿; tsc --noEmit 0 错;新渲染测试 26 checks + 既有 plugin panel 55 / data 90 / schema values 40 / channel config 70 全绿。我用 sed 把 values.ts 的 option.value 全换成 option.label ⇒ 渲染测试红(「点选把候选项的 id 追加到 降级链末尾」got="bocha, 智谱 BigModel(zhipu)")—— 这正是会把配置文件写坏的 错法;还原后 md5 一致且绿。 ⚠️ 契约面变更(我在 Docs/api-client.md 登记):plugin.providers 候选响应形状 字符串数组 → 对象数组;label 仅供显示、value 才是落盘值。 ⚠️ 已知边界:viewer 角色看不到 form 区块 ⇒ 表格对 viewer 仍显示裸 id(未做)。 ⚠️ 真机浏览器点击未跑(项目无 jsdom)⇒ 建议刷新管理页点一次确认观感。
- sections.tsx 的分组升级为可折叠(CollapsibleGroup);新增 plugins 分组由 GET /api/v1/admin/panels 动态填充(navModel.ts 纯函数可单测) - 插件页走动态段 /admin/plugin/<pluginId>/<panelId>,不进 router.ts 的静态白名单 - 把既有三段式(字段声明表 + 纯函数规则 + 渲染器,含「留空=不改」掩码语义)泛化成 schema → 控件(src/admin/schema/),复用它渲染插件表单/表格/事实面/日志 - 可测判据:新增插件页不得改动 sections.tsx / router.ts / AdminApp.tsx 的静态清单 - when 不满足 ⇒ 入口不出现;viewer 只看到只读区块 我的复跑:npx tsc --noEmit 通过;npx vite build 通过(3.67s);50 个前端脚本式测试全部 打印 checks passed(含本片新增 pluginData 90 / PluginPanel.render 55 / navModel 62 / schema values 40)。