新编辑器架构设计.md 5.1 KB

块编辑器架构设计

1. 当前架构

AX 前端是 React + TypeScript + Vite 单页应用。应用采用左右双面板布局:左侧为聊天面板,右侧根据 UI 状态显示导出记录或文档编辑器。

main.tsx
  -> App
     -> ErrorBoundary
     -> ResizableLayout
        -> ChatPanel
        -> ExportRecordList / EditorPanel
                         -> EditorWithOutline
                            -> DocumentOutline
                            -> BlockEditor
                               -> MainToolbar
                               -> BlockCanvas
                                  -> BlockRenderer

ChatPanelEditorPanelExportRecordListSessionListReact.lazy 按需加载。会话抽屉关闭时不会挂载 SessionList

2. 组件职责

模块 主要职责
App.tsx 组装布局、切换右侧视图、管理会话抽屉和网络状态
ResizableLayout 左右面板布局、拖拽调整和宽度持久化
EditorPanel 编辑器面板外壳,保持 App 的集成接口
EditorWithOutline 组合编辑器与可折叠文档大纲
BlockEditor 加载文档、连接 store、显示工具栏和画布
BlockCanvas block_order 排序并渲染 blocks
BlockRenderer block.type 分发具体块组件
RichTextEditor 基于 contenteditable 的富文本编辑和选区工具栏
TableBlock 表格渲染、选择、结构操作和尺寸调整

3. Block 数据模型

核心类型位于 src/types/editor.ts,当前支持:

type BlockType = 'heading' | 'paragraph' | 'table' | 'image' | 'toc';

每个 block 至少包含 idblock_ordertypelevelindexword_stylestylemetadata。标题和段落的 content 可以是字符串或 RichText[];表格 content 包含行、单元格和可选列宽;图片 content 是图片 Data URL;目录块由标题数据生成。

4. 数据流

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 个块。

5. 表格结构算法

表格不能只用数组下标理解。getTableVisualCellPositions 会根据 rowspancolspan 和隐藏单元格计算视觉坐标。合并、拆分、插入和删除操作先在视觉网格中确定范围,再由重建函数重建行和单元格。

修改表格功能时应遵循:

  1. 先规范化旧数据,再计算视觉坐标。
  2. 用视觉范围验证选择区域是否连续。
  3. 重建后同步 metadata.colsmetadata.rows 和宽度数组。
  4. 用保存后的数据再次渲染,检查合并单元格没有重复插入。

表格样式面板和表格边框控制组件已经移除。当前保留的是结构工具栏、单元格文本格式和垂直对齐能力。

6. 导出和大依赖

  • Word:exportService 调用后端 /api/v1/export/doc,再下载导出记录。
  • Markdown:clientExportService 根据 blocks 在浏览器生成 Blob。
  • PDF:点击导出后动态加载 html2canvasjspdf
  • Word 内容解析:documentContentService 在需要时动态加载 mammoth

不要将这些可选依赖改成入口静态导入,否则会增加首屏 JavaScript 和 TBT。

7. 安全边界

richTextConverter 在生成 HTML 前会转义文本,并限制颜色和字体值格式。富文本解析使用临时 DOM,不应直接将未过滤的用户 HTML 传给 dangerouslySetInnerHTML

前端只读取 VITE_AI_PROXY_URLVITE_WORKFLOW_PROXY_URL,AI 与工作流上游密钥必须由后端代理保存和使用。任何 VITE_*_API_KEY 都会被打包到浏览器,不能配置为秘密。

8. 性能决策

  • 面板和会话列表懒加载。
  • 生产构建使用 Oxc 和 Lightning CSS 压缩,关闭 source map。
  • 分栏拖动使用 requestAnimationFrame,结束时才写入 localStorage
  • 图片预留宽高比例,并使用 lazy loading 和 async decoding。
  • 长聊天消息使用 content-visibility: auto
  • 可选导出库保持动态导入。

9. 变更原则

  • 优先修改拥有状态或数据变换职责的模块,不在渲染层复制保存逻辑。
  • 保持 EditorPanelProps 兼容,避免无关修改 App.tsx
  • 新增 block 类型时同步更新类型、渲染器、插入菜单、保存接口和导出逻辑。
  • 任何表格算法变更都应覆盖无合并、横向合并、纵向合并和混合合并场景。