AX 前端是 React + TypeScript + Vite 单页应用。应用采用左右双面板布局:左侧为聊天面板,右侧根据 UI 状态显示导出记录或文档编辑器。
main.tsx
-> App
-> ErrorBoundary
-> ResizableLayout
-> ChatPanel
-> ExportRecordList / EditorPanel
-> EditorWithOutline
-> DocumentOutline
-> BlockEditor
-> MainToolbar
-> BlockCanvas
-> BlockRenderer
ChatPanel、EditorPanel、ExportRecordList 和 SessionList 由 React.lazy 按需加载。会话抽屉关闭时不会挂载 SessionList。
| 模块 | 主要职责 |
|---|---|
App.tsx |
组装布局、切换右侧视图、管理会话抽屉和网络状态 |
ResizableLayout |
左右面板布局、拖拽调整和宽度持久化 |
EditorPanel |
编辑器面板外壳,保持 App 的集成接口 |
EditorWithOutline |
组合编辑器与可折叠文档大纲 |
BlockEditor |
加载文档、连接 store、显示工具栏和画布 |
BlockCanvas |
按 block_order 排序并渲染 blocks |
BlockRenderer |
按 block.type 分发具体块组件 |
RichTextEditor |
基于 contenteditable 的富文本编辑和选区工具栏 |
TableBlock |
表格渲染、选择、结构操作和尺寸调整 |
核心类型位于 src/types/editor.ts,当前支持:
type BlockType = 'heading' | 'paragraph' | 'table' | 'image' | 'toc';
每个 block 至少包含 id、block_order、type、level、index、word_style、style 和 metadata。标题和段落的 content 可以是字符串或 RichText[];表格 content 包含行、单元格和可选列宽;图片 content 是图片 Data URL;目录块由标题数据生成。
MessageItem / ExportRecordList
-> uiStore.openDocumentPreview
-> App 选择 EditorPanel
-> editorStore.loadDocument
-> blockService.getBlocks
-> blocks 按 block_order 排序
-> BlockCanvas 渲染
-> 用户修改
-> editorStore.updateBlock
-> dirtyBlocks / hasModified
-> 自动保存或 MainToolbar.saveDocument
-> blockService.updateBlock / createBlock / deleteBlock
editorStore 负责加载、脏块追踪、自动保存、并发限制、失败重试、哈希比较和请求取消。自动保存默认在停止编辑 3 秒后触发,最多同时保存 3 个块。
表格不能只用数组下标理解。getTableVisualCellPositions 会根据 rowspan、colspan 和隐藏单元格计算视觉坐标。合并、拆分、插入和删除操作先在视觉网格中确定范围,再由重建函数重建行和单元格。
修改表格功能时应遵循:
metadata.cols、metadata.rows 和宽度数组。表格样式面板和表格边框控制组件已经移除。当前保留的是结构工具栏、单元格文本格式和垂直对齐能力。
exportService 调用后端 /api/v1/export/doc,再下载导出记录。clientExportService 根据 blocks 在浏览器生成 Blob。html2canvas 和 jspdf。documentContentService 在需要时动态加载 mammoth。不要将这些可选依赖改成入口静态导入,否则会增加首屏 JavaScript 和 TBT。
richTextConverter 在生成 HTML 前会转义文本,并限制颜色和字体值格式。富文本解析使用临时 DOM,不应直接将未过滤的用户 HTML 传给 dangerouslySetInnerHTML。
前端不得读取 VITE_AI_API_KEY 或 VITE_WORKFLOW_API_KEY。Vite 的 VITE_* 变量会被打包到浏览器,不能视为秘密。AI 和工作流请求统一通过后端代理,目标工作流固定为 /api/v2/chat/completions,并由后端附带固定的 appId/projectId。
requestAnimationFrame,结束时才写入 localStorage。content-visibility: auto。EditorPanelProps 兼容,避免无关修改 App.tsx。