feat(frontend): 第二阶段前端审阅修复(Trace 建树 / 主题包 CSS / 引用定位 / 插件命令) #23

Closed
saint wants to merge 0 commits from feat/frontend-phase2-themes-trace-mermaid into main
Collaborator

背景

上一版前端第二阶段 PR(#18)被审阅关闭,结论「请求修改,暂不合并」,共 6 项 P2 问题。本 PR 在同一分支上逐项修复并补充回归测试,功能范围与 #18 一致(Agent Trace 可视化、主题清单/导入、Mermaid 渲染、插件命令与设置 UI)。

审阅版本 12869b5,本次提交 f273fef

审阅问题修复(6 项 P2)

1. 主题导入未读取用户提供的 CSS

inspectThemePackage 原先只解析 YAML 清单,ThemesView.confirmInstall 另外调用 generateThemeCss() 生成硬编码演示配色 —— 用户提供的 CSS 被整份丢弃。

  • 定义并落实单文件主题包格式:YAML 清单 + 独占一行的 --- + 主题 CSS,新增 parseThemePackage() 取出真实 CSS,安装时原样使用,已删除 generateThemeCss
  • 浏览器端无解压能力,明确拒绝 ZIP 而不是当文本解析出乱码再报「清单无效」:按内容嗅探 PK\x03\x04 与 base64 的 UEsDB(文件选择器的 accept 只是提示,不能依赖),返回 THEME_PACKAGE_UNSUPPORTED_FORMAT
  • accept 收窄为 .yaml,.yml,.theme,统一 readAsText
  • CSS 安全校验提前到预览阶段:不合规的包在「预览」就被拒,不再等到用户点安装;installTheme 内保留 validateCssSafety 必须先于 applyThemeCss 的约束与注释(未校验的 CSS 一律不进页面)。
  • 预览面板新增 <details> 展示将要安装的 CSS 原文与字符数,便于用户核对。

2. 刷新页面后无法恢复自定义主题

initTheme 原先 void loadCustomThemes() 未 await,applyTheme(saved) 因主题不在 allThemes 里而静默 return,页面停在完全没有 data-theme 的裸状态。

  • applyTheme 改为返回 boolean,主题不存在时返回 false 而非静默失败。
  • 拆成两步:先同步落一个内置主题兜底{ persist: false },避免把用户存的自定义主题 id 冲掉,也不让首屏干等接口),await loadCustomThemes() 之后再切到真正保存的那个。
  • 保存的主题已被卸载时回退默认主题、清除失效记录,并通过新增的 themeLoadWarning 告知用户;loadCustomThemes 的 catch 不再吞掉异常。

3. Trace 建树逻辑与真实后端事件顺序不一致

核对 backend/app/agent/runtime.py 后确认:真实顺序是 ModelCallStarted → ModelCallCompleted → Usage → ToolCall/ToolResult,工具在模型调用完成之后才执行,且多个工具并发跑(asyncio.gather + Semaphore)会交错到达 —— 事件相邻性根本不可用。

  • 归属关系全部改为 id 关联:model_call_id / parent_model_call_id / tool_call_id
  • ToolResult 不再单独成节点,而是回填对应 ToolCall 的状态与耗时,结束后不会再停在 running;结果数据合并进调用节点供展开查看。
  • ModelCallCompleted/Failed 同样只更新对应节点状态。
  • SSE 断点恢复只拿到后半段时,配不上父节点的孤立事件退回顶层而不是被丢弃;此时落单的失败 ToolResult 也按 success 显示 error,不再一律算成功。
  • 顺带修正 Usage 展示字段:后端只发累计的 token_usage,原实现读的 input_tokens/output_tokens/total_tokens 都不存在。

4. 树形视图叶子节点无法展开详情

行的 click 是 children.length && toggleExpand(id),而详情 v-if 又要求 children.length === 0 —— 两个条件互斥,叶子详情永远打不开。

  • 拆成 expandedNodes(子树展开)与 detailNodes(查看本节点数据)两个独立状态集合,任何节点含叶子都能查看自身数据。
  • 展开箭头改为独立 <button> + @click.stop + aria-label;行本身 role="button" tabindex="0",支持 Enter/Space,aria-expanded 跟随详情开合。

5. 引用点击未接入笔记定位流程

  • 抽出 navigateToCitation()(纯函数 + 依赖注入,可脱离 Vue 上下文测试)与 useCitationNavigation() 组合式封装。
  • 调用顺序固化并注明原因:必须先 await loadFilehighlightBlock —— stores/editor.tsloadFile 末尾会把 highlightBlockId 清空,顺序反了高亮会被自己冲掉;loadFile 失败时抛出且不跳转,避免把用户从未保存的编辑器里弹走。
  • AgentView 接上 @open-citation 并把失败原因显示给用户;顺带消除原有三处重复实现:ChatView 改为委托同一函数,AppShell 里那份既无调用方又漏了 loadFile 的副本一并修正。
  • Trace 引用节点新增「定位」按钮。

6. 新命令页缺少参数输入和实际效果执行

排查后发现 PluginsView 的命令页是 PluginMcpPanel 已有 schema 驱动表单的劣化副本,因此按建议复用而非各自改进:

  • 抽出共享组件 PluginCommandPanel.vueloadVersion 防竞态、重新加载时重置表单避免用旧值提交非法参数、when 条件判断),PluginMcpPanelPluginsView 均改为复用,删除两份重复实现与相关孤立 CSS。
  • 新增纯逻辑模块 pluginCommandForm.tscommandFields / initialArguments / coerceArgument / missingRequiredFields / cleanArguments / applyCommandEffect
  • effect 真的执行navigate 走路由白名单(未知路由只提示,不会 router.push(undefined))、refresh 按 scope 实际派发(新增 workspaceStore.refreshFileTree()),不再只拼成说明文本。
  • 同时修掉原 PluginMcpPanel 里两个潜在问题:必填字段标了「必填」却无客户端校验(只能靠后端拒绝才知道),以及布尔下拉框显示「否」但参数对象里没有该键 —— UI 与实际提交值不一致。

测试

  • 回归测试 +64 项(相关 spec 由 25 项增至 89 项):
    • traceService.spec.ts —— 按真实后端事件顺序重写建树用例,覆盖并发工具交错到达、ToolResult 回填状态、SSE 断点恢复孤立事件。
    • theme.spec.ts —— 覆盖 await 后恢复、首屏兜底不覆盖已存 id、主题失效回退与告警、列表加载失败告警。
    • themePackageService.spec.ts —— 新增 parseThemePackage 全套用例(真实 CSS 提取、CSS 内含 --- 不被截断、CRLF、缺分隔行、空 CSS、ZIP 与 base64 ZIP 拒绝),并补「注入的 CSS 与包内内容逐字一致」。
    • 新增 pluginCommandForm.spec.tsuseCitationNavigation.spec.tsTraceTimeline.spec.ts
  • 对第 2、3、4 项做了变异验证:把修复分别回退成原写法后重跑,7 个对应用例确实失败,确认测试不是空转。
  • 全量:vue-tsc -b --force 通过,vitest 32 个文件 / 182 项全部通过vite build 成功。

已知项 / 待确认

  • 仍存在大体积 chunk 警告(MarkdownContent 约 1.5 MB,主因 mermaid + katex)。可改为动态 import 拆分,但会影响首次渲染时序,倾向单独一个 PR 处理,本轮未动。
  • tsconfig.app.jsonskipLibCheck: true 是绕开 mermaid 打包缺陷(它把 type-fest 泄漏进发布类型却只声明为自身 devDependency)。另一条路是自行补 type-fest@^4 devDependency(^5 需要 ES2024,与本项目 ES2022 目标冲突)。听审阅意见取舍。
  • 尚未进行真实浏览器端到端联调(与 #18 相同,仍是待补项)。

范围

frontend/src 下第二阶段前端相关文件,未改动 backend 与契约实现(contracts/index.ts 仅新增 ThemePackageInspection.css 字段与两个主题错误码)。

## 背景 上一版前端第二阶段 PR(#18)被审阅关闭,结论「请求修改,暂不合并」,共 6 项 P2 问题。本 PR 在同一分支上逐项修复并补充回归测试,功能范围与 #18 一致(Agent Trace 可视化、主题清单/导入、Mermaid 渲染、插件命令与设置 UI)。 审阅版本 `12869b5`,本次提交 `f273fef`。 ## 审阅问题修复(6 项 P2) **1. 主题导入未读取用户提供的 CSS** `inspectThemePackage` 原先只解析 YAML 清单,`ThemesView.confirmInstall` 另外调用 `generateThemeCss()` 生成硬编码演示配色 —— 用户提供的 CSS 被整份丢弃。 - 定义并落实单文件主题包格式:**YAML 清单 + 独占一行的 `---` + 主题 CSS**,新增 `parseThemePackage()` 取出真实 CSS,安装时原样使用,已删除 `generateThemeCss`。 - 浏览器端无解压能力,**明确拒绝 ZIP** 而不是当文本解析出乱码再报「清单无效」:按内容嗅探 `PK\x03\x04` 与 base64 的 `UEsDB`(文件选择器的 `accept` 只是提示,不能依赖),返回 `THEME_PACKAGE_UNSUPPORTED_FORMAT`。 - `accept` 收窄为 `.yaml,.yml,.theme`,统一 `readAsText`。 - **CSS 安全校验提前到预览阶段**:不合规的包在「预览」就被拒,不再等到用户点安装;`installTheme` 内保留 `validateCssSafety` 必须先于 `applyThemeCss` 的约束与注释(未校验的 CSS 一律不进页面)。 - 预览面板新增 `<details>` 展示将要安装的 CSS 原文与字符数,便于用户核对。 **2. 刷新页面后无法恢复自定义主题** `initTheme` 原先 `void loadCustomThemes()` 未 await,`applyTheme(saved)` 因主题不在 `allThemes` 里而静默 `return`,页面停在完全没有 `data-theme` 的裸状态。 - `applyTheme` 改为返回 `boolean`,主题不存在时返回 `false` 而非静默失败。 - 拆成两步:**先同步落一个内置主题兜底**(`{ persist: false }`,避免把用户存的自定义主题 id 冲掉,也不让首屏干等接口),`await loadCustomThemes()` 之后再切到真正保存的那个。 - 保存的主题已被卸载时回退默认主题、清除失效记录,并通过新增的 `themeLoadWarning` 告知用户;`loadCustomThemes` 的 catch 不再吞掉异常。 **3. Trace 建树逻辑与真实后端事件顺序不一致** 核对 `backend/app/agent/runtime.py` 后确认:真实顺序是 `ModelCallStarted → ModelCallCompleted → Usage → ToolCall/ToolResult`,工具在模型调用**完成之后**才执行,且多个工具并发跑(`asyncio.gather` + `Semaphore`)会交错到达 —— 事件相邻性根本不可用。 - 归属关系全部改为 id 关联:`model_call_id` / `parent_model_call_id` / `tool_call_id`。 - `ToolResult` 不再单独成节点,而是**回填对应 `ToolCall` 的状态与耗时**,结束后不会再停在 `running`;结果数据合并进调用节点供展开查看。 - `ModelCallCompleted/Failed` 同样只更新对应节点状态。 - SSE 断点恢复只拿到后半段时,配不上父节点的孤立事件**退回顶层而不是被丢弃**;此时落单的失败 `ToolResult` 也按 `success` 显示 error,不再一律算成功。 - 顺带修正 Usage 展示字段:后端只发累计的 `token_usage`,原实现读的 `input_tokens/output_tokens/total_tokens` 都不存在。 **4. 树形视图叶子节点无法展开详情** 行的 click 是 `children.length && toggleExpand(id)`,而详情 `v-if` 又要求 `children.length === 0` —— 两个条件互斥,叶子详情永远打不开。 - 拆成 `expandedNodes`(子树展开)与 `detailNodes`(查看本节点数据)两个独立状态集合,任何节点含叶子都能查看自身数据。 - 展开箭头改为独立 `<button>` + `@click.stop` + `aria-label`;行本身 `role="button" tabindex="0"`,支持 Enter/Space,`aria-expanded` 跟随详情开合。 **5. 引用点击未接入笔记定位流程** - 抽出 `navigateToCitation()`(纯函数 + 依赖注入,可脱离 Vue 上下文测试)与 `useCitationNavigation()` 组合式封装。 - **调用顺序固化并注明原因**:必须先 `await loadFile` 再 `highlightBlock` —— `stores/editor.ts` 的 `loadFile` 末尾会把 `highlightBlockId` 清空,顺序反了高亮会被自己冲掉;`loadFile` 失败时抛出且不跳转,避免把用户从未保存的编辑器里弹走。 - `AgentView` 接上 `@open-citation` 并把失败原因显示给用户;顺带消除原有三处重复实现:`ChatView` 改为委托同一函数,`AppShell` 里那份既无调用方又漏了 `loadFile` 的副本一并修正。 - Trace 引用节点新增「定位」按钮。 **6. 新命令页缺少参数输入和实际效果执行** 排查后发现 `PluginsView` 的命令页是 `PluginMcpPanel` 已有 schema 驱动表单的**劣化副本**,因此按建议复用而非各自改进: - 抽出共享组件 `PluginCommandPanel.vue`(`loadVersion` 防竞态、重新加载时重置表单避免用旧值提交非法参数、`when` 条件判断),`PluginMcpPanel` 与 `PluginsView` 均改为复用,删除两份重复实现与相关孤立 CSS。 - 新增纯逻辑模块 `pluginCommandForm.ts`:`commandFields` / `initialArguments` / `coerceArgument` / `missingRequiredFields` / `cleanArguments` / `applyCommandEffect`。 - **effect 真的执行**:`navigate` 走路由白名单(未知路由只提示,不会 `router.push(undefined)`)、`refresh` 按 scope 实际派发(新增 `workspaceStore.refreshFileTree()`),不再只拼成说明文本。 - 同时修掉原 `PluginMcpPanel` 里两个潜在问题:必填字段标了「必填」却无客户端校验(只能靠后端拒绝才知道),以及布尔下拉框显示「否」但参数对象里没有该键 —— UI 与实际提交值不一致。 ## 测试 - 回归测试 **+64 项**(相关 spec 由 25 项增至 89 项): - `traceService.spec.ts` —— 按真实后端事件顺序重写建树用例,覆盖并发工具交错到达、`ToolResult` 回填状态、SSE 断点恢复孤立事件。 - `theme.spec.ts` —— 覆盖 await 后恢复、首屏兜底不覆盖已存 id、主题失效回退与告警、列表加载失败告警。 - `themePackageService.spec.ts` —— 新增 `parseThemePackage` 全套用例(真实 CSS 提取、CSS 内含 `---` 不被截断、CRLF、缺分隔行、空 CSS、ZIP 与 base64 ZIP 拒绝),并补「注入的 CSS 与包内内容逐字一致」。 - 新增 `pluginCommandForm.spec.ts`、`useCitationNavigation.spec.ts`、`TraceTimeline.spec.ts`。 - 对第 2、3、4 项做了**变异验证**:把修复分别回退成原写法后重跑,7 个对应用例确实失败,确认测试不是空转。 - 全量:`vue-tsc -b --force` 通过,`vitest` **32 个文件 / 182 项全部通过**,`vite build` 成功。 ## 已知项 / 待确认 - 仍存在大体积 chunk 警告(`MarkdownContent` 约 1.5 MB,主因 mermaid + katex)。可改为动态 import 拆分,但会影响首次渲染时序,倾向单独一个 PR 处理,本轮未动。 - `tsconfig.app.json` 的 `skipLibCheck: true` 是绕开 mermaid 打包缺陷(它把 `type-fest` 泄漏进发布类型却只声明为自身 devDependency)。另一条路是自行补 `type-fest@^4` devDependency(`^5` 需要 ES2024,与本项目 ES2022 目标冲突)。听审阅意见取舍。 - 尚未进行真实浏览器端到端联调(与 #18 相同,仍是待补项)。 ## 范围 仅 `frontend/src` 下第二阶段前端相关文件,未改动 backend 与契约实现(`contracts/index.ts` 仅新增 `ThemePackageInspection.css` 字段与两个主题错误码)。
saint added 2 commits 2026-09-05 10:08:01 +08:00
实现第二阶段分工表中吉海燕负责的 P0/P1 前端能力。

- Agent Trace 可视化:新增 traceService 将扁平事件流折叠为树
  (ModelCallStarted 区间内的工具/文本事件挂为子节点,运行级事件保持顶层),
  TraceTimeline 支持时间线/树两种视图、耗时统计与引用跳转。
- 主题包:新增 themePackageService(Web Mock Adapter),
  校验 manifest 必填字段与 theme_id 格式,拒绝远程 css_entry;
  CSS 侧拒绝 @import / expression() / javascript:,
  未通过校验的 CSS 不会注入页面。内置主题走 data-theme=light|dark|sepia,
  自定义主题走 data-theme={theme_id} + 独立 style 节点。
  ThemesView 增加“已安装/社区主题”两个标签页与导入、预览、卸载流程。
- Mermaid:新增 mermaidService(securityLevel: strict)与 MermaidBlock,
  markdown 渲染管线识别 mermaid 代码块;MarkdownContent 随亮/暗主题重渲染
  (SVG 配色在渲染时烘焙,无法靠 CSS 变量事后调整)。
- 插件贡献 UI:PluginsView 增加“概览/命令/设置”标签页,
  PluginSettingsPanel 按 Schema 动态生成表单;
  secret 字段只写不读,仅展示 configured 状态,不进 store 也不回显。

与 main 上队友成果的整合(rebase 时处理):
- 命令面板保留队友基于真实后端的实现(when 条件求值、效果白名单、
  参数命令跳详情页),仅叠加我新增的主题/任务两条内置命令。
- 删除我先前的 pluginContributionService(mock 版),
  统一改用队友已落地的 pluginService 真实接口;
  相应修正表单以匹配真实契约(options 为 string[]、min/max 可空、无 placeholder)。
- 移除 contracts 中与队友重复的 PluginHostStatus / PluginCommand /
  PluginSettingField / PluginSettingsSchema 声明,以队友版本为准。
- PluginsView 概览页保留队友的 PluginMcpPanel,并补回被我改写时丢掉的空状态。

顺带修复:
- 开启 skipLibCheck —— mermaid 11.17 把 type-fest 泄漏进了发布产物的
  .d.ts,但只声明为自身 devDependency,vue-tsc -b 会因此报错。

验证:pnpm test 26 文件 / 113 测试通过(新增 traceService、
themePackageService 两个测试文件共 22 项);pnpm build 通过。
审阅意见逐项修复:

1. 主题包安装丢弃用户 CSS
   inspectThemePackage 之前只解析 YAML 清单,ThemesView 安装时另外
   生成一套硬编码调色板,用户提供的 CSS 被整份丢掉。现在定义单文件
   格式(YAML 清单 + `---` + CSS),parseThemePackage 取出真实 CSS
   并原样安装;CSS 安全校验提前到预览阶段;按内容识别并拒绝 ZIP。

2. 主题恢复竞态导致页面无 data-theme
   initTheme 之前没有 await loadCustomThemes,自定义主题还没进
   allThemes,applyTheme 找不到主题直接 return。现在先同步落一个
   内置主题兜底(不写 localStorage,避免冲掉用户存的自定义主题 id),
   加载完成后再切到真正保存的那个;主题失效或列表加载失败时回退并
   通过 themeLoadWarning 告知用户,不再静默。

3. Trace 建树依赖事件相邻顺序
   后端真实顺序是 ModelCallStarted → ModelCallCompleted → Usage →
   ToolCall/ToolResult,工具在模型调用完成后才执行且并发跑,相邻性
   不可用。改为按 model_call_id / parent_model_call_id / tool_call_id
   关联;ToolResult 回填 ToolCall 的状态与耗时,结束后不再显示
   running;SSE 断点恢复的孤立事件退回顶层而不是丢弃。

4. Trace 叶子节点无法查看数据
   行的 click 是 `children.length && toggleExpand`,而详情 v-if 又
   要求 `children.length === 0`,两个条件互斥。拆成 expandedNodes
   与 detailNodes 两个状态集合;展开箭头改为独立按钮,行支持键盘
   与 aria-expanded;引用节点补「定位」按钮。同时修正 Usage 卡片
   字段(后端只发累计 token_usage)。

5. 引用定位逻辑三处重复且各自有缺陷
   抽出 navigateToCitation(依赖注入,可独立测试)+ useCitationNavigation。
   调用顺序固化:必须先 await loadFile 再 highlightBlock,否则
   editor store 的 loadFile 末尾会把高亮清掉;loadFile 失败时不跳转。
   AgentView / ChatView / AppShell 统一走这一处。

6. 插件命令 UI 重复实现
   抽出 PluginCommandPanel 复用 PluginMcpPanel 的 schema 驱动表单,
   删除 PluginsView 里的劣化副本。effect 现在真的执行 navigate /
   refresh(此前只拼成文本显示);补上必填校验与布尔字段初始值,
   修正「显示否但不提交该键」的不一致。

补充回归测试 64 项(相关 spec 由 25 项增至 89 项),并对 2、3、4 三项
缺陷做了变异验证:把修复回退成原写法后对应测试确实失败。
涉及 traceService / theme store / themePackageService / pluginCommandForm /
useCitationNavigation / TraceTimeline,其中后三个为新增文件。

vue-tsc -b、vitest(32 文件 182 项)、vite build 全部通过。
Owner

审阅结论:请求修改,暂不合并

审阅 PR #23,版本 f273fef,目标分支 main 为 1c7b5b4

本轮发现两个已复现的问题,另有 8 个文件存在合并冲突。建议完成以下修复并解决冲突后,再进行合并验证。

[P2] 未启用主题的 CSS 仍会影响当前页面

位置:frontend/src/services/themePackageService.ts:235–240

安装主题时会立即注入 CSS,加载主题列表时又会注入所有已安装主题的 CSS。当前校验仅对缺少主题作用域给出警告,因此包内的 body:root 等规则会直接影响当前页面,即使用户尚未启用该主题。

已复现:

  1. 当前主题保持为 light
  2. 安装包含 body { color: rgb(1, 2, 3) !important; } 的自定义主题。
  3. 自定义主题未启用,但页面 body 颜色已发生变化。

切回内置主题也不会移除这些规则,多个已安装主题之间可能互相覆盖。

建议只挂载当前主题的 CSS,并在切换时移除旧主题样式;或者强制隔离主题包中的所有规则,确保未启用主题不会影响页面。

建议补充回归测试:

  • 安装未启用主题不改变当前页面样式。
  • 切换回内置主题后,自定义主题样式不再生效。
  • 多个已安装主题之间不会互相污染。

[P2] 保存响应会错误清除请求期间的新编辑状态

位置:frontend/src/features/plugins/PluginSettingsPanel.vue:56–62

设置保存期间输入框仍可编辑,但请求成功后会无条件执行 hasChanges = false。这会把请求发出后产生的新编辑错误地标记为已保存。

已复现:

  1. 输入 first edit 并点击保存。
  2. 保存请求尚未返回时,将内容改为 second edit
  3. 请求返回,后端保存的是 first edit
  4. 界面仍显示 second edit,但保存按钮已禁用,未保存提示也已消失。

此时用户无法直接保存最新输入,离开页面还可能丢失该修改。

建议使用编辑版本或提交快照判断是否仍有未保存内容;也可以在保存期间禁用编辑,确保显示状态与实际持久化结果一致。

建议补充回归测试:

  • 保存期间继续编辑,响应返回后仍保留未保存状态。
  • 再次保存时提交最新内容。
  • 保存失败后保留用户输入并允许重试。

合并冲突

当前 PR 与最新 main 存在以下 8 个文件冲突:

  • frontend/src/components/common/CommandPalette.vue
  • frontend/src/features/agent/AgentView.vue
  • frontend/src/features/chat/ChatView.vue
  • frontend/src/features/plugins/PluginMcpPanel.vue
  • frontend/src/features/plugins/PluginsView.vue
  • frontend/src/features/themes/ThemesView.vue
  • frontend/src/main.ts
  • frontend/src/stores/theme.ts

解决冲突时需保留 main 已合入的中英文支持、聊天记录持久化及会话并发修复,随后对合并结果重新运行测试和构建。

验证记录

  • PR 原分支:182 项测试通过。
  • 类型检查及生产构建通过。
  • 本轮补充的两个复现测试均失败,确认上述问题。
  • 尚未进行真实浏览器端到端验收。
  • 当前通过结果针对 PR 原分支,不代表解决冲突后的合并版本。

请修复上述问题、补充回归测试并解决合并冲突后再次请求审阅。

## 审阅结论:请求修改,暂不合并 审阅 PR #23,版本 `f273fef`,目标分支 main 为 `1c7b5b4`。 本轮发现两个已复现的问题,另有 8 个文件存在合并冲突。建议完成以下修复并解决冲突后,再进行合并验证。 ### [P2] 未启用主题的 CSS 仍会影响当前页面 位置:`frontend/src/services/themePackageService.ts:235–240` 安装主题时会立即注入 CSS,加载主题列表时又会注入所有已安装主题的 CSS。当前校验仅对缺少主题作用域给出警告,因此包内的 `body`、`:root` 等规则会直接影响当前页面,即使用户尚未启用该主题。 已复现: 1. 当前主题保持为 `light`。 2. 安装包含 `body { color: rgb(1, 2, 3) !important; }` 的自定义主题。 3. 自定义主题未启用,但页面 body 颜色已发生变化。 切回内置主题也不会移除这些规则,多个已安装主题之间可能互相覆盖。 建议只挂载当前主题的 CSS,并在切换时移除旧主题样式;或者强制隔离主题包中的所有规则,确保未启用主题不会影响页面。 建议补充回归测试: - 安装未启用主题不改变当前页面样式。 - 切换回内置主题后,自定义主题样式不再生效。 - 多个已安装主题之间不会互相污染。 ### [P2] 保存响应会错误清除请求期间的新编辑状态 位置:`frontend/src/features/plugins/PluginSettingsPanel.vue:56–62` 设置保存期间输入框仍可编辑,但请求成功后会无条件执行 `hasChanges = false`。这会把请求发出后产生的新编辑错误地标记为已保存。 已复现: 1. 输入 `first edit` 并点击保存。 2. 保存请求尚未返回时,将内容改为 `second edit`。 3. 请求返回,后端保存的是 `first edit`。 4. 界面仍显示 `second edit`,但保存按钮已禁用,未保存提示也已消失。 此时用户无法直接保存最新输入,离开页面还可能丢失该修改。 建议使用编辑版本或提交快照判断是否仍有未保存内容;也可以在保存期间禁用编辑,确保显示状态与实际持久化结果一致。 建议补充回归测试: - 保存期间继续编辑,响应返回后仍保留未保存状态。 - 再次保存时提交最新内容。 - 保存失败后保留用户输入并允许重试。 ### 合并冲突 当前 PR 与最新 main 存在以下 8 个文件冲突: - `frontend/src/components/common/CommandPalette.vue` - `frontend/src/features/agent/AgentView.vue` - `frontend/src/features/chat/ChatView.vue` - `frontend/src/features/plugins/PluginMcpPanel.vue` - `frontend/src/features/plugins/PluginsView.vue` - `frontend/src/features/themes/ThemesView.vue` - `frontend/src/main.ts` - `frontend/src/stores/theme.ts` 解决冲突时需保留 main 已合入的中英文支持、聊天记录持久化及会话并发修复,随后对合并结果重新运行测试和构建。 ### 验证记录 - PR 原分支:182 项测试通过。 - 类型检查及生产构建通过。 - 本轮补充的两个复现测试均失败,确认上述问题。 - 尚未进行真实浏览器端到端验收。 - 当前通过结果针对 PR 原分支,不代表解决冲突后的合并版本。 请修复上述问题、补充回归测试并解决合并冲突后再次请求审阅。
Kronecker closed this pull request 2026-09-05 15:32:31 +08:00
Owner

变更说明

整合最新 main,并修复 frontend phase2 前两轮审阅发现的问题。

主题管理

  • 安装和加载主题列表时不再注入 CSS,仅挂载当前启用主题的样式。
  • 切换主题时移除旧样式,返回内置主题后不残留自定义 CSS。
  • 未安装的社区主题可在隔离预览窗口中展示实际 CSS,不安装主题、不修改当前主题。
  • 移除预览结束后的延迟回滚逻辑,避免覆盖用户后续选择。

插件设置

  • 普通设置保存使用编辑版本判断,保留请求期间产生的新修改。
  • 密钥保存使用提交快照,仅清空未变化的输入。
  • 保存失败保留输入并允许重试,阻止重复提交。
  • 切换插件或卸载组件后忽略旧请求响应。

与 main 整合

  • 解决 9 个冲突文件。
  • 保留 main 的聊天持久化、会话并发修复、中英文支持及完整 Shiki 语言与图标能力。
  • 保留 phase2 的 Trace 可视化、引用导航、主题包、Mermaid 和共享插件命令表单。

验证

  • 40 个前端测试文件、233 项测试通过。
  • 类型检查和生产构建通过。
  • 回归测试覆盖主题样式隔离、全部社区主题预览、保存期间继续编辑、失败重试和旧响应隔离。
  • 浏览器验证主题切换、预览窗口隔离、延迟保存,以及 Mermaid 与 Shiki 渲染。

限制

  • 构建仍有大体积 chunk 提示。
  • 插件请求的浏览器验证使用测试接口,未执行生产插件后端端到端验收。
## 变更说明 整合最新 main,并修复 frontend phase2 前两轮审阅发现的问题。 ### 主题管理 - 安装和加载主题列表时不再注入 CSS,仅挂载当前启用主题的样式。 - 切换主题时移除旧样式,返回内置主题后不残留自定义 CSS。 - 未安装的社区主题可在隔离预览窗口中展示实际 CSS,不安装主题、不修改当前主题。 - 移除预览结束后的延迟回滚逻辑,避免覆盖用户后续选择。 ### 插件设置 - 普通设置保存使用编辑版本判断,保留请求期间产生的新修改。 - 密钥保存使用提交快照,仅清空未变化的输入。 - 保存失败保留输入并允许重试,阻止重复提交。 - 切换插件或卸载组件后忽略旧请求响应。 ### 与 main 整合 - 解决 9 个冲突文件。 - 保留 main 的聊天持久化、会话并发修复、中英文支持及完整 Shiki 语言与图标能力。 - 保留 phase2 的 Trace 可视化、引用导航、主题包、Mermaid 和共享插件命令表单。 ## 验证 - 40 个前端测试文件、233 项测试通过。 - 类型检查和生产构建通过。 - 回归测试覆盖主题样式隔离、全部社区主题预览、保存期间继续编辑、失败重试和旧响应隔离。 - 浏览器验证主题切换、预览窗口隔离、延迟保存,以及 Mermaid 与 Shiki 渲染。 ## 限制 - 构建仍有大体积 chunk 提示。 - 插件请求的浏览器验证使用测试接口,未执行生产插件后端端到端验收。
Kronecker reopened this pull request 2026-09-05 17:42:48 +08:00
Owner

审阅结论:同意合并。

此前提出的主题 CSS 泄漏、保存状态竞态、密钥新输入丢失和社区主题预览无效问题均已修复,并补充回归测试。

本轮复审未发现新的阻塞问题。最新 main 的聊天持久化、会话并发修复、中英文支持和 Shiki 能力得到保留。

验证结果:233 项前端测试、类型检查及生产构建通过;浏览器已验证主题隔离、实际预览与保存行为。

生产插件后端端到端验收尚未覆盖,构建体积提示可作为后续优化项。

审阅结论:同意合并。 此前提出的主题 CSS 泄漏、保存状态竞态、密钥新输入丢失和社区主题预览无效问题均已修复,并补充回归测试。 本轮复审未发现新的阻塞问题。最新 main 的聊天持久化、会话并发修复、中英文支持和 Shiki 能力得到保留。 验证结果:233 项前端测试、类型检查及生产构建通过;浏览器已验证主题隔离、实际预览与保存行为。 生产插件后端端到端验收尚未覆盖,构建体积提示可作为后续优化项。
Kronecker closed this pull request 2026-09-05 17:43:07 +08:00
Kronecker reopened this pull request 2026-09-05 17:43:11 +08:00
Kronecker closed this pull request 2026-09-05 17:45:12 +08:00

Pull request closed

Please reopen this pull request to perform a merge.
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Kronecker/NotesAgentic#23