# 译文编辑器重构实施方案

## 方案进度概览

- 当前阶段：方案已修订完成，待进入阶段 1 实施
- 当前执行规则：每完成一步，经用户验收确认后，先更新本文档中的进度概览与阶段状态，再开始下一步
- 已确认内容：
  - 右侧译文编辑器内容来自接口返回，数据结构与 `merged_ans.json` 一致，后续仅新增专用译文字段，不破坏原有 node 结构
  - 对齐粒度为 `node 对 node`
  - 一页只对应一个译文容器，页内允许弱化块级对齐，但页级联动必须准确
  - 样式切换不仅修改展示，还必须同步修改语义字段
  - 导航树必须根据最新语义结果严格按 `heading_level` 层级嵌套重建
  - 左右联动必须同时支持：同步滚动、点击定位、页码跟随
  - 对照实现方式不限定为某一种技术，只要能稳定达成左右对照效果即可
  - 右侧编辑器基于 TipTap
  - 当前版本只保留：修改文本内容、切换正文/一级标题/二级标题
  - 当前版本必须隐藏或移除：段级新增、段级删除
  - 最终仅导出译文为 Word 文档
- 已补充的实施约束：
  - 前端必须保留结构化 node 模型，不能让 TipTap 直接吞并整篇文档结构
  - 编辑态必须保留 `node_id`、`page_idx`、`reading_order`、`semantic_type`、`heading_level`
  - 译文字段在数据适配层统一映射，组件内部不得散落多套字段判断
  - 左右联动必须加事件来源标记与防循环触发控制
  - 目录树重建必须包含层级容错规则
  - 展示层采用“按页成行”的流式页面组结构，左侧 PDF 页与右侧译文页一一配对
  - 同一行内左右两页顶部必须齐平，短的一侧通过底部空白补齐，不允许通过拉伸内容实现对齐
  - 行与行之间按页面顺序纵向排列，滚动与定位以“行”为最小联动单元
- 待确认内容：
  - 最终保存接口形态：全量 / 按页 / 按 node / 草稿发布
  - Word 导出由前端直出还是后端生成
  - Word 是否保留显式页分隔
  - 导航树是否需要自动展开当前路径

---

## 阶段进展

### 阶段 1：数据适配与编辑态模型
- 状态：已完成
- 目标：
  - 基于 `merged_ans.json` 同构结构建立前端编辑态
  - 为每个 node 增加统一译文字段映射
  - 建立稳定的 `pageIdx -> nodes`、`nodeId -> node`、`pageIdx -> pageContainer` 映射
- 强约束：
  - 编辑态必须保留：`node_id`、`page_idx`、`reading_order`、`semantic_type`、`heading_level`
  - 原文数据与编辑态数据必须分离，原文结构不能被直接覆盖
  - 译文字段名暂按适配层统一映射，组件内部只消费标准化字段
- 验收标准：
  - 右侧可基于编辑态按页渲染数据
  - 每个 node 都有稳定唯一标识
  - 原始数据、编辑态、视图映射三层关系清晰可追踪

### 阶段 2：页对页流式译文渲染
- 状态：进行中
- 目标：
  - 将左右展示层重构为按页成行的流式页面组
  - 每一行由左侧 PDF 页与右侧对应译文页组成
  - 左右两页一一对应，按页顺序自上而下连续排列
  - 当前阶段仅完成静态页级渲染，不接入 TipTap 编辑能力
  - 每页内部按 node 阅读顺序渲染块
  - 清理或隐藏段级新增、段级删除入口
- 强约束：
  - 页级容器必须可独立定位和滚动观测
  - 每个 block 必须保留 `node_id` 和 `page_idx` 锚点
  - 同一行内左右页顶部必须齐平，短的一侧通过空白补齐，不允许拉伸内容
  - 以“行”为最小布局与联动单元，禁止将左右两栏完全拆成彼此独立的滚动容器
  - 当前阶段先保证页级准确，不强求页内完全视觉对齐
  - 当前阶段不处理内容编辑，仅完成展示骨架
- 验收标准：
  - 形成稳定的按页成行页面列表
  - 每一行左右页严格一一对应
  - 行内顶部齐平，短边空白补齐生效
  - 段级新增/删除功能在 UI 中不可用
  - 译文页展示骨架可供后续编辑能力接入

### 阶段 3：TipTap 内容编辑接入
- 状态：未开始
- 目标：
  - 接入 TipTap 完成译文内容编辑
  - 建立 block 编辑结果回写机制
  - 保证当前选中块、编辑块、展示块状态一致
- 强约束：
  - TipTap 只负责内容编辑，不负责整篇结构管理
  - 任意编辑结果都必须准确回写到对应 `node_id`
  - 不允许因 TipTap 序列化导致 node 映射、页映射丢失
- 验收标准：
  - 可编辑译文内容
  - 编辑结果能稳定同步到 node 编辑态
  - 当前选中块状态可正确维护

### 阶段 4：语义编辑与导航树重建
- 状态：未开始
- 目标：
  - 支持正文 / 一级标题 / 二级标题三种语义切换
  - 样式变化同步更新 `semantic_type` 与 `heading_level`
  - 根据最新 node 状态重建导航树
- 强约束：
  - 正文：`semantic_type = paragraph`，`heading_level = null`
  - 一级标题：`semantic_type = heading`，`heading_level = 1`
  - 二级标题：`semantic_type = heading`，`heading_level = 2`
  - 导航树仅允许 heading 节点进入
  - 导航树必须严格按 `heading_level` 嵌套，并包含容错规则：
    - 小于 1 的层级按 1 处理
    - 大于 2 的层级按 2 处理
    - 二级标题若缺少一级父节点，则自动提升或挂到最近有效父级
    - 空标题默认不进树，必要时显示为“未命名标题”
- 验收标准：
  - 样式与语义字段同步变化
  - 标题改正文后目录节点消失
  - 正文改标题后目录节点出现
  - 导航树层级结构稳定且可用

### 阶段 5：左右联动
- 状态：未开始
- 目标：
  - 实现同步滚动、点击定位、页码跟随
  - 保证右侧驱动左侧、左侧驱动右侧、目录树驱动双侧三类联动均可用
  - 联动基于“页对页成行”的流式布局，滚动时以行的当前位置作为同步基准
- 强约束：
  - 联动事件必须带来源标记：如 `editor` / `pdf` / `outline`
  - 程序触发滚动时，必须抑制反向重复触发，避免死循环和抖动
  - 页级同步优先于块级同步
  - 即使块级定位失败，也必须保证页级定位成功
  - 当某一侧内容更短时，联动以该行最终补齐后的总高度为准，确保对应页顶部始终对齐
- 验收标准：
  - 右侧滚动到某页，左侧准确停留到对应页
  - 左侧点击块或跳页时，右侧能定位到对应页或块
  - 目录树高亮、当前页状态、双侧视图状态保持一致
  - 同一行内左右页顶部齐平，滚动过程中不会出现页顶错位

### 阶段 6：保存机制
- 状态：未开始
- 目标：
  - 形成可持久化的编辑结果提交模型
  - 明确保存粒度与前后端职责
  - 前端负责维护编辑态，后端负责落库、合并和导出协作
- 备选方案：
  - 全量保存
  - 按页保存
  - 按 node 增量保存
  - 草稿 + 发布
- 当前主方案：
  - 第一版优先按页保存
  - 但前端必须保留向 node 增量保存扩展的能力
- 后续扩展：
  - 后续再扩展按 node 增量保存
  - 如业务需要可补充草稿 + 发布
- 验收标准：
  - 至少确定并落地一种保存方案
  - 编辑后的译文、语义字段、导航结果可被正确序列化
  - 保存数据可支持后续导出使用

### 阶段 7：Word 导出
- 状态：未开始
- 目标：
  - 基于最终编辑态导出仅含译文的 Word 文档
  - 保持标题层级和段落顺序正确
  - 导出结果必须只依赖最终编辑态，不得直接拼接原始解析结构
- 强约束：
  - 导出源只能是最终编辑态，不能直接使用原始解析结果
  - 只导出译文，不导出原文
  - Word 中的标题层级必须与 `heading_level` 对应
- 实施要求：
  - 导出时必须从最终编辑态生成，不能依赖原始解析树直接拼装
- 验收标准：
  - 可导出 `.docx`
  - 导出内容仅包含译文
  - 标题层级、正文顺序与编辑结果一致

---

## 页面结构与展示规则

### 1. 页面组织方式
- 页面整体按“行”组织，不按左右两栏独立组织
- 每一行只包含一组对应页面：`左侧 PDF 页 + 右侧译文页`
- 行与行之间按页码顺序从上到下连续排列
- 单个页面组内部允许左右宽度不同，但页顶必须对齐

### 2. 对齐与补空白规则
- 以同一行内更高的一侧作为该行最终高度基准
- 较短一侧在底部补空白，占位完成高度对齐
- 补空白只改变布局占位，不改变内容真实高度，不拉伸 PDF 页或译文内容
- 空白补齐区域必须不可编辑、不可参与语义结构、不可导出为内容

### 3. 高度计算规则
- 行高度以初次渲染后的真实内容高度为准
- 若字体加载、图片加载、编辑内容变化导致高度变化，必须重新计算该行高度
- 重新计算后，左右两侧仍需保持顶部齐平
- 若因异步渲染导致短暂高度不一致，需允许布局二次稳定

### 4. 联动规则
- 联动优先以“页”为单位，其次才考虑页内块定位
- 用户滚动时，当前可见行应作为当前页基准
- 点击目录、点击页码、点击块时，优先定位到对应行，再尝试精确定位到页内块
- 任何程序触发的滚动都必须携带来源标记，避免互相反复触发

### 5. 页面示意
- 结构可理解为：
  - 第 1 行：`PDF 第 1 页 | 译文第 1 页`
  - 第 2 行：`PDF 第 2 页 | 译文第 2 页`
  - 第 3 行：`PDF 第 3 页 | 译文第 3 页`
- 当第 2 页译文比 PDF 更长时：
  - 以译文页高度为准
  - PDF 第 2 页下方补空白
  - 第 3 行从统一起点继续向下排列

---

## 待确认事项

以下事项如果后续实现中仍有歧义，需要在开始对应阶段前向用户确认：

1. 当某页译文内容极长，单页高度远超 PDF 页时，是否允许该页内部继续自然增长，还是需要拆分显示策略
2. 页面滚动锚点以“行顶部”为准，还是以“当前视窗中最接近中心的行”为准
3. 左侧 PDF 是否允许独立缩放，若允许，缩放后是否需要实时重新计算整行高度
4. 译文页中的图片、表格、脚注等复杂元素，是否允许影响行高度并参与补齐
5. 导航树点击后，是优先定位到整行，还是优先定位到具体 node
6. 空白补齐区域是否需要在开发调试态中显示辅助边界

---

## 文档更新规则

- 仅当某阶段完成并经用户验收后，才可将该阶段状态更新为“已完成”
- 进入下一阶段前，必须先更新：
  - `方案进度概览`
  - 对应阶段的状态
  - 如有新增约束或风险，同步补入本文档
- 如需求发生变化，必须先修订本文档，再继续实施
- 后续讨论和实施中，若出现任何不确定、模糊、存在歧义或实现成本过高的点，必须先向用户提问确认，再继续推进
- 在未获得确认前，不得擅自用默认方案替代关键需求
