# 前端组件化梳理与规划

## 背景与现状

- `static/index.html` 将所有界面（配额通知、对话侧栏、文件/待办/子智能体面板、聊天区、输入区、聚焦文件、个性化抽屉、上下文菜单等）堆在一个根模板里，通过 `v-if`/`v-for` 直接绑定根实例的数据，缺乏可复用组件边界和样式隔离。`static/index.html:35-1094`
- `static/app.js` 中单个 `createApp` 承担了连接管理、消息流、文件树、资源监控、使用额度、工具控制、上传、个性化设置、彩蛋等所有状态/方法，数据段跨越 150+ 行，方法 200+ 个，总体约 4.4k 行，导致维护和测试困难。`static/app.js:186-4362`
- 现有代码依赖 Vue CDN，全局变量（`ICONS`、`TOOL_CATEGORY_ICON_MAP`、`window.requestSocketToken` 等）直接注入；没有构建步骤，也就无法按模块拆分或使用 TypeScript/ESM。

> ✅ Phase 1（脚手架 + 入口切换）已落地，过程总结见 `doc/frontend/phase1_setup.md`。
> ✅ Phase 2（对话侧栏 + 文件面板）已上线，详见 `doc/frontend/phase2_sidebar.md`。
> 以下规划作为 Phase 3+ 的拆分依据。

## 目标与约束

1. 拆分出语义明确的组件，使每个功能模块有独立的模板/逻辑/样式，降低互相影响。
2. 引入现代化构建链路（Vite + Vue 3 SFC），保留当前 API/UI 行为、Socket.IO 逻辑与安全脚本（`security.js`、彩蛋脚本）。
3. 建立状态分层，区分“跨组件共享状态”“本地 UI 状态”“请求数据”，以 Pinia/composable 管理。
4. 支持渐进式迁移：新旧模板可短期共存，确保每阶段都能运行并回归关键流程（登录→聊天→终端→上传→用量面板）。

## 构建与目录规划

1. **构建工具**：采用 Vite（支持 Vue 3、热更新、自动代码分割），后续易拓展 TypeScript/测试。
2. **目录建议**（均位于 `static/` 下）：
   - `src/main.ts`：入口，挂载 `App.vue`、注入 Pinia、注册全局指令/组件。
   - `src/App.vue`：根壳组件，只负责大布局与全局 overlay 容器。
   - `src/components/`：按功能域组织（如 `chat/`, `sidebar/`, `panels/`, `overlays/`, `common/`）。
   - `src/stores/`：Pinia store（`useConnectionStore`, `useConversationStore`, `useResourceStore`, `useFileStore`, `useUiStore`, `usePersonalizationStore`）。
   - `src/composables/`：WebSocket 管理、消息流解析、Toast/Confirm service、复制代码块等工具。
   - `src/assets/`：静态 SVG/样式；继续复用 `style.css`，逐步拆为 `src/styles/`。
   - `src/utils/request.ts`：封装带 CSRF 的 fetch。
3. **构建输出**：Vite 输出到 `static/dist/`，Flask 模板改为加载 `dist/assets/*.js|css`；保留 CDN 版本作为 fallback，直到迁移完成。

## 组件树（建议）

```
AppShell
├─ GlobalToasts（quota + toast 队列）
├─ ConfirmDialog
├─ EasterEggOverlay + ContextMenu + CopyCodeHandler
├─ ConversationSidebar
├─ WorkspaceLayout
│  ├─ LeftPanel
│  │  ├─ StatusHeader
│  │  ├─ PanelSwitcher
│  │  └─ PanelContent
│  │      ├─ FileTreePanel（含 FileNode）
│  │      ├─ TodoPanel
│  │      └─ SubAgentPanel
│  ├─ ChatArea
│  │  ├─ TokenDrawer
│  │  ├─ MessageList
│  │  │  ├─ MessageItem（User/Assistant/System）
│  │  │  └─ ToolAction / ThinkingBlock / MarkdownBlock
│  │  ├─ ScrollLockToggle
│  │  └─ InputComposer
│  │      ├─ QuickMenu
│  │      │  ├─ ToolToggleMenu
│  │      │  └─ SettingsMenu
│  │      └─ AttachmentButton / ModeSwitch
│  └─ RightFocusPanel
└─ PersonalizationDrawer
```

说明：
- ConversationSidebar 负责搜索/分页/对话操作，接收 `conversations`, `currentConversationId`、触发 `loadConversation/delete/duplicate`。`static/index.html:91-204`
- LeftPanel 根据 `panelMode` 动态渲染文件树（`fileTree`, `expandedFolders`）、待办（`todoList`）、子智能体（`subAgents`）。`static/index.html:217-368`
- ChatArea 内部组件拆分消息渲染、Token/资源卡片、输入框及快捷菜单。`static/index.html:374-820`
- RightFocusPanel 只观察 `focusedFiles`，提供 tabs 和 `downloadFile` 操作。`static/index.html:906-958`
- PersonalizationDrawer 独立组件管理表单、拖拽排序与保存逻辑。`static/index.html:959-1094`
- Toasts/Confirm/ContextMenu/EasterEgg 形成 `overlays/` 组件，避免根实例直接绑定。

## 状态域与 Store 设计

| Store/Composable | 职责 | 关键数据来源 |
| --- | --- | --- |
| `useConnectionStore` | 维护 Socket.IO 实例、连接状态、stop 请求、服务器状态快照；统一事件监听与销毁。| `initSocket` 逻辑 `static/app.js:711-1103` |
| `useConversationStore` | 对话列表、当前对话、消息流、工具状态、压缩/清空等命令；暴露 action（load/create/delete/duplicate/send/compress）。| `loadConversationsList`、`loadConversation`、`fetchAndDisplayHistory`、消息 handlers `static/app.js:1904-3336` |
| `useResourceStore` | Token 统计、容器状态、网络/存储、配额轮询；提供格式化工具。| `loadInitialData`、`updateContainerStatus`、`pollContainerStats`、`fetchUsageQuota` `static/app.js:1595-1870` |
| `useFileStore` | 文件树、聚焦文件、右键菜单、下载逻辑。| `updateFileTree`、`downloadFile/Folder` `static/app.js:615-707`、`static/index.html:906-958` |
| `useTaskStore` | 待办列表、子智能体轮询。| `fetchSubAgents`, `fetchTodoList` `static/app.js:2885-2938` |
| `useUploadStore` | 上传状态、toast 更新、复用安全扫描提示。| `uploadSelectedFile` `static/app.js:2940-3108` |
| `usePersonalizationStore` | 个人页表单、保存/启停逻辑。| `openPersonalPage`~`savePersonalization` `static/app.js:2496-2669` |
| `useUiStore` | 侧栏折叠、面板宽度、QuickMenu/ToolMenu/Settings、toast 队列、confirm/overlay。| `data` 中的 UI 字段 `static/app.js:224-360` + `pushToast/confirmAction` `static/app.js:4200-4334` |

Store 之间通过 action 通信，如 `conversationStore` 发出 `loadConversation` 后通知 `resourceStore` 更新 token；`uiStore` 暴露 toast/confirm 服务供其他 store/组件调用。

## 事件与数据流

1. **WebSocket**：`io('/')` 监听 quota/token/status/conversation/message/easter-egg 等事件（`static/app.js:769-1103`），将原本直接修改 `data` 的逻辑迁移到 store action（例如 `conversationStore.handleAiMessageStart`、`resourceStore.handleQuotaUpdate`）。
2. **REST API**：`loadInitialData` 涵盖 `/api/files`, `/api/focused`, `/api/status`, `/api/conversations/current`, `/api/usage` 等；应抽象为 `apiClient`，集中处理 CSRF/错误提示。
3. **定时轮询**：容器 0.5s、存储 5s、子智能体 5s、配额 10s（可在 store 内封装 start/stop，以路由或可见性驱动）。
4. **命令通道**：`socket.emit('send_command', {...})`、`run_command`、`stop_task` 等保持不变，只是从组件调用 store action。

## 迁移路线（建议）

1. **Phase 0 – 基础设施**  
   - 初始化 Vite + Vue 3 + Pinia + ESLint + Prettier。  
   - 配置构建输出与 Flask 静态路径，写入 README 使用说明。
2. **Phase 1 – 壳 + Overlay**  
   - 实现 `AppShell`、toast/confirm/context-menu/personalization overlay，保持旧版聊天区域引用旧模板，通过 `mount` 在 `#app` 下混用。  
   - 验证登录、对话加载、toast/confirm 是否正常。
3. **Phase 2 – 侧栏组件**  
   - 拆 ConversationSidebar、LeftPanel（含 FileTree/Todo/SubAgent）。  
   - 引入 `useConversationStore`、`useFileStore`、`useTaskStore`。  
   - 测试对话搜索/切换、文件树展开/右键、待办/子智能体展示。
4. **Phase 3 – ChatArea**  
   - 拆 TokenDrawer、MessageList、InputComposer、QuickMenu、ToolMenu。  
   - 落地 WebSocket/消息 store，复用现有渲染逻辑（Markdown、思考块、工具卡片）。  
   - 回归消息流、工具调用、停止按钮、上传/设置/模式切换。
5. **Phase 4 – RightPanel & Personalization**  
   - 拆聚焦文件与个人页组件，接入 `usePersonalizationStore`。  
   - 完成 CSS 抽取，移除旧版 `static/app.js`/`index.html`，保留必要 polyfill。

每个阶段都保持 `main.py`/`web_server.py` 无需改动，仅替换静态入口文件即可。

## 测试与验证

| 阶段 | 核心验证 | 自动化建议 |
| --- | --- | --- |
| Phase 0 | `npm run dev/build` 正常，Flask 引用 `dist`；加载页可看到“正在连接服务器”。 | 添加 `npm run lint`、`npm run typecheck`（若启用 TS）。 |
| Phase 1 | Toast/Confirm/上下文菜单、彩蛋覆盖层可交互；连接断开/重连提示正常。 | 编写 Vitest 针对 toast store 的单测。 |
| Phase 2 | 对话 CRUD、文件树右键下载、待办/子智能体轮询无误；侧栏拖拽宽度保持。 | Playwright 脚本：创建/切换/删除对话，校验 API 调用。 |
| Phase 3 | 消息流畅、工具卡片渲染、上传扫描提示、快捷菜单/设置工作。 | 录制 `socket` mock，用 Jest/Vitest 验证消息 reducer。 |
| Phase 4 | 聚焦文件显示、个人化表单保存；整体构建包大小可控。 | e2e：提交 personalization，再刷新确保状态加载。 |

## 风险与应对

1. **WebSocket 事件错杀**：拆分过程中如未正确解绑监听，会重复触发。→ 在 `useConnectionStore` 中集中注册/清理，配合 `onBeforeUnmount`。
2. **CSS 回归**：`.sidebar`, `.chat-container` 等现有样式都位于 `style.css`，迁移时注意命名冲突，可引入 `postcss-nesting` + CSS Modules，逐步拆分。
3. **Build/Runtime 混用**：迁移初期若需要旧版 `app.js` 支撑未拆组件，可通过 Vite 的 `define` 暴露全局函数，与 Flask 模板条件加载。
4. **依赖外链**：Prism/KaTeX/Marked、Socket.IO 目前从 CDN 引入。组件化后建议使用 npm 依赖，统一由 Vite 打包，避免版本漂移。

## 下一步

Phase 3（进行中）：聊天区实时化 + 输入/工具 + Token 面板

1. **连接与消息流（已完成）**：`useConnectionStore` + `useChatStore` + `ChatArea`，加载历史、实时流式输出、发送/停止。
2. **工具卡片与 Quick Menu**（当前待做）
   - 抽象 `useToolStore` 管理工具分类、状态、开关（迁移 `toolSettings`）。
   - 组件化 Quick Menu（上传、工具开关、设置），接入 Pinia store。
   - 渲染工具执行卡片（命令输出、文件操作等），替换 legacy DOM 结构。
3. **Token/Quota/资源面板**
   - 新建 `useResourceStore` 轮询 `/api/container-status`、`/api/project-storage`、`/api/usage`，替换 token drawer。
   - 组件化 `TokenDrawer`，展示 Token/Quota/CPU/内存/网络/存储卡片。
4. **Toast/Confirm/Context**
   - 搬迁 `pushToast/confirmAction/contextMenu`/EasterEgg overlay，形成 `useUiStore` + `GlobalToasts/ConfirmDialog` 组件。

完成 Phase 3 后，再进入 Phase 4：聚焦面板、个性化抽屉、样式统一等。
