feat/frontend-phase2-themes-trace-mermaid
main
上一版前端第二阶段 PR(#18)被审阅关闭,结论「请求修改,暂不合并」,共 6 项 P2 问题。本 PR 在同一分支上逐项修复并补充回归测试,功能范围与 #18 一致(Agent Trace 可视化、主题清单/导入、Mermaid 渲染、插件命令与设置 UI)。
审阅版本 12869b5,本次提交 f273fef。
12869b5
f273fef
1. 主题导入未读取用户提供的 CSS
inspectThemePackage 原先只解析 YAML 清单,ThemesView.confirmInstall 另外调用 generateThemeCss() 生成硬编码演示配色 —— 用户提供的 CSS 被整份丢弃。
inspectThemePackage
ThemesView.confirmInstall
generateThemeCss()
---
parseThemePackage()
generateThemeCss
PK\x03\x04
UEsDB
accept
THEME_PACKAGE_UNSUPPORTED_FORMAT
.yaml,.yml,.theme
readAsText
installTheme
validateCssSafety
applyThemeCss
<details>
2. 刷新页面后无法恢复自定义主题
initTheme 原先 void loadCustomThemes() 未 await,applyTheme(saved) 因主题不在 allThemes 里而静默 return,页面停在完全没有 data-theme 的裸状态。
initTheme
void loadCustomThemes()
applyTheme(saved)
allThemes
return
data-theme
applyTheme
boolean
false
{ persist: false }
await loadCustomThemes()
themeLoadWarning
loadCustomThemes
3. Trace 建树逻辑与真实后端事件顺序不一致
核对 backend/app/agent/runtime.py 后确认:真实顺序是 ModelCallStarted → ModelCallCompleted → Usage → ToolCall/ToolResult,工具在模型调用完成之后才执行,且多个工具并发跑(asyncio.gather + Semaphore)会交错到达 —— 事件相邻性根本不可用。
backend/app/agent/runtime.py
ModelCallStarted → ModelCallCompleted → Usage → ToolCall/ToolResult
asyncio.gather
Semaphore
model_call_id
parent_model_call_id
tool_call_id
ToolResult
ToolCall
running
ModelCallCompleted/Failed
success
token_usage
input_tokens/output_tokens/total_tokens
4. 树形视图叶子节点无法展开详情
行的 click 是 children.length && toggleExpand(id),而详情 v-if 又要求 children.length === 0 —— 两个条件互斥,叶子详情永远打不开。
children.length && toggleExpand(id)
v-if
children.length === 0
expandedNodes
detailNodes
<button>
@click.stop
aria-label
role="button" tabindex="0"
aria-expanded
5. 引用点击未接入笔记定位流程
navigateToCitation()
useCitationNavigation()
await loadFile
highlightBlock
stores/editor.ts
loadFile
highlightBlockId
AgentView
@open-citation
ChatView
AppShell
6. 新命令页缺少参数输入和实际效果执行
排查后发现 PluginsView 的命令页是 PluginMcpPanel 已有 schema 驱动表单的劣化副本,因此按建议复用而非各自改进:
PluginsView
PluginMcpPanel
PluginCommandPanel.vue
loadVersion
when
pluginCommandForm.ts
commandFields
initialArguments
coerceArgument
missingRequiredFields
cleanArguments
applyCommandEffect
navigate
router.push(undefined)
refresh
workspaceStore.refreshFileTree()
traceService.spec.ts
theme.spec.ts
themePackageService.spec.ts
parseThemePackage
pluginCommandForm.spec.ts
useCitationNavigation.spec.ts
TraceTimeline.spec.ts
vue-tsc -b --force
vitest
vite build
MarkdownContent
tsconfig.app.json
skipLibCheck: true
type-fest
type-fest@^4
^5
仅 frontend/src 下第二阶段前端相关文件,未改动 backend 与契约实现(contracts/index.ts 仅新增 ThemePackageInspection.css 字段与两个主题错误码)。
frontend/src
contracts/index.ts
ThemePackageInspection.css
实现第二阶段分工表中吉海燕负责的 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 全部通过。
审阅 PR #23,版本 f273fef,目标分支 main 为 1c7b5b4。
1c7b5b4
本轮发现两个已复现的问题,另有 8 个文件存在合并冲突。建议完成以下修复并解决冲突后,再进行合并验证。
位置:frontend/src/services/themePackageService.ts:235–240
frontend/src/services/themePackageService.ts:235–240
安装主题时会立即注入 CSS,加载主题列表时又会注入所有已安装主题的 CSS。当前校验仅对缺少主题作用域给出警告,因此包内的 body、:root 等规则会直接影响当前页面,即使用户尚未启用该主题。
body
:root
已复现:
light
body { color: rgb(1, 2, 3) !important; }
切回内置主题也不会移除这些规则,多个已安装主题之间可能互相覆盖。
建议只挂载当前主题的 CSS,并在切换时移除旧主题样式;或者强制隔离主题包中的所有规则,确保未启用主题不会影响页面。
建议补充回归测试:
位置:frontend/src/features/plugins/PluginSettingsPanel.vue:56–62
frontend/src/features/plugins/PluginSettingsPanel.vue:56–62
设置保存期间输入框仍可编辑,但请求成功后会无条件执行 hasChanges = false。这会把请求发出后产生的新编辑错误地标记为已保存。
hasChanges = false
first edit
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 已合入的中英文支持、聊天记录持久化及会话并发修复,随后对合并结果重新运行测试和构建。
请修复上述问题、补充回归测试并解决合并冲突后再次请求审阅。
整合最新 main,并修复 frontend phase2 前两轮审阅发现的问题。
审阅结论:同意合并。
此前提出的主题 CSS 泄漏、保存状态竞态、密钥新输入丢失和社区主题预览无效问题均已修复,并补充回归测试。
本轮复审未发现新的阻塞问题。最新 main 的聊天持久化、会话并发修复、中英文支持和 Shiki 能力得到保留。
验证结果:233 项前端测试、类型检查及生产构建通过;浏览器已验证主题隔离、实际预览与保存行为。
生产插件后端端到端验收尚未覆盖,构建体积提示可作为后续优化项。
No dependencies set.
The note is not visible to the blocked user.
背景
上一版前端第二阶段 PR(#18)被审阅关闭,结论「请求修改,暂不合并」,共 6 项 P2 问题。本 PR 在同一分支上逐项修复并补充回归测试,功能范围与 #18 一致(Agent Trace 可视化、主题清单/导入、Mermaid 渲染、插件命令与设置 UI)。
审阅版本
12869b5,本次提交f273fef。审阅问题修复(6 项 P2)
1. 主题导入未读取用户提供的 CSS
inspectThemePackage原先只解析 YAML 清单,ThemesView.confirmInstall另外调用generateThemeCss()生成硬编码演示配色 —— 用户提供的 CSS 被整份丢弃。---+ 主题 CSS,新增parseThemePackage()取出真实 CSS,安装时原样使用,已删除generateThemeCss。PK\x03\x04与 base64 的UEsDB(文件选择器的accept只是提示,不能依赖),返回THEME_PACKAGE_UNSUPPORTED_FORMAT。accept收窄为.yaml,.yml,.theme,统一readAsText。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)会交错到达 —— 事件相邻性根本不可用。model_call_id/parent_model_call_id/tool_call_id。ToolResult不再单独成节点,而是回填对应ToolCall的状态与耗时,结束后不会再停在running;结果数据合并进调用节点供展开查看。ModelCallCompleted/Failed同样只更新对应节点状态。ToolResult也按success显示 error,不再一律算成功。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的副本一并修正。6. 新命令页缺少参数输入和实际效果执行
排查后发现
PluginsView的命令页是PluginMcpPanel已有 schema 驱动表单的劣化副本,因此按建议复用而非各自改进:PluginCommandPanel.vue(loadVersion防竞态、重新加载时重置表单避免用旧值提交非法参数、when条件判断),PluginMcpPanel与PluginsView均改为复用,删除两份重复实现与相关孤立 CSS。pluginCommandForm.ts:commandFields/initialArguments/coerceArgument/missingRequiredFields/cleanArguments/applyCommandEffect。navigate走路由白名单(未知路由只提示,不会router.push(undefined))、refresh按 scope 实际派发(新增workspaceStore.refreshFileTree()),不再只拼成说明文本。PluginMcpPanel里两个潜在问题:必填字段标了「必填」却无客户端校验(只能靠后端拒绝才知道),以及布尔下拉框显示「否」但参数对象里没有该键 —— UI 与实际提交值不一致。测试
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。vue-tsc -b --force通过,vitest32 个文件 / 182 项全部通过,vite build成功。已知项 / 待确认
MarkdownContent约 1.5 MB,主因 mermaid + katex)。可改为动态 import 拆分,但会影响首次渲染时序,倾向单独一个 PR 处理,本轮未动。tsconfig.app.json的skipLibCheck: true是绕开 mermaid 打包缺陷(它把type-fest泄漏进发布类型却只声明为自身 devDependency)。另一条路是自行补type-fest@^4devDependency(^5需要 ES2024,与本项目 ES2022 目标冲突)。听审阅意见取舍。范围
仅
frontend/src下第二阶段前端相关文件,未改动 backend 与契约实现(contracts/index.ts仅新增ThemePackageInspection.css字段与两个主题错误码)。实现第二阶段分工表中吉海燕负责的 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 通过。审阅结论:请求修改,暂不合并
审阅 PR #23,版本
f273fef,目标分支 main 为1c7b5b4。本轮发现两个已复现的问题,另有 8 个文件存在合并冲突。建议完成以下修复并解决冲突后,再进行合并验证。
[P2] 未启用主题的 CSS 仍会影响当前页面
位置:
frontend/src/services/themePackageService.ts:235–240安装主题时会立即注入 CSS,加载主题列表时又会注入所有已安装主题的 CSS。当前校验仅对缺少主题作用域给出警告,因此包内的
body、:root等规则会直接影响当前页面,即使用户尚未启用该主题。已复现:
light。body { color: rgb(1, 2, 3) !important; }的自定义主题。切回内置主题也不会移除这些规则,多个已安装主题之间可能互相覆盖。
建议只挂载当前主题的 CSS,并在切换时移除旧主题样式;或者强制隔离主题包中的所有规则,确保未启用主题不会影响页面。
建议补充回归测试:
[P2] 保存响应会错误清除请求期间的新编辑状态
位置:
frontend/src/features/plugins/PluginSettingsPanel.vue:56–62设置保存期间输入框仍可编辑,但请求成功后会无条件执行
hasChanges = false。这会把请求发出后产生的新编辑错误地标记为已保存。已复现:
first edit并点击保存。second edit。first edit。second edit,但保存按钮已禁用,未保存提示也已消失。此时用户无法直接保存最新输入,离开页面还可能丢失该修改。
建议使用编辑版本或提交快照判断是否仍有未保存内容;也可以在保存期间禁用编辑,确保显示状态与实际持久化结果一致。
建议补充回归测试:
合并冲突
当前 PR 与最新 main 存在以下 8 个文件冲突:
frontend/src/components/common/CommandPalette.vuefrontend/src/features/agent/AgentView.vuefrontend/src/features/chat/ChatView.vuefrontend/src/features/plugins/PluginMcpPanel.vuefrontend/src/features/plugins/PluginsView.vuefrontend/src/features/themes/ThemesView.vuefrontend/src/main.tsfrontend/src/stores/theme.ts解决冲突时需保留 main 已合入的中英文支持、聊天记录持久化及会话并发修复,随后对合并结果重新运行测试和构建。
验证记录
请修复上述问题、补充回归测试并解决合并冲突后再次请求审阅。
变更说明
整合最新 main,并修复 frontend phase2 前两轮审阅发现的问题。
主题管理
插件设置
与 main 整合
验证
限制
审阅结论:同意合并。
此前提出的主题 CSS 泄漏、保存状态竞态、密钥新输入丢失和社区主题预览无效问题均已修复,并补充回归测试。
本轮复审未发现新的阻塞问题。最新 main 的聊天持久化、会话并发修复、中英文支持和 Shiki 能力得到保留。
验证结果:233 项前端测试、类型检查及生产构建通过;浏览器已验证主题隔离、实际预览与保存行为。
生产插件后端端到端验收尚未覆盖,构建体积提示可作为后续优化项。
Pull request closed