# Codex 数据分析(前端新包)开发计划 > 目标:在 `ai-electron/frontend` 里新开一个**独立包**,基于已集成的 Codex 基座,做一个数据分析类的对话产品。 > 约束:**不复用** `src/ai/` 的聊天代码;新建代码文件**一律 `codex` 前缀**;业务/后端接口 `ai-server` 不动。 > 版本号用于跟踪进度:**每个版本一次提交 + 一次验收**,未验收不进下一版。 --- ## 一、现状盘点(已确认) ### 1.1 Codex 基座已就绪(主进程 `ai-electron/electron/`) | 层 | 文件 | 状态 | | --- | --- | --- | | Controller | `controller/codexCtl.ts` | 已有,ee-core 自动注册,统一返回 `{success, data}` | | Service | `service/codex/{codexRuntime,codexLocator,codexHome,eventLog,eventMapper,approvalBroker,mcpService,skillService,providerService,jsonRpcPeer}.ts` | 已有 | | 推送通道 | `codex/event`、`codex/status`、`codex/approval`、`codex/approval-closed`、`codex/diagnostic` | 已有 | | 渲染进程 IPC 封装 | `src/ai/api/codexApi.ts` | 已有(**非聊天代码,属基础设施**) | 已可直接调用的方法:`ping / status / start / stop / restart / listModels / probeProvider / applyProvider / clearProvider / mcpList|Save|Remove / skillList|Read|InstallFolder|InstallZip|Remove|SetEnabled|OpenFolder / threadStart / turnRun / turnInterrupt / threadUnsubscribe / approvalResolve / eventsRead / logsTail`。 `AgentEvent` 形状(前端唯一渲染输入): `{ id, threadId, turnId, kind: 'lifecycle'|'assistant'|'thought'|'tool'|'file'|'plan'|'error', title, message, timestamp, method?, itemId?, isDelta? }` 渲染规则:**同 `id` 覆盖,`isDelta=true` 追加**。 ### 1.2 缺口(本计划必须补的) - **G1 审批通道未封装**:主进程已推 `codex/approval` / `codex/approval-closed`,但 `codexApi.ts` 里只有 `onCodexEvent/onCodexStatus/onCodexDiagnostic`,**没有** `onCodexApproval`。→ 需补。 - **G2 没有"产物"能力**:`codexCtl` 里没有任何文件读写。产物=Codex 在 `cwd`(默认 `%data%/codex-data/workspace`)里生成的文件。要做**列表 / 预览 / 另存为下载**,必须新增主进程能力(沙箱限定在 workspace 内)。→ 需新增 `codexArtifactCtl`。 - **G3 没有线程列表**:只有 `eventsRead(threadId)`,没有 `threadList`。会话列表只能前端自持(localStorage 存 threadId + 标题 + 时间)。→ 前端方案,后端不加接口。 - **G4 渲染进程可直接用 node**:`config.default.ts` 里 `nodeIntegration: true` + `contextIsolation: false`,产物能力也可以走 IPC(推荐),不依赖 fs 直读。 ### 1.3 入口机制(已确认,纯前端可改) - 菜单**不走后端**,来自本地 `src/core/store/modules/menu.json`(`permission.ts` 直接 import)。 - 视图映射在 `src/core/router/helper/routeHelper.ts` 的 `explicitDynamicViewMap`(AI 三个页面已在此登记),**不走 glob**。 所以新增一个页面 = 改 2 个文件 + 建 1 个视图。 --- ## 二、新包结构与命名 包根:`ai-electron/frontend/src/codex/`(与 `src/ai/`、`src/graph/` 平级)。所有新建文件 **`codex` 前缀**。 ``` src/codex/ codexIndex.ts # 包出口(唯一不是 index.ts 的地方:文件名也要 codex 前缀) styles/codexTheme.less # 设计 token:--codex-*(--ai-* 那套不依赖,自建) types/codexTypes.ts # 消息模型 / 块模型 / 产物模型 api/codexApi.ts # IPC 封装(从 src/ai/api/codexApi.ts 搬家 + 补审批通道) api/codexArtifactApi.ts # 产物 IPC 封装 store/codexChatStore.ts # pinia:运行时状态 / 会话列表 / 消息流 / 产物 utils/codexEventReducer.ts # AgentEvent → 消息模型 utils/codexBlockParser.ts # 围栏协议解析 utils/codexEcharts.ts # echarts 按需注册 utils/codexArtifact.ts # 产物类型判定、大小/时间格式化 utils/codexReportExport.ts # 报告导出(md/html/png/xlsx) utils/codexReportTemplate.ts # 报告模板 components/codexSessionSidebar.vue components/codexChatToolbar.vue # 状态条:运行时/模型/工作目录 components/codexMessageList.vue components/codexMessageItem.vue components/codexComposer.vue components/codexProcessTimeline.vue components/codexToolCard.vue components/codexApprovalDialog.vue components/codexBlockRenderer.vue components/codexMarkdownBlock.vue components/codexCodeBlock.vue components/codexTableBlock.vue components/codexJsonBlock.vue components/codexChartBlock.vue components/codexGraphBlock.vue components/codexArtifactPanel.vue components/codexArtifactPreview.vue components/codexDiagnosticDrawer.vue views/codexAnalysis/codexAnalysis.vue # 页面入口 ``` 包外**仅新增 2 处登记**(不改现有逻辑): 1. `routeHelper.ts` → `explicitDynamicViewMap['/codexAnalysis/codexAnalysis']` 2. `core/store/modules/menu.json` → 新增菜单项(挂在 `10263` "AI 数据分析" 分组下,标题「Codex 分析」,`path`/`component`/`url` 均为 `/codexAnalysis/codexAnalysis`) --- ## 三、版本计划 ### v1.0.0 — 包骨架 + 入口打通 - **交付**:`src/codex/index.ts`、`styles/codexTheme.less`、`views/codexAnalysis/codexAnalysis.vue`(三栏骨架:会话栏 / 消息区 / 产物栏,内容为占位)、`routeHelper.ts` + `menu.json` 两处登记。 - **验收**:菜单出现「Codex 分析」并能进入页面;`pnpm build` 通过;`pnpm type:check` 本包文件 0 报错。 ### v1.1.0 — 会话与运行时门禁 - **交付**:`api/codexApi.ts`(搬家 + 补 `onCodexApproval` / `onCodexApprovalClosed`)、`store/codexChatStore.ts`、`components/codexSessionSidebar.vue`、`components/codexChatToolbar.vue`。 - **内容**:`ping/status` 门禁(未就绪 → 提示去「插件」页配模型);`threadStart` 新建会话;会话列表 localStorage 持久化(threadId / 标题取首条输入 / 时间);切换会话走 `eventsRead` 恢复现场。 - **验收**:能新建/切换/删除会话;刷新后会话仍在;非桌面端整页降级提示。 ### v1.2.0 — 流式对话闭环(核心) - **交付**:`utils/codexEventReducer.ts`、`components/codexMessageList.vue`、`codexMessageItem.vue`、`codexComposer.vue`。 - **内容**:`turnRun` 提交;订阅 `codex/event` + `codex/status` + `codex/diagnostic`;assistant 增量聚合、思考/工具/文件/计划/错误分列;停止 = `turnInterrupt`;贴底滚动 + 复制 + 重发;`cwd` 可由用户选目录。 - **验收**:发一句需求,能看到**逐字增量**回复 + 过程事件;能中断;`status` 变化实时反映到顶部。 ### v1.3.0 — 富内容块(Markdown / 代码 / 表格 / JSON) - **交付**:`utils/codexBlockParser.ts`、`components/codexBlockRenderer.vue`、`codexMarkdownBlock.vue`、`codexCodeBlock.vue`、`codexTableBlock.vue`、`codexJsonBlock.vue`。 - **围栏协议**(自定义,写进系统提示,不复用 `parseMessageContent.ts`): ```` ```echarts ``` 、 ```graph ``` 、 ```table ``` 、 ```sql ``` 、 ```json ``` 、 ```report ``` ```` - **验收**:markdown 表格/代码块(行号+复制)/JSON 折叠 正常;超长表格可翻页 + 导出 CSV。 ### v1.4.0 — 图表与关系图 - **交付**:`components/codexChartBlock.vue`、`codexGraphBlock.vue`、`utils/codexEcharts.ts`。 - **内容**:echarts 按需注册 + 自适应 + 全屏 + 导出 PNG;关系图节点/边/缩放/筛选 + 导出。 - **验收**:模型输出 ```echarts / ```graph 时能渲染、能全屏、能导出图片。 ### v1.5.0 — 过程可视化:工具与审批 - **交付**:`components/codexProcessTimeline.vue`、`codexToolCard.vue`、`codexApprovalDialog.vue`。 - **内容**:一次 turn 的 lifecycle/thought/tool/plan 折叠成时间线;命令与输出折叠面板、diff 高亮;消费 `codex/approval` → `approvalResolve`(accept / acceptForSession / decline / cancel,含 `answers` 表单)。 - **验收**:`approvalPolicy=on-request` 时能弹审批并把决策回传主进程。 ### v1.6.0 — 产物:列表 / 预览 / 下载 - **交付(主进程,唯一需要动 electron 的一版)**:`electron/controller/codexArtifactCtl.ts` + `electron/service/codex/codexArtifactService.ts`(**沙箱限定在会话 cwd 内**,拒绝 `..` 穿越)。 方法:`list / readText / readBase64 / saveAs / revealInFolder / open`。 - **交付(前端)**:`api/codexArtifactApi.ts`、`utils/codexArtifact.ts`、`components/codexArtifactPanel.vue`(目录树 + 大小/时间 + 新产物角标 + 刷新)、`codexArtifactPreview.vue`(md / html / 代码 / 图片 / pdf / 表格 六类预览)。 - **验收**:Codex 产出的文件出现在产物栏,可预览、可另存为。 ### v1.7.0 — 报告生成与导出 - **交付**:`utils/codexReportTemplate.ts`、`utils/codexReportExport.ts`。 - **内容**:导出 md / html / png(html2canvas)/ xlsx(xlsx)—— 复用仓库已有依赖,**实现自写**(不复用 `src/ai/utils/reportExport.ts`)。报告含:标题、时间、结论、图表快照、数据附表。 - **验收**:一键导出可用,图表与表格进报告。 ### v1.8.0 — 会话恢复与诊断 - **交付**:`components/codexDiagnosticDrawer.vue` + store 持久化完善。 - **内容**:消息流落盘(消息 + 过程事件)、`eventsRead` 恢复、`logsTail` 诊断抽屉、失败重试与错误码文案、runtime 未就绪禁用输入。 - **验收**:刷新页面后会话与过程可完整恢复。 ### v1.9.0 — 打磨与端到端联调 - **内容**:快捷键、空态引导 + 示例 prompt、窄屏适配、loading 骨架、错误边界。 - **验收**:完整走通「提问 → 过程 → 图表/关系图 → 产物 → 报告下载」;`pnpm type:check`(本包 0 报错)+ `pnpm build` 通过。 --- ## 四、v2.0.0 — 数据分析增强(计划 + 实施) > 定位:v1.x 解决「能用」,v2.0 解决「日常顺手」。全部在前端闭环,不动 `ai-server`。 > 版本号继续递进,每个子版本单独可验收。 ### 前置修正(v1.9.1)——先补 v1.x 的两处偏差 | # | 内容 | 交付 | 验收 | | --- | --- | --- | --- | | **v1.9.1** ✅ | ① 关系图导出成图片;② HTML 报告内嵌真实图表图片 | 新增 `utils/codexVisualRegistry.ts`(内容哈希 → 图片提供者的注册表);`codexGraphBlock.vue` 增加 `captureImage()` 与「导出图片」;`codexReportTemplate` 的 HTML 分支改为 `` 内嵌 data URL,取不到时回退折叠 JSON | 关系图能导出 PNG;导出的 HTML 报告里图表/关系图是图片,单文件可离线打开 | ### v2.0.0 子版本 #### v2.0.1 — 分析模板库 - **问题**:每次都要重新组织提问措辞,高频分析(概览 / TopN / 时序 / 资金流向)无法复用。 - **交付**:`utils/codexTemplateLibrary.ts`(预置模板 + 自定义模板 + localStorage CRUD)、`components/codexTemplatePanel.vue`(抽屉:分组列表、搜索、一键填入、保存当前输入为模板、删除自定义)。 - **预置模板**(贴合案件数据研判场景,8 条):数据概览、字段画像与空值排查、TopN 排行、时序趋势、资金流向链路、时空碰撞、异常值排查、结论报告成稿。 - **验收**:抽屉里选模板能填入输入框;自定义模板保存后重启仍在;预置模板不可删除。 #### v2.0.2 — 工作目录管理 - **问题**:目录只能选一次、忘了上次选的是哪儿;换数据就得手动翻文件夹。 - **交付**:store 增加 `recentCwds`(去重、上限 10、localStorage)、`components/codexWorkspacePicker.vue`(弹窗:最近目录列表 + 原生选择 + 一键「用该目录新建会话」+ 移除记录)。 - **验收**:选过的目录进入最近列表;能从列表直接新建会话并落到该目录;目录被删/不可用时给出明确提示。 #### v2.0.3 — 多会话并行 - **问题**:`submitting` 是全局单飞,A 会话跑着就没法去 B 会话提问,切走等于干等。 - **交付**:store 把 `submitting: boolean` 改为 `runningThreads: string[]`,新增 `isThreadRunning()`;`send()` 只拦「同一会话重复提交」;侧栏会话行显示运行中点;工具栏显示在跑会话数;输入区按当前会话判断可用性。 - **验收**:在 A 会话发问后切到 B 会话可继续发问;两个会话的事件互不串台;切回 A 能看到它自己的增量输出。 #### v2.0.4 — 会话内检索与定位 - **问题**:长会话翻历史靠滚动。 - **交付**:消息流顶部检索条(`codexMessageList` 内),关键词高亮 + 上/下跳转 + 命中计数。 - **验收**:输入关键词能逐个跳转到命中位置并高亮。 #### v2.0.5 — 案件工作空间联动 - **问题**:案件的数据文件都在**后端的案件工作空间**里;Codex 用客户端默认目录时看不到案件数据,每次都得手动指路,等于把「工作空间」这件事交给了用户记。 - **先摸清的事实**:后端 `PathConst.WORKSPACE = /QingJian/workspace`,案件工作空间即 `workspace/<案件ID>` (`CaseInfoService.create()` 建案时就已经 `FileUtil.mkdir` 建好,duckdb 也放在里面)。 但 `CaseInfo` 的 `db` / `db_path` 都标了 `@JsonIgnore`,**工作空间路径没有回传给前端**。 - **交付** - **后端(ai-server,纯增量,不改既有逻辑)**: ① `CaseInfo` 增加非持久化字段 `workspacePath`(`@TableField(exist = false)`,MyBatis-Plus 不落库); ② `CaseInfoService` 增加 `workspacePath(caseId)` 与 `fillWorkspacePath(caseInfo)`(与建案时同一套推导,顺带 `mkdir` 兜底); ③ 在 `listByOwner()` / `current()` 返回前置值,`CaseInfoController.open()` 回传前补一次(该接口原本又 `getById` 取了一次,正好在返回前补)。 - **前端**:`plat/api/case/caseApi.ts` 的 `CaseInfo` 增 `workspacePath?: string`; codex store 增 `caseWorkspace`,cwd 解析优先级改为 **会话自身 cwd → 案件工作空间 → 用户手选目录 → 客户端默认目录**; 工作目录卡片与目录弹窗标明当前来源(案件工作空间 / 手动目录 / 客户端默认)。 - **验收**:进入案件后新建会话,Codex 的 cwd 即该案件工作空间,产物栏直接列出案件数据文件;切换案件后新会话跟着切;没有案件时仍回落到手动目录 / 客户端默认目录。 - **风险与理由**:这是本项目里**唯一一次动 `ai-server`**(此前约定不动后端)。用户本轮明确要求「后端返回该案件的工作空间」,故只做「加一个非持久化字段 + 返回前置值」,不触碰数据源、权限、校验等任何既有逻辑。 ### v2.0.0 完成情况 | 版本 | 交付 | 状态 | | --- | --- | --- | | v1.9.1 | `utils/codexVisualRegistry.ts`(内容哈希 → 图片提供者);`codexGraphBlock` 的 `captureImage()` + 「导出图片」;`codexReportTemplate.visualBlockToHtml()` 内嵌 ``;`downloadCodexDataUrl()` 收敛三处重复的 base64→Blob 逻辑 | ✅ | | v2.0.1 | `utils/codexTemplateLibrary.ts`(8 条预置模板 + 自定义模板 localStorage CRUD)、`components/codexTemplatePanel.vue` | ✅ | | v2.0.2 | store 的 `recentCwds` / `rememberCwd` / `removeRecentCwd`、`components/codexWorkspacePicker.vue` | ✅ | | v2.0.3 | store 的 `runningThreads` / `isThreadRunning`、侧栏运行中呼吸点、工具栏运行中会话数 | ✅ | | v2.0.4 | `codexMessageList` 会话内检索(粘顶检索条、命中计数、上/下一条居中跳转、`utils/codexHighlight.ts` 文本级关键词高亮)、`codexMessageItem` 命中强调条 | ✅ | | v2.0.5 | **后端**:`CaseInfo.workspacePath`(`@TableField(exist = false)`)、`CaseInfoService.workspacePath(caseId)` / `fillWorkspacePath(caseInfo)`(在 `listByOwner` / `current` / `CaseInfoController.open` 返回前置值);**前端**:`CaseInfo.workspacePath`、store 的 `caseId` / `caseWorkspace` / `cwdOverrides` / `syncCaseContext()` / `activeCwdSource`,cwd 优先级改为「会话 → 案件手动覆盖 → 案件工作空间 → 客户端默认」,视图与目录弹窗标明来源 | ✅ | ### v2.0.0 验证结果(同前一轮口径) | 项 | 结果 | | --- | --- | | 前端 `pnpm type:check` | 89 条 = 仓库既有基线,**本包 0 报错** | | 前端 `pnpm build` | exit=0 | | **后端 `mvn -o -DskipTests compile`** | exit=0;已用 `javap` 核验 `CaseInfo.workspacePath` 与 `CaseInfoService.workspacePath/fillWorkspacePath` 真的进了 class | | 主进程 | 本轮未改 electron 代码,沿用上一轮 `tsc --noEmit` / 91 单测 / `build-electron` 全绿 | ### v2.0.0 实现要点(容易踩的地方) - **关系图导出图片**用 html2canvas 整块栅格化:节点是 HTML、连线是 SVG,手撸 SVG 只能拿到半张图。 导出前 `zoomToFit()` 保证整图进框;报告内嵌用的缓存图用 `scale=1` 且**不**改用户当前视图。 - **图片提供者必须同步返回**(注册表是同步取值),所以关系图走「延迟 600ms 后台截图 → 缓存 → 提供者读缓存」, 缓存没就绪时报告自动回退成折叠 JSON,不会出现空白图。 - **多会话并行**把 `submitting: boolean` 换成 `runningThreads: string[]`: 只拦「同一会话重复提交」,不同会话各跑各的(事件本来就按 threadId 分流)。 输入区因此不再因别的会话在跑而禁用,只有当前会话在跑时按钮变「停止」。 - **不用 `window.prompt`**:Electron 渲染进程不支持,模板命名改成抽屉里的行内表单。 - **关键词高亮不改已渲染的 DOM**:手工替换文本节点会破坏 Vue 缓存的 vnode 引用, 下一次 patch 会把新文本写进已脱离文档的旧节点,表现为「内容卡住不更新」。 所以 markdown(v-html)走**字符串替换**(标签逐段跳过,天然不碰标签与属性), 提问气泡与代码块走**插值切段**加 class,两条路都不碰渲染后的 DOM。 ### v2.0.0 交付物清单(供核对) 新增文件(前端 `src/codex/`,共 4 个,本轮第 4 个): `utils/codexVisualRegistry.ts`、`utils/codexTemplateLibrary.ts`、`components/codexTemplatePanel.vue`、 `components/codexWorkspacePicker.vue`、`utils/codexHighlight.ts`。 改动文件:`codexGraphBlock`(导出图片 + 图片提供者)、`codexChartBlock`(登记图片提供者)、 `codexReportTemplate`(内嵌图片)、`codexReportExport`、`codexFormat`(`downloadCodexDataUrl`)、 `codexChatStore`(`recentCwds` / `runningThreads` / `caseId` / `caseWorkspace` / `cwdOverrides`)、 `codexComposer`(模板与目录入口、草稿上抛)、`codexChatToolbar`(检索入口、运行中会话数)、 `codexSessionSidebar`(运行中呼吸点)、`codexMessageList`(检索条)、`codexMessageItem`(命中态与关键词)、 `codexBlockRenderer` / `codexMarkdownBlock` / `codexCodeBlock`(关键词透传)、`codexAnalysis.vue`(装配)。 ### 不在 v2.0.0 范围(需先确认前提) - **`cwd` 直连「当前案件数据目录」**:需要先确认案件数据在本机是否有确定的落地目录(目前数据在 `ai-server` 侧,桌面端只有 `codex-data` / `codex-home`)。前提不明就不做,避免写死一个不存在的路径。 - **SQL 结果直连预览**:Codex 自己执行命令取数,客户端代执行 SQL 没有意义,跳过。 --- ### 待定 v2.1.0+(已展开,见文末「八、v2.1.0 — 可用性补强与协作(可执行计划)」) 分析结论沉淀成可对比的快照、模板导出/导入(团队共享)、图表联动筛选、产物变更 diff; 另补一项实现缺口(运行参数被硬编码)与两项评估后不做的说明,详见第八节。 --- ## 五、决策点 **已拍板**(2026-09-24 本次确认): | # | 决策 | 结论 | | --- | --- | --- | | **D1** | 视图文件名 | ✅ **严格全部 `codex` 前缀** → `views/codexAnalysis/codexAnalysis.vue`,菜单 `component` 写 `/codexAnalysis/codexAnalysis` | | **D4** | 产物能力放哪 | ✅ **新建 `electron/controller/codexArtifactCtl.ts`**(允许新增主进程代码,沙箱限定在会话 cwd 内) | | **D8** | 执行节奏 | ✅ **逐版本做完停下 review**,未验收不进下一版 | | **D3** | 会话列表来源 | ✅ 前端自持(localStorage),本期不动后端接口 | **待定**(不阻塞 v1.0.0,到对应版本前再确认): | # | 决策 | 选项 | 我的建议 | | --- | --- | --- | --- | | **D2** | IPC 封装放哪 | A) 新包自持 + 老文件一行转发
B) 直接用 `src/ai/api/codexApi.ts` | ✅ 已按 **A** 落地:`src/codex/api/codexApi.ts` 为唯一实现,`src/ai/api/codexApi.ts` 改为 `export * from '@/codex/api/codexApi'` | | **D5** | 报告导出实现 | A) 自写 `codexReportExport.ts`
B) 复用 `src/ai/utils/reportExport.ts` | ✅ 已按 **A** 落地:只复用 `xlsx` / `html2canvas` / `downloadByData` 这些底层依赖 | | **D6** | 富内容块协议 | `echarts/graph/table/json/report` 围栏;要不要 mermaid | ✅ 本期**不引入 mermaid** | | **D7** | 菜单挂哪 | A) 挂 `10263` 分组
B) 新起一级分组 | ✅ 已按 **A** 落地 | --- ## 六、边界与验证 - **不改**:`ai-server` 后端、`src/ai/**` 的聊天代码(仅 D2 里 `codexApi.ts` 可能变成转发,行为不变)。 - **验证**:每版 `pnpm build`(cwd `ai-electron/frontend`)+ `pnpm type:check`。 注意:`type:check` 仓库本身有约 89 条既有报错(集中在 `call/trans/graph/otg/person`),判断标准是**本包文件 0 报错**,不看退出码。 - **清理**:不留临时脚本、临时日志。 --- ## 七、完成情况(v1.0.0 → v1.9.0 全部落地) ### 前端新包 `src/codex/`(31 个文件,全部 `codex` 前缀) | 层 | 文件 | | --- | --- | | 入口 / 样式 | `codexIndex.ts`、`styles/codexTheme.less` | | 类型 | `types/codexTypes.ts`、`types/codexModuleShims.d.ts`(markdown-it 无类型声明的最小补丁) | | API | `api/codexApi.ts`(+ 审批通道)、`api/codexArtifactApi.ts` | | 状态 | `store/codexChatStore.ts` | | 工具 | `codexEventReducer`、`codexBlockParser`、`codexMarkdown`、`codexEcharts`、`codexFullscreen`、`codexArtifact`、`codexReportTemplate`、`codexReportExport`、`codexFormat` | | 组件 | `codexChatToolbar`、`codexSessionSidebar`、`codexMessageList`、`codexMessageItem`、`codexComposer`、`codexProcessTimeline`、`codexToolCard`、`codexApprovalDialog`、`codexBlockRenderer`、`codexMarkdownBlock`、`codexCodeBlock`、`codexTableBlock`、`codexJsonBlock`、`codexChartBlock`、`codexGraphBlock`、`codexArtifactPanel`、`codexArtifactPreview`、`codexDiagnosticDrawer` | | 视图 | `views/codexAnalysis/codexAnalysis.vue` | ### 主进程新增 - `electron/service/codex/codexArtifactService.ts` —— 产物读写,双重边界:**root 白名单**(落盘 `codex-data/artifact-roots.json`,重启后仍可读)+ **路径穿越校验**;只读不写、不提供删除。 - `electron/controller/codexArtifactCtl.ts` —— 7 个方法:`list / readText / readBinary / saveAs / reveal / open / openRoot`。 - `codexCtl.ts` 三处增量(不改变既有行为):`threadStart` 返回 `cwd` 并登记产物 root、`turnRun` 登记可选 cwd、新增 `defaultWorkspace`。 ### 行为要点 - **会话**:`threadStart` 拿 threadId + 真实 cwd,列表存 localStorage,切换走 `eventsRead` 回读;删除只从本地列表移除(不删 Codex 落盘历史)。 - **提问落盘**:Codex 的事件流里只有 agent 侧输出,`eventsRead` 回读不到我们发出去的文本 —— 所以提问另存 `codex.prompts.v1`,否则刷新后会「只剩回复、看不到问过什么」。 - **事件**:`codex/event` 按 threadId 缓存,同 id 覆盖、`isDelta` 追加;`turn/completed` 决定回合结局(done / error / interrupted)。 - **审批**:`codex/approval` → 弹窗 → `approvalResolve`(accept / acceptForSession / decline / cancel),`input` 类支持 questions 表单;遮罩点击等同拒绝(避免静默挂到 2 分钟超时)。 - **围栏协议**:```` ```echarts ```graph ```table ```json ```report ``` ````,解析失败一律回退成代码块展示原文。 - **产物**:回合结束后对比上一轮快照,新出现的文件打「新」角标;手动刷新即清除。 - **导出**:Markdown / HTML(自包含单文件)/ Excel(表格合集)/ PNG(html2canvas 截图真实 DOM,图表关系图一起进图)。 ### 与计划的偏差(如实记录) | 计划原文 | 实际实现 | 状态 | | --- | --- | --- | | v1.4.0「关系图…+ 导出」/ 验收「能导出图片」 | 初版只给了**导出数据(JSON)** | ✅ **v1.9.1 已补齐**:按钮「导出图片」,html2canvas 栅格化整块图谱(节点是 HTML、连线是 SVG,只有整块 DOM 截图能一次拿全),导出前自动适配画布保证整图进框 | | v1.7.0「报告含…图表快照」 | 初版 HTML 报告里图表是折叠的 ECharts option | ✅ **v1.9.1 已补齐**:块组件按内容哈希登记图片提供者(`utils/codexVisualRegistry.ts`),HTML 报告内嵌真实 ``,单文件离线可看;取不到图时回退折叠 JSON | | v1.8.0「消息流落盘(消息 + 过程事件)」 | 过程事件走主进程 `eventsRead` 回读(本来就有落盘),本地只落提问 | 等价或更好,但与原文描述不同 | | v1.5.0「diff 高亮」 | 文件变更的 patch 用代码块展示,**未做行级 diff 染色** | 未做(可读性略低) | | v1.9.0「端到端联调」 | 只做了编译/类型/单测三重验证,**未在真机 GUI 里点一遍全链路** | 待你实际跑一次确认交互 | > Markdown 报告仍保留原始围栏块(`` 的 data URL 会让 .md 变成几十 MB,不适合再加工场景), > 需要图片就走 HTML / PNG 导出。 ### 验证结果 | 项 | 命令 | 结果 | | --- | --- | --- | | 前端类型 | `pnpm type:check` | 89 条(= 仓库既有基线),**codex 包 0 报错** | | 前端构建 | `pnpm build` | exit=0,产出 `codexAnalysis-*.{js,css}` | | 主进程类型 | `npx tsc --noEmit`(cwd `ai-electron`) | exit=0,**0 报错** | | 主进程单测 | `npm test` | 6 文件 / **91 用例全通过** | | 主进程打包 | `npm run build-electron` | exit=0,`codexArtifactCtl` 已进 bundle | ### 环境提示(worktree 首次使用) - 本仓库 `ai-electron` 是 **npm 项目**(只有 `package-lock.json`,node_modules 无 `.pnpm`),**不能用 pnpm 装**;且 `package-lock.json` 被 gitignore,worktree 里需要从主仓库拷。 - `frontend` 是 pnpm 项目,首次安装要用 `pnpm install --ignore-scripts`(esbuild postinstall 会因 EBUSY 失败并导致 `.bin` 不生成)。 --- ## 八、v2.1.0 — 可用性补强与协作(可执行计划) > 定位:v2.0 把「进案件 → 工作空间 → 取数出图」跑通了,v2.1 磨掉日常使用里真正硌手的地方。 > 执行原则沿用:前端闭环优先、不动 `ai-server`、每子版本一次验收(`pnpm type:check` + `pnpm build`)、做完停下 review。 ### 8.1 版本总览与执行顺序 | 顺序 | 版本 | 主题 | 依赖 | 改动面 | | --- | --- | --- | --- | --- | | 1 | **v2.1.1** | 运行参数显式化(**补 v2.0 实现缺口**) | 无 | store + composer + 类型,约 5 个文件 | | 2 | **v2.1.5** | 产物增强:搜索 / 类型筛选 / 本轮变更 | 产物服务(已有) | 产物栏 + 事件归并,约 4 个文件 | | 3 | **v2.1.2** | 块级「钉到看板」 | 块注册表 `codexVisualRegistry`(已有) | 新看板组件 + 3 个块加按钮,约 6 个文件 | | 4 | **v2.1.3** | 结论快照与对比 | `parseCodexBlocks`(已有) | 新快照 + diff 模块 + 抽屉,约 4 个文件 | | 5 | **v2.1.4** | 模板 / 会话列表 导入导出 | 模板库(已有) | 模板抽屉 + 会话栏,约 3 个文件 | 排序理由:先补「运行参数被硬编码」这个**实现缺口**(它关系到安全语义,最该先修),再做低风险、无新 UI 的产物增强, 最后做三个带新界面的(看板 / 快照 / 导入导出)。 --- ### 8.2 v2.1.1 — 运行参数显式化 **问题**:v2.0 里 `threadStart` / `turnRun` 的 `sandbox` 与 `approvalPolicy` 是**我硬编码**的 (`workspace-write` + `on-request`)。用户想做「只读分析」(更安全)或「无人值守」时无从下手。 这不是新需求,是实现缺口。 **交付物** - 类型:`CodexSessionMeta` 增 `sandbox: CodexSandbox`、`approvalPolicy: CodexApprovalPolicy`(默认沿用现值,兼容已有 localStorage 数据)。 - store:`setSessionRunOptions(threadId, { sandbox, approvalPolicy })`;`send()` 与 `createSession()` 从会话取参下发(不再写死)。 - UI:`components/codexRunOptionsPanel.vue` —— composer 的 popover(`a-popover` + 全局类名,避开 teleport 作用域问题), 两个下拉:沙箱模式、审批策略,各带一句后果说明。 - 工具栏:非默认参数时显示徽标(`只读` / `全权` / `免审批`),点击可回到该面板。 **验收** - 切到 `read-only` 后让 Codex 写文件,应被拒绝(Codex 侧报错,前端如实显示)。 - 切到 `never`(免审批)后不弹审批弹窗。 - 重启客户端后参数仍在;徽标与实际参数一致。 **决策点**:D1 —— 参数按会话记还是全局默认?→ 建议**按会话记 + 一份全局默认**(新会话继承全局默认)。 **风险**:`danger-full-access` 是真危险项。 → 对策:选它或选 `never` 时 `Modal.confirm` 二次确认(文案写清后果),且**不允许**作为全局默认被静默继承。 --- ### 8.3 v2.1.5 — 产物增强(搜索 / 筛选 / 本轮变更) **问题**:产物栏只能一层层翻目录;Codex 改了哪些文件、这轮新增了什么,看不出来(v2.0 只给新文件打了「新」角标)。 **交付物** - `utils/codexArtifactChange.ts`:把 `kind === 'file'` 的事件按路径聚合成「本轮回合变更清单」。 路径来源:`item/fileChange/patchUpdated` 的 patch 里形如 `*** Update File: ` / `*** Add File:` 的行; `item/completed`(`fileChange` 类型)里 `changes` 的路径字段作为补充。 - 产物栏:搜索框(按文件名过滤)、类型筛选(全部 / 表格 / 图片 / 报告 / 代码 / 其他)、 「本轮变更」分区(列出改动文件 + 点击展开 patch,patch 用 `codexCodeBlock` 渲染)。 - store:`artifactChanges` 在回合结束时落定,手动刷新即清空。 **验收** - 让 Codex 改一个已有文件:产物栏「本轮变更」能列出它,点开能看到 patch。 - 搜索框输入即过滤;类型筛选切换正确。 **风险**:patch 的路径提取依赖 Codex 的输出格式。 → 对策:**解析失败一律静默降级** —— 只显示原始 patch,不猜路径、不误报、不抛错。 **决策点**:D2 —— patch 里可能带真实数据片段,是否默认折叠?→ 建议**默认折叠**,点开才展开。 --- ### 8.4 v2.1.2 — 块级「钉到看板」 **问题**:一次分析常产出多个图表与表格,要滚回去对照很累;右侧产物栏又被文件占着。 **交付物** - 表格 / 图表 / 关系图三个块头加「钉住 / 取消钉住」。 - `components/codexBoardPanel.vue`:产物栏**上方**新增可折叠「看板」分区,把钉住的块按原样渲染在一起, 支持上下排序、单个取消、一键清空。 - 持久化:只存「内容哈希 + 类型 + 标题」(复用 `codexTextKey`),切会话 / 刷新后按哈希从当前会话内容里找回块; 内容已变(哈希对不上)时把该钉住项标为**失效**并允许移除 —— 不假装还在。 **验收** - 钉 3 个块 → 切到别的会话再切回来,看板仍在。 - 表格与图表在看板里都能正常渲染(图表自适应宽度、可导出 PNG)。 - 取消钉住即从看板移除。 **风险**:看板里重复渲染 ECharts 会翻倍开销。 → 对策:看板**折叠时用 `v-if` 不渲染**、最多钉 6 个、超出时提示先取消。 **决策点**:D3 —— 看板放右侧产物栏上方,还是底部抽屉?→ 建议**产物栏上方**(与「分析的产物」同侧;窄屏本就收起产物栏,行为一致)。 --- ### 8.5 v2.1.3 — 结论快照与对比 **问题**:同一个问题隔几天再问一次,两次结论的差异只能靠人眼在长对话里比对。 **交付物** - `utils/codexSnapshot.ts`:把会话的(提问 + 回复正文 + 表格数据)存成本地快照,键 `codex.snapshots.v1`; **只存文本与表格、不存图片**,单条上限 200 KB(超出截断并标注),可命名、可删除。 - `utils/codexTextDiff.ts`:最简行级 LCS diff,输出 `{type: 'same'|'add'|'del', text}` 序列。 - `components/codexSnapshotPanel.vue`(抽屉):存快照、快照列表、勾两个 → 并排 diff 视图。 - 快照可导出 Markdown(复用 `codexReportExport`)。 **验收** - 存两次快照 → 并排看到新增/删除行被高亮。 - 删除快照;重启客户端后快照仍在。 **风险**:`localStorage` 配额(快照文本可能较大)。 → 对策:单条 200 KB 上限 + 最多 20 条 + 超限时提示先删旧快照;写入失败静默降级并提示。 **决策点**:D4 —— diff 配色。建议**新增绿 / 删除红**(这是通用 diff 语义,与本项目「股价涨红跌绿」属不同语境,不冲突)。 若你希望和项目色系保持一致,也可以改成「新增=主色、删除=警告色」,请在验收前定。 --- ### 8.6 v2.1.4 — 模板 / 会话列表 导入导出 **问题**:模板与会话列表都只在本机 `localStorage`;换机器、重装客户端就没了,也没法给同事。 **交付物** - 模板抽屉加「导出 / 导入」:导出 `{ version, exportedAt, templates[] }` JSON; 导入时**先把同名冲突列出来**让用户确认覆盖还是跳过。 - 会话栏加「导出会话列表 / 导入」:只导出元数据(threadId / 标题 / cwd / 模型 / 时间 / 回合数), **不含消息正文**(正文在主进程事件日志里,导出没意义)。 - 导入按 `threadId` 去重,已存在则跳过并给出统计(导入 N 条 / 跳过 M 条)。 **验收** - 导出 → 清空(或换目录)→ 导入,模板与会话列表恢复。 - 重复导入不产生重复项;冲突处理和提示一致。 **风险**:导入的会话 `threadId` 在本机 Codex 侧并不存在 → 点开必然读不到事件。 → 对策:导入的会话打「来自外部」标记,打开时明确提示「本机无该会话记录」,**不装作能恢复**(与 v2.0「删除只从本地列表移除」同一套诚实原则)。 **决策点**:D5 —— 是否顺手把会话元数据下沉到主进程文件(`codex-data/sessions.json`),彻底摆脱 localStorage? → 建议**本版不做**(导入导出已覆盖跨机场景,下沉单独排 v2.2.0,避免这次改动面过大)。 --- ### 8.7 评估后不做(附理由,避免以后重复讨论) | 登记项 | 结论 | | --- | --- | | **图表联动筛选** | 做不出**可靠**交互:图表与表格没有统一数据模型与字段映射,只能靠「图表 x 轴类目值 ∩ 表格某列值」这种启发式猜关联,猜错比不做更糟。**替代**:v2.1.2「钉到看板」解决「对照」这个真实诉求 | | **SQL 结果直连预览** | Codex 自己执行命令取数,客户端代执行 SQL 没有意义(v2.0 已判定,维持) | | **消息级删除 / 编辑历史** | Codex 侧不支持删回合,前端删掉就是撒谎(v2.0 已判定,维持) | ### 8.8 决策点汇总(验收前需你确认) | # | 决策 | 我的建议 | | --- | --- | --- | | D1 | 运行参数按会话记还是全局默认 | 按会话记 + 一份全局默认(新会话继承) | | D2 | 产物 patch 是否默认折叠 | 默认折叠(可能含真实数据片段) | | D3 | 看板位置:产物栏上方 / 底部抽屉 | 产物栏上方 | | D4 | diff 配色:通用(新增绿/删除红)还是项目色系 | 通用 diff 语义(与股价红涨绿跌不同语境) | | D5 | 本版是否下沉会话元数据到主进程 | 不在本版,排 v2.2.0 | ### 8.9 v2.2.0 候选(先登记,不展开) - 会话元数据下沉主进程(`codex-data/sessions.json`),彻底摆脱 localStorage - 产物打包导出 zip(需先评估是否引入 zip 依赖;现有 `yauzl` 只解压) - 多会话标签页(现在靠侧栏切换) - 主进程 `threadList` 对接(前提是 Codex 后续提供该接口;目前只有 `eventsRead`) - 报告模板自定义(标题页、页眉页脚、单位信息) --- ## 九、v2.1.0 完成情况 ### 9.1 交付清单 | 版本 | 新增文件 | 改动文件 | | --- | --- | --- | | v2.1.1 运行参数显式化 | `components/codexRunOptionsPanel.vue` | `types/codexTypes.ts`(`CodexSandbox`/`CodexApprovalPolicy`/`CodexRunOptions` 归位)、`api/codexApi.ts`(类型转出)、`store`(`runDefaults` / `setRunDefaults` / `setSessionRunOptions` / `activeRunOptions`,会话字段归一化)、`codexComposer`(挂载面板)、`codexChatToolbar`(参数徽标) | | v2.1.5 产物增强 | — | `utils/codexArtifactChange.ts`(新)、`codexArtifactPanel`(容器重构 + 搜索 + 类型筛选 + 本轮变更) | | v2.1.2 钉到看板 | `components/codexBoardPanel.vue` | `store`(`board` / `toggleCodexBoardItem` / `isOnCodexBoard` / `removeCodexBoardItem` / `moveCodexBoardItem` / `clearCodexBoard` / `boardBlocks` / `boardFull`)、三个可视化块(钉住按钮)、`codexAnalysis.vue`(aside 容器) | | v2.1.3 结论快照 | `utils/codexTextDiff.ts`、`utils/codexSnapshot.ts`、`components/codexSnapshotPanel.vue` | `codexChatToolbar`(快照入口)、`codexAnalysis.vue` | | v2.1.4 导入导出 | — | `codexTemplatePanel`(导出 / 导入 + 冲突确认)、`codexSessionSidebar`(导出 / 导入 + `external` 标记 + 打开时如实提示)、`store`(`exportSessionsJson` / `importSessionsJson`) | ### 9.2 决策落地(按 8.8 的建议执行) - D1 运行参数**按会话记 + 一份全局默认**:会话存 `sandbox` / `approvalPolicy`,新会话继承 `runDefaults`,「设为新会话默认」显式落盘。 - D2 产物 patch **默认折叠**,点开才渲染。 - D3 看板放在**产物栏上方**,折叠时用 `v-if` 整块不渲染。 - D4 diff 用**通用语义**:新增绿 `+`、删除红 `−`(与股价红涨绿跌不同语境)。 - D5 会话元数据**不在本版下沉**,排 v2.2.0。 ### 9.3 验证结果 | 项 | 命令 | 结果 | | --- | --- | --- | | 前端类型 | `pnpm type:check` | 89 条 = 仓库既有基线,**本包 0 报错** | | 前端构建 | `pnpm build` | exit=0,产出 `codexAnalysis-*.{js,css}` | | 主进程 / 后端 | 本轮未改动 | 沿用上一轮 `tsc --noEmit` / 91 单测 / `build-electron` / `mvn compile` 全绿 | ### 9.4 本轮顺带发现(**既有缺陷,非本次引入**) `vite-plugin-theme-vite3` 生成的暗色主题文件里,**自定义属性的 `--` 前缀被抹掉了**: ``` dist/assets/app-antd-dark-theme-style.e3b0c442.css grep -F -- "--codex-bg" → 0 处 grep -F -- "ai-text" → 有(且同样没有 --) ``` 即暗色模式下 `--ai-*`、`--codex-*`、`--jeesite-*` 这些自定义属性都不会被覆盖, 页面在暗色主题下仍然套用浅色 `:root` 的值(antd 类名那部分是正常的)。 - **影响**:暗色主题下自定义 token 不生效 —— 对既有 `--ai-*` 模块与新的 `--codex-*` 模块**同等影响**。 - **是否本次引入**:不是。`--ai-*` 在 v2.1 之前就是这个状态,本次只是把 codex 模块也纳入了同一套机制。 - **修与不修**:修它要动构建期的主题插件(`build/theme/*` + `vite-plugin-theme-vite3` 的产物处理), 属于构建管线改造,**风险与收益不匹配**,因此本次只记录、不动手;需要时单独立项。 ### 9.5 变更记录 | 时间 | 变更 | | --- | --- | | 2026-09-24 晚 | **工作目录改为纯代码决定、界面不可改**:删除 `codexWorkspacePicker` 与 store 的 `cwdOverrides` / `recentCwds` / `setWorkspaceDir` 等,cwd 优先级简化为「会话 cwd → 案件工作空间 → 客户端默认」,界面只读展示;同时修复产物栏「该目录不是本客户端的会话工作目录,已拒绝访问」(`requireAllowedRoot` 对绝对路径且真实存在的目录自动补登记,解决「还没有会话就列产物」的时序问题) | | 2026-09-24 晚 | **移除「检索」与「快照」两个功能**(用户要求):删除 `codexMessageList` 的检索条、`codexMessageItem` 的命中态、`codexMarkdownBlock` / `codexCodeBlock` 的关键词高亮透传、工具栏两个按钮,以及 `utils/codexHighlight.ts`、`utils/codexSnapshot.ts`、`utils/codexTextDiff.ts`、`components/codexSnapshotPanel.vue` 四个文件。v2.0.4 的「会话内检索」与 v2.1.3 的「结论快照」就此下线;保留产物栏自己的文件名筛选、模板库搜索、关系图节点搜索(这些是各自的过滤功能,与被移除的会话检索无关)。包体积 45 → 41 文件 |