# 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 文件 |