Files
hermes-ui/docs/known-issues.md
T
toor 77b1858163 Remove the API relay link and the ESP32 device feature
Two pieces of upstream promotion came out of this deployment.

The sidebar "apiRelay" tab only opened a registration page on a third-party
site, so it goes with no replacement.

The circuit badge next to Settings was not what it looked like. It opened an
ESP32 device manager: add a device, show LAN status, remote connect/disconnect,
rename, delete, backed by six REST routes and an mcu_devices table. The purchase
button inside that dialog was the promotional part; the rest was real device
management. Since the hardware is out of scope here, the entire feature is
removed rather than just the link that pointed upstream - UI entry, client API,
routes, controller, store, schema, locale strings, and the one GlobalAgentServer
method that only existed to answer its status column.

Table creation is dropped but no DROP TABLE was added, so an already-provisioned
database keeps its rows as harmless orphans instead of losing data on upgrade.

DevicesView and the hermes.devices route are untouched: that is LAN device
discovery, a separate feature.

AGENTS.md now records the removal so the badge does not come back.
2026-10-19 01:32:51 +08:00

189 lines
11 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.
# 已知问题与迁移遗留
记录 UI 库迁移(Phase 006-007,naive-ui → Arco Design Vue)过程中**发现但按规则未修**的问题,以及自动化测试**测不到、只能靠手工验证**的风险点。
用法:日常使用时遇到异常表现,先来本文档搜关键词。确认命中后按「证据」栏定位,确认是同一个问题再修。
新增条目请追加到对应分区末尾,不要重排已有条目。
---
## 一、已确认的业务逻辑缺陷
这些是读代码时确认存在的真实缺陷,与迁移无关(迁移前就存在),但迁移过程中被注意到。修它们属于逻辑改动,需单独开分支。
### 1. ModelSelector 的 computed 里有副作用
- **现象**:切换模型时 provider 选择偶尔「自己跳」,或 `customProvider` 状态被莫名改写
- **位置**:`packages/client/src/components/layout/ModelSelector.vue`,`providerOptions` computed 的 getter
- **证据**:computed 的 getter 内部给 `customProvider.value` 赋值。computed 应当是纯读。副作用是:任何其它 computed 或渲染逻辑读到 `providerOptions`,都会顺带改写 `customProvider`
- **修法**:把赋值移到 `watch` 或显式的 setter 里
### 2. ProfileSelector 把 i18n key 当文案用
- **现象**:某些运行时状态提示里显示原始 key(如 `profiles.runtime.checking`)而不是中文/英文文案
- **位置**:`packages/client/src/components/layout/ProfileSelector.vue`,`gatewayStatusText` / `bridgeStatusText`
- **证据**:`message.warning(t('profiles.runtime.checking'))` —— `t()` 的结果(一个 key 字符串)被直接当消息正文,缺了外层再套一次 `t()`
- **修法**:改成 `message.warning(t('profiles.runtime.checking'))`(让 `t()` 返回字符串函数的结果),即检查是否需要 `t(t(key))` 或改用 `te()` + 手动查表
### 3. McpManagerView 的 `_autoRetryCount` 赋值可能静默失效
- **现象**:MCP 失败后点「重试」,自动重试计数似乎没有正确归零
- **位置**:`packages/client/src/views/hermes/McpManagerView.vue`,`let _autoRetryCount`(模块级)
- **证据**:在 header 按钮的内联表达式 `@click="_autoRetryCount = 0; loadServers()"` 里被重置。`<script setup>` 中的 `let` 绑定在模板内联语句里**不可写**(编译器只对 ref 做解包),这个重置很可能不生效
- **修法**:改成 `ref`,或把重置逻辑移进一个具名函数
### 4. McpManagerView 的 handleModeChange 有冗余重试
- **现象**:无功能影响,仅代码冗余
- **位置**:`packages/client/src/views/hermes/McpManagerView.vue`,`handleModeChange`
- **证据**:两段重复的 try/catch 用同一个 `mode` 解析同一份文本,第一次失败后用相同模式重试一次才 return。第一个 catch 分支应直接 return
### 5. WorkflowView 的批量删除确认 ref 已无读取方
- **现象**:无功能影响
- **位置**:`packages/client/src/views/hermes/WorkflowView.vue`,`showWorkflowBatchDeleteConfirm`
- **证据**:naive 的 `NPopconfirm v-model:show` 需要它;Arco 的 `a-popconfirm` 无 `show` prop 后它失去读取方,只剩写。关闭改由 `@ok`/`@cancel` 驱动
### 6. ProfileCreateModal 有两个重复的 style 块
- **现象**:无功能影响
- **位置**:`packages/client/src/components/hermes/profiles/ProfileCreateModal.vue`
- **证据**:两个 `<style scoped>` 内容相同(都是 `.modal-footer`)。迁移前存在
---
## 二、Arco 与 naive 的行为差异(可能表现为「功能坏了」)
迁移时按等价语义做了处理,但以下差异在某些边界条件下可能暴露。如遇异常,先查这里。
### a-input-password 默认是遮蔽的
- **风险**:任何给 `a-input-password` 传 `:default-visibility="false"` 的地方,密码/API Key 会**明文显示**
- **正确做法**:不要传这个 prop。源码 `es/input/input-password.js:77` 是 `type: mergedVisible ? "password" : "text"`,`defaultVisibility` 默认 `true` 即遮蔽
- **检查命令**:`grep -rn "default-visibility\|defaultVisibility" packages/client/src`(期望 0 命中)
### a-upload 默认会发真实 XHR
- **风险**:没加 `:auto-upload="false"` 的上传组件,选一次文件就向当前页 URL 发一个无意义 multipart 请求
- **正确做法**:凡是「只选文件、由 store 走 API」的上传,`a-upload` 必须显式加 `:auto-upload="false"`
- **涉及文件**:`ProfileImportModal.vue`、`SkillImportModal.vue`、`FileUploadModal.vue`
### Arco Trigger 不能嵌套
- **风险**:`a-tooltip` / `a-dropdown` / `a-popconfirm` / `a-popover` 互相嵌套时,菜单或提示**点不开**
- **原因**:它们内部都用 `mergeFirstChild` 接管首个元素节点的事件,多层会互相踩
- **正确做法**:同一元素既要 tooltip 又要 dropdown 时,tooltip 改用原生 `:title`
- **已处理**:`GroupChatInput.vue` 的设置按钮(`NDropdown > NTooltip > NButton` → `a-dropdown > a-button` + `:title`)、`ChatInput.vue` L1130-1162
### tooltip/dropdown 默认插槽必须只含一个元素
- **风险**:尾随的纯文本(`{{ t(...) }}`)会被当可见内容渲染进布局流,变成 **0 宽的 flex item**,把工具栏挤成竖排或让文本被截断
- **正确做法**:文案放 `content` prop,默认插槽只留触发元素
- **已踩坑位置**:`ChatInput.vue` 三处、`GroupChatInput.vue`、`TerminalPanel.vue`、`KanbanTaskCard.vue`、`KanbanView.vue` 排序按钮
- **自查**:`grep -rn "a-tooltip" -A 8 packages/client/src | grep -B2 "{{ t"` 然后肉眼确认
### NDropdown 的 droplist 没有对应
- **风险**:给 `a-dropdown` 传 `droplist` 会**静默失效**(Arco 不对未声明的 prop 报错,未声明的 prop 落为普通 attribute)
- **正确做法**:菜单写进 `#content` 插槽用 `<a-doption>`
- **历史**:Phase 006 的三个下拉全打不开,根因就是这个
### a-input / a-textarea 的 modelValue 不接受 null
- **风险**:`null` 初值会导致该组件显示异常或 TS 报错
- **正确做法**:`:model-value="x ?? undefined"`,配合 `@change="x = $event ?? null"` 桥接保住 null 语义
- **涉及**:`AuxiliaryModelsPanel.vue`、`DisplaySettings.vue`、`VoiceApiFormModal.vue`、`KanbanCreateForm.vue`、`JobFormModal.vue`、`GroupChatPanel.vue`
### a-spin 的 size 是 Number(px)不是字符串档位
- **风险**:传 `size="small"` 会被拼成 `fontSize: "smallpx"`
- **正确做法**:传 `:size="14"` 这样的数字
- **涉及**:`PlatformSettings.vue`、`ProfileCard.vue`、`JobRunHistory.vue`、`VoiceApiFormModal.vue`
### a-input-password 没有 width prop
- **风险**:`:width="220"` 被静默丢弃(底层 Trigger 设了 `inheritAttrs: false`)
- **正确做法**:改用 `:content-style`
### a-scrollbar 用 max-height 会高度塌陷
- **正确做法**:必须显式给 `height`
- **涉及**:`McpManagerView.vue`
### a-modal 的 footer 默认 true
- **风险**:原本没有 footer 插槽的弹窗,会多出取消/确定按钮
- **正确做法**:无自定义 footer 插槽时加 `:footer="false"`;**有**插槽时**绝对不能**加(加了插槽内容不渲染)
### Arco 按钮在 loading 时吞掉后续 click
- **风险**:快速连点同一按钮,第二次点击不触发
- **影响**:`SkillsUsageView.vue` 的刷新按钮(测试已改写场景绕过,但 UI 上真实存在)
- **备注**:不是 bug,是 Arco 的行为
---
## 三、自动化测不到、需手工确认的风险点
单元测试和 e2e 都覆盖不到这些。首次正式使用时请重点走一遍。
### 右键菜单
- **文件**:`FileContextMenu.vue`、`WorkflowView.vue`、`HistoryView.vue`、`GroupChatPanel.vue`
- **风险**:naive 的 `NDropdown trigger="manual"` + `:x/:y` 迁到了 `a-trigger trigger="contextMenu"` + `:popup-style`
- **需确认**:① 右键能弹出 ② **菜单打开时滚动页面,菜单是否跟随滚动**(Arco 在 `autoFitPosition: false` 时不重算位置,菜单会留在原地)
- **需确认**:③ 刚右键弹出就立刻点别处,菜单是否被误关(naive 靠 `contextMenuOpenedAt` 时间戳防抖,Arco 靠内置 `clickOutsideToClose`,时序可能微妙不同)
### 文件树懒加载
- **文件**:`FileTree.vue`(naive `NTree` → Arco `a-tree`)
- **需确认**:① 点目录能加载子目录 ② 只加载目录不加载文件(`filter(e => e.isDir)`)③ 快速切换目录时不会出现子节点串台(深层懒加载的竞态保护弱于根节点,`rootLoadSeq` 只在切 profile/session 时自增)
### Kanban 折叠与布局
- **文件**:`KanbanView.vue`、`KanbanColumn.vue`
- **备注**:迁移时发现 **kanban 实际没有拖拽逻辑**(排序靠卡片上的 sort 按钮),任务书里的「拖拽落点」风险不存在
- **需确认**:① 折叠/展开的列状态正确 ② Arco `a-collapse` 默认全部展开(已显式设 `:default-expand-all="false"`)
### 表格列的省略与 tooltip
- **文件**:`UserManagementSettings.vue`
- **备注**:naive 的 `ellipsis: { tooltip: true }` → Arco 的 `ellipsis: true` + 列顶层 `tooltip: true`
- **需确认**:长文本省略后悬浮能显示完整内容
### 视觉细节
- **圆角 tag**:naive 的 `NTag round` 无 Arco 对应,改用了 `border-radius: 999px`(`PetdexView.vue`、`DevicesView.vue`)
- **按钮尺寸**:naive 的 `size="tiny"` → Arco `size="mini"`,可能与相邻元素不齐
- **滚动条**:naive 的 `:native-scrollbar="false"` 无对应,已删除,滚动条样式会变
### 弹窗的遮罩/ESC 行为
- **风险**:Arco 默认 `maskClosable` 和 `escToClose` 都是 `true`。用户填了一半的表单按 ESC 或点遮罩会**直接丢弃且不重置**
- **涉及**:`VoiceApiFormModal.vue`、`VoiceApiConfigurator.vue`
- **需确认**:是否应该设 `:mask-closable="false"` / `:esc-to-close="false"`
### 模型选择弹窗(ProviderFormModal)
- **备注**:naive 的 `NButton type="primary" secondary`(浅色主按钮)在 Arco 无对应组合,去掉了 `secondary` 保留 `type="primary"`,视觉变成实心主色按钮
- **需确认**:视觉是否可接受
---
## 四、其它观察
- `TerminalView.vue` 的终端主题下拉选项 label 是**硬编码英文**(`"Default"`、`"Monokai"`),未走 i18n。迁移前既有问题
- `AuxiliaryModelsPanel.vue` 有 5 处手写 `<label>` + `<span>` 而非 `a-form-item`,与其它 Arco 表单的对齐/必填星号规则不一致
- `ProviderFormModal.vue` 的 `handleSave` 有 6 个近乎重复的 OAuth 分支,重复度高、容易漏改,建议后续合并成 provider→trigger 映射表
- `FileUploadModal.vue` 关闭时只清空了本地 `fileList`,未清 `a-upload` 内部状态。目前靠 modal 默认 `unmount-on-close` 兜底,**若有人关掉该 prop 会出现「列表已清空但 UI 仍显示」**
- `McpManagerView.vue` 的 `handleModeChange` 与 `FileContextMenu.vue` 的 `show()` 快速连点时,Arco popup 的 transition 理论上可能动画排队
---
## 相关文档
- `docs/arco-migration-rules.md` —— 迁移规约与完整组件映射表
- `docs/arco-mapping.md` —— naive → Arco 映射与结构差异
- `docs/chat-chain-changes/2026-10-18-phase007-arco-full-migration.md` —— Phase 007 变更清单