فهرست منبع

[Stage0] Jianpu Renderer - Project framework and documentation system

- Created jianpu-renderer code framework (29 files)

- Established complete documentation system (11 docs)

- Added visual progress dashboard

- Stage 0 progress: 75%
tianyong 1 ماه پیش
والد
کامیت
39fe7e9f8d
42فایلهای تغییر یافته به همراه4821 افزوده شده و 0 حذف شده
  1. 151 0
      docs/jianpu-renderer/00-START_HERE.md
  2. 933 0
      docs/jianpu-renderer/01-TASKS_CHECKLIST.md
  3. 263 0
      docs/jianpu-renderer/02-PROGRESS.md
  4. 818 0
      docs/jianpu-renderer/03-MUSICXML_KNOWLEDGE.md
  5. 33 0
      docs/jianpu-renderer/04-MUSICXML_MAPPING.md
  6. 32 0
      docs/jianpu-renderer/05-VEXFLOW_COMPAT.md
  7. 35 0
      docs/jianpu-renderer/06-RENDER_SPEC.md
  8. 140 0
      docs/jianpu-renderer/HOW_TO_UPDATE_PROGRESS.md
  9. 230 0
      docs/jianpu-renderer/PROJECT_SUMMARY.md
  10. 136 0
      docs/jianpu-renderer/README.md
  11. 922 0
      docs/jianpu-renderer/progress.html
  12. 154 0
      src/jianpu-renderer/JianpuRenderer.ts
  13. 101 0
      src/jianpu-renderer/README.md
  14. 18 0
      src/jianpu-renderer/__tests__/setup.test.ts
  15. 17 0
      src/jianpu-renderer/adapters/DOMAdapter.ts
  16. 51 0
      src/jianpu-renderer/adapters/OSMDCompatibilityAdapter.ts
  17. 2 0
      src/jianpu-renderer/adapters/index.ts
  18. 84 0
      src/jianpu-renderer/core/config/RenderConfig.ts
  19. 1 0
      src/jianpu-renderer/core/config/index.ts
  20. 35 0
      src/jianpu-renderer/core/drawer/LineDrawer.ts
  21. 29 0
      src/jianpu-renderer/core/drawer/LyricDrawer.ts
  22. 23 0
      src/jianpu-renderer/core/drawer/ModifierDrawer.ts
  23. 27 0
      src/jianpu-renderer/core/drawer/NoteDrawer.ts
  24. 4 0
      src/jianpu-renderer/core/drawer/index.ts
  25. 42 0
      src/jianpu-renderer/core/layout/MeasureLayoutEngine.ts
  26. 20 0
      src/jianpu-renderer/core/layout/MultiVoiceAligner.ts
  27. 15 0
      src/jianpu-renderer/core/layout/NotePositionCalculator.ts
  28. 19 0
      src/jianpu-renderer/core/layout/SystemLayoutEngine.ts
  29. 4 0
      src/jianpu-renderer/core/layout/index.ts
  30. 54 0
      src/jianpu-renderer/core/parser/OSMDDataParser.ts
  31. 22 0
      src/jianpu-renderer/core/parser/TimeCalculator.ts
  32. 2 0
      src/jianpu-renderer/core/parser/index.ts
  33. 21 0
      src/jianpu-renderer/index.ts
  34. 76 0
      src/jianpu-renderer/models/JianpuMeasure.ts
  35. 91 0
      src/jianpu-renderer/models/JianpuNote.ts
  36. 55 0
      src/jianpu-renderer/models/JianpuScore.ts
  37. 36 0
      src/jianpu-renderer/models/JianpuSystem.ts
  38. 8 0
      src/jianpu-renderer/models/index.ts
  39. 46 0
      src/jianpu-renderer/utils/Constants.ts
  40. 27 0
      src/jianpu-renderer/utils/MathHelper.ts
  41. 41 0
      src/jianpu-renderer/utils/SVGHelper.ts
  42. 3 0
      src/jianpu-renderer/utils/index.ts

+ 151 - 0
docs/jianpu-renderer/00-START_HERE.md

@@ -0,0 +1,151 @@
+# 🚀 简谱渲染引擎重写 - 从这里开始!
+
+> **欢迎!** 这是简谱渲染引擎重写项目的起始页  
+> **首次阅读时间:** 约10分钟  
+> **重要性:** ⭐⭐⭐⭐⭐
+
+---
+
+## 👋 新开发者入门
+
+如果你是第一次参与这个项目,请按以下顺序阅读:
+
+### 第一步:了解项目背景(5分钟)
+1. 阅读主项目的 `README.md`
+2. 阅读 `src/jianpu-renderer/README.md`
+3. 了解项目目标和技术栈
+
+### 第二步:学习核心知识(30分钟)
+阅读 **[03-MUSICXML_KNOWLEDGE.md](./03-MUSICXML_KNOWLEDGE.md)**
+- MusicXML基础概念
+- 简谱渲染规范
+- VexFlow技术要点
+- 常见陷阱
+
+### 第三步:查看任务清单(10分钟)
+打开 **[01-TASKS_CHECKLIST.md](./01-TASKS_CHECKLIST.md)**
+- 浏览所有阶段和任务
+- 了解整体开发流程
+- 找到下一个待完成的任务
+
+### 第四步:开始开发!
+按照任务清单逐步完成
+
+---
+
+## 🔄 每次开发的标准流程
+
+### 开始前(5分钟)
+
+```bash
+# 1. 进入项目目录
+cd D:\git\music-score
+
+# 2. 拉取最新代码
+git pull
+
+# 3. 打开进度文档,查看当前状态
+# 文件:docs/jianpu-renderer/02-PROGRESS.md
+
+# 4. 打开任务清单,找到下一个任务
+# 文件:docs/jianpu-renderer/01-TASKS_CHECKLIST.md
+```
+
+### 开发中(持续)
+
+```bash
+# 1. 按照任务清单逐项完成
+# 2. 遇到问题查阅知识文档(03-MUSICXML_KNOWLEDGE.md)
+# 3. 频繁提交代码
+git add .
+git commit -m "[任务X.Y] 完成XXX功能"
+
+# 4. 运行测试
+npm test
+```
+
+### 结束时(10分钟)
+
+```bash
+# 1. 更新任务清单
+# 在 01-TASKS_CHECKLIST.md 中标记 [x] 完成的任务
+
+# 2. 更新进度文档
+# 在 02-PROGRESS.md 中:
+# - 更新当前状态
+# - 记录完成的任务
+# - 添加开发日志
+# - 记录下次开始点
+
+# 3. 提交所有更改
+git add .
+git commit -m "[文档] 更新开发进度"
+git push
+```
+
+---
+
+## 📋 快速参考
+
+### 当前状态(2026-01-29)
+- **当前阶段:** 阶段0 - 准备工作(50%完成)
+- **下一个任务:** 任务0.3 - 创建测试数据和环境
+- **整体进度:** 6% (3/50+ 任务)
+
+### 重要提醒
+- ✅ **不要跳过任务** - 按顺序完成,每个任务都有依赖关系
+- ✅ **及时更新文档** - 进度文档是团队协作的基础
+- ✅ **遵循规范** - 参考03、04、05、06号文档
+- ✅ **频繁测试** - 每完成一个任务立即测试
+
+---
+
+## 📁 文档列表
+
+| 编号 | 文档名 | 用途 | 状态 |
+|------|--------|------|------|
+| 00 | START_HERE.md | 起始页(当前文档) | ✅ |
+| 01 | TASKS_CHECKLIST.md | 完整任务清单 | ✅ |
+| 02 | PROGRESS.md | 开发进度追踪 | ✅ |
+| 03 | MUSICXML_KNOWLEDGE.md | MusicXML核心知识 | ✅ |
+| 04 | MUSICXML_MAPPING.md | 元素映射规范 | ⏸️ 待创建 |
+| 05 | VEXFLOW_COMPAT.md | 兼容性规范 | ⏸️ 待创建 |
+| 06 | RENDER_SPEC.md | 渲染细节规范 | ⏸️ 待创建 |
+| 07 | API_REFERENCE.md | API参考 | ⏸️ 待创建 |
+| 08 | ARCHITECTURE.md | 架构设计 | ⏸️ 待创建 |
+| 09 | TESTING_GUIDE.md | 测试指南 | ⏸️ 待创建 |
+| 10 | CHANGELOG.md | 变更日志 | ⏸️ 待创建 |
+
+---
+
+## 🆘 遇到问题?
+
+### 开发相关问题
+1. 查阅 `03-MUSICXML_KNOWLEDGE.md` 的"常见陷阱"章节
+2. 查看代码注释和TODO标记
+3. 运行测试,查看失败信息
+
+### 任务不清楚?
+1. 查看 `01-TASKS_CHECKLIST.md` 的详细说明
+2. 查看验收标准和测试用例
+3. 参考代码模板
+
+### 不知道从哪里继续?
+1. 打开 `02-PROGRESS.md`
+2. 查看"下一步行动"章节
+3. 按照指示继续
+
+---
+
+## 🎯 成功的关键
+
+1. **按部就班** - 不要跳过任务,每个任务都经过精心设计
+2. **文档驱动** - 遇到问题先查文档,减少重复讨论
+3. **测试驱动** - 每个功能完成立即测试
+4. **持续更新** - 及时更新进度,方便间断后继续
+
+---
+
+**祝开发顺利!** 🎉
+
+有任何问题,请参考文档或联系团队。

+ 933 - 0
docs/jianpu-renderer/01-TASKS_CHECKLIST.md

@@ -0,0 +1,933 @@
+# 简谱渲染引擎重写 - 完整任务清单
+
+> **项目状态:** 准备阶段  
+> **开始日期:** 2026-01-29  
+> **预计完成:** 8-10周  
+> **当前进度:** 阶段0 - 准备工作(进行中)
+
+---
+
+## 📋 总览
+
+### 开发阶段(已调整,总计10周)
+- [ ] **阶段0:准备工作**(第1周)- 50% 完成
+- [ ] **阶段0.5:规范文档编写**(第2周)⭐ 新增
+- [ ] **阶段1:核心解析器**(第3周)- 已调整细化
+- [ ] **阶段2:布局引擎**(第4-5周)
+- [ ] **阶段3:绘制引擎**(第6-7周)- 已调整细化
+- [ ] **阶段4:兼容层实现**(第8周)- 已调整细化
+- [ ] **阶段5:测试与优化**(第9-10周)
+
+### 调整说明
+- ✅ 新增阶段0.5:创建规范文档(磨刀不误砍柴工)
+- ✅ 总周期从8周调整为10周(更稳妥)
+- ✅ 细化关键任务(divisions处理、增时线算法、兼容层字段)
+
+---
+
+## 🎯 阶段0:准备工作(第1周)
+
+### 任务0.1:创建项目结构 ✅ 已完成
+- [x] 创建主目录 `src/jianpu-renderer`
+- [x] 创建子目录结构
+  - [x] `core/parser` - 解析器
+  - [x] `core/layout` - 布局引擎
+  - [x] `core/drawer` - 绘制引擎
+  - [x] `core/config` - 配置
+  - [x] `models` - 数据模型
+  - [x] `adapters` - 兼容层
+  - [x] `utils` - 工具函数
+  - [x] `__tests__` - 测试文件
+- [x] 创建主入口文件
+  - [x] `index.ts`
+  - [x] `JianpuRenderer.ts`
+  - [x] `README.md`
+
+**验收标准:**
+- [x] 所有目录创建完成
+- [x] 文件结构清晰
+- [x] README文档完整
+
+---
+
+### 任务0.2:定义核心数据模型 ✅ 已完成
+- [x] 定义 `JianpuNote` 音符模型
+  - [x] 基础属性(id, pitch, octave, duration等)
+  - [x] 修饰符(accidental, dots)
+  - [x] 时间信息(timestamp, startTime, endTime)
+  - [x] 位置信息(x, y, width, height)
+  - [x] OSMD兼容数据
+- [x] 定义 `JianpuMeasure` 小节模型
+  - [x] 基础信息(index, measureNumber)
+  - [x] 拍号和调号
+  - [x] 音符数组(多声部)
+  - [x] 布局信息
+- [x] 定义 `JianpuSystem` 行模型
+- [x] 定义 `JianpuScore` 总谱模型
+- [x] 定义 `RenderConfig` 渲染配置
+
+**验收标准:**
+- [x] 所有数据模型定义完成
+- [x] TypeScript类型完整
+- [x] 注释详细清晰
+- [x] 导出文件完整
+
+---
+
+### 任务0.3:创建测试数据和环境 ⏸️ 待开始
+- [ ] 准备测试用的MusicXML文件(至少5个)
+  - [ ] `basic.xml` - 基础简谱(只有四分音符)
+  - [ ] `mixed-durations.xml` - 混合时值(八分、四分、二分音符)
+  - [ ] `multi-voice.xml` - 多声部简谱
+  - [ ] `with-lyrics.xml` - 带歌词简谱
+  - [ ] `complex.xml` - 复杂记号(装饰音、连音符等)
+- [ ] 创建测试环境
+  - [ ] 创建对比测试页面 `__tests__/compare.html`
+  - [ ] 配置测试框架(Jest或其他)
+  - [ ] 编写基础测试用例
+- [ ] 创建开发文档
+  - [ ] 创建 `DEVELOPMENT.md` 开发指南
+  - [ ] 创建 `API.md` API文档框架
+
+**验收标准:**
+- [ ] 至少5个测试XML文件准备完成
+- [ ] 测试页面可以正常打开
+- [ ] 测试框架配置完成
+- [ ] 开发文档创建完成
+
+**预计时间:** 0.5天
+
+---
+
+### 任务0.4:创建核心模块空白模板 ✅ 已完成
+- [x] 解析器模块
+  - [x] `OSMDDataParser.ts` - 框架代码
+  - [x] `TimeCalculator.ts` - 框架代码
+- [x] 布局引擎模块
+  - [x] `MeasureLayoutEngine.ts` - 框架代码
+  - [x] `SystemLayoutEngine.ts` - 框架代码
+  - [x] `NotePositionCalculator.ts` - 框架代码
+  - [x] `MultiVoiceAligner.ts` - 框架代码
+- [x] 绘制引擎模块
+  - [x] `NoteDrawer.ts` - 框架代码
+  - [x] `LineDrawer.ts` - 框架代码
+  - [x] `LyricDrawer.ts` - 框架代码
+  - [x] `ModifierDrawer.ts` - 框架代码
+- [x] 兼容层模块
+  - [x] `OSMDCompatibilityAdapter.ts` - 框架代码
+  - [x] `DOMAdapter.ts` - 框架代码
+- [x] 工具函数
+  - [x] `SVGHelper.ts`
+  - [x] `MathHelper.ts`
+  - [x] `Constants.ts`
+
+**验收标准:**
+- [x] 所有模块文件创建完成
+- [x] 每个文件都有基本的类结构和TODO注释
+- [x] 导出文件完整
+
+---
+
+## 📝 阶段0.5:规范文档编写(第2周)⭐ 新增阶段
+
+> **为什么需要这个阶段:** 磨刀不误砍柴工!先定义清楚规范,开发时就不会反复讨论"应该怎么处理"
+
+### 任务0.5.1:创建MusicXML元素映射规范 ⏸️ 待开始
+**目标:** 创建MusicXML标签到简谱的完整映射表
+
+**文件:** `docs/jianpu-renderer/04-MUSICXML_MAPPING.md`
+
+**内容:**
+- [ ] **Divisions转换规则**
+  - divisions的定义和作用
+  - duration到实际时值的转换公式
+  - 处理不同divisions值(1, 256, 480, 960等)
+  - 异常情况处理(divisions=0, null等)
+  
+- [ ] **音高元素解析规则**
+  - `<step>` → 简谱数字(C=1, D=2, ... B=7)
+  - `<octave>` → 高低音点数量
+  - `<alter>` → 升降号(1=#, -1=♭, 0=♮)
+  - 首调vs固定调的处理差异
+  
+- [ ] **时值元素解析规则**
+  - `<type>` → 音符基准类型
+  - `<dot>` → 附点时值计算(+50%, +75%)
+  - `<duration>` → 实际时值
+  - 时值验证(确保不超过小节限制)
+  
+- [ ] **特殊元素处理规则**
+  - `<grace>` → 装饰音(duration=0)
+  - `<rest>` → 休止符
+  - `<chord>` → 和弦(多个音符同时发声)
+  - `<tuplet>` → 连音符(3连音、5连音等)
+  - `<tie>` → 延音线
+  
+- [ ] **修饰符处理规则**
+  - `<accidental>` → 临时升降号
+  - `<articulations>` → 演奏技法(staccato顿音等)
+  - `<ornaments>` → 装饰音记号
+  - `<dynamics>` → 力度记号
+
+**验收标准:**
+- [ ] 文档包含完整的映射表(至少20个元素)
+- [ ] 每个元素都有示例XML
+- [ ] 每个元素都有转换公式
+- [ ] 包含边界情况处理方案
+
+**预计时间:** 2天
+
+---
+
+### 任务0.5.2:创建简谱渲染规范 ⏸️ 待开始
+**目标:** 定义简谱各元素的精确渲染规则
+
+**文件:** `docs/jianpu-renderer/06-RENDER_SPEC.md`
+
+**内容:**
+- [ ] **增时线渲染规范** ⭐⭐⭐
+  - 增时线数量计算公式:`Math.floor(realValue) - 1`
+  - 增时线位置计算:每条占1个四分音符空间
+  - 增时线长度:占空间的70%,居中显示
+  - 附点+增时线的组合处理
+  - 多声部场景下的垂直对齐要求
+  
+- [ ] **减时线渲染规范**
+  - 减时线数量计算公式:`Math.log2(4 / realValue)`
+  - 减时线位置:紧贴音符下方
+  - 减时线间距:2-3像素
+  - 连续音符的减时线连接规则
+  
+- [ ] **高低音点规范**
+  - 高音点:在数字上方,每个点代表高八度
+  - 低音点:在数字下方,每个点代表低八度
+  - 点的大小和间距标准
+  
+- [ ] **附点规范**
+  - 附点位置:音符右侧
+  - 多附点的排列方式
+  
+- [ ] **升降号规范**
+  - 位置:音符左上方
+  - 大小和字体
+  - 固定调vs首调的显示差异
+  
+- [ ] **视觉尺寸标准**
+  - 音符字体大小:20px(默认)
+  - 歌词字体大小:14px
+  - 高低音点半径:2px
+  - 增时线粗细:1px
+  - 小节线粗细:1px
+
+**验收标准:**
+- [ ] 所有渲染元素都有精确的位置和尺寸定义
+- [ ] 包含视觉示意图(ASCII或图片)
+- [ ] 包含特殊情况的处理规则
+
+**预计时间:** 1天
+
+---
+
+### 任务0.5.3:创建VexFlow兼容性规范 ⏸️ 待开始
+**目标:** 定义与VexFlow/OSMD的完整兼容规范
+
+**文件:** `docs/jianpu-renderer/05-VEXFLOW_COMPAT.md`
+
+**内容:**
+- [ ] **DOM结构规范**
+  - 元素ID命名规则:`vf-{noteId}`
+  - CSS类名清单:`vf-note`, `vf-numbered-note-head`等
+  - 元素层级结构
+  - 属性标记规则
+  
+- [ ] **state.times字段完整清单** ⭐⭐⭐
+  - 从业务代码中提取所有使用的字段(30-40个)
+  - 每个字段的类型定义
+  - 每个字段的来源(如何计算/从哪里获取)
+  - 每个字段的用途(哪个业务功能使用)
+  
+- [ ] **cursor接口规范**
+  - Iterator对象的必需方法
+  - reset(), next()等方法的行为定义
+  - CurrentVoiceEntries的数据结构
+  
+- [ ] **GraphicSheet接口规范**
+  - MeasureList结构
+  - parentSourceMeasure必需字段
+  
+- [ ] **其他兼容接口**
+  - Sheet.userStartTempoInBPM
+  - EngravingRules.DYMusicScoreType
+  - 其他业务代码使用的接口
+
+**验收标准:**
+- [ ] state.times字段清单完整(通过业务代码验证)
+- [ ] DOM规范可以生成兼容的HTML
+- [ ] 所有接口都有TypeScript类型定义
+
+**预计时间:** 1.5天
+
+---
+
+### 任务0.5.4:创建测试基准数据 ⏸️ 待开始
+**目标:** 使用现有引擎生成测试基准,作为新引擎的对比标准
+
+**内容:**
+- [ ] 选择5个测试曲谱
+  - basic.xml: 基础简谱
+  - mixed-durations.xml: 混合时值
+  - multi-voice.xml: 多声部
+  - with-lyrics.xml: 带歌词
+  - complex.xml: 复杂记号
+  
+- [ ] 使用旧引擎渲染,保存基准
+  - 截图保存视觉效果
+  - console.log保存state.times数据
+  - 保存DOM结构(innerHTML)
+  - 记录关键测量值(小节宽度、音符位置等)
+  
+- [ ] 创建对比测试页面
+  - 左侧显示旧引擎渲染结果
+  - 右侧显示新引擎渲染结果
+  - 自动对比差异
+
+**验收标准:**
+- [ ] 5个测试曲谱准备完成
+- [ ] 基准数据完整保存
+- [ ] 对比页面可以正常工作
+
+**预计时间:** 1.5天
+
+---
+
+**阶段0.5总结:**
+- 预计时间:6天(约1周+)
+- 核心产出:3个规范文档 + 测试基准数据
+- 价值:为后续8周开发提供明确的规范和标准
+
+---
+
+## 🔧 阶段1:核心解析器(第3周)⭐ 已调整细化
+
+### 任务1.0:创建Divisions处理器 ⏸️ 待开始 ⭐ 新增
+**目标:** 实现MusicXML divisions的正确处理(这是时值计算的基础!)
+
+**文件:** `src/jianpu-renderer/core/parser/DivisionsHandler.ts`
+
+**内容:**
+- [ ] 实现divisions缓存机制
+  - 每个小节可能有不同的divisions
+  - 缓存当前divisions值
+  
+- [ ] 实现duration转换
+  ```typescript
+  realValue = duration / divisions;  // 转换为四分音符为单位
+  ```
+  
+- [ ] 处理异常情况
+  - divisions为0
+  - divisions为null/undefined
+  - duration为负数
+  
+- [ ] 编写单元测试
+  - 测试不同divisions值(1, 256, 480, 960)
+  - 测试转换准确性
+  - 测试异常处理
+
+**验收标准:**
+- [ ] 能正确处理任意divisions值
+- [ ] 转换准确(误差<0.001)
+- [ ] 异常情况有明确错误提示
+- [ ] 单元测试通过(至少8个测试用例)
+
+**预计时间:** 0.5天
+
+---
+
+### 任务1.1:实现OSMD数据解析器 ⏸️ 待开始 ⭐ 已调整细化
+- [ ] 实现 `OSMDDataParser.parse()` 主方法
+  - [ ] 解析OSMD对象结构
+  - [ ] 提取曲谱元数据(标题、作曲家等)
+  - [ ] 提取速度信息
+- [ ] 实现 `parseMeasures()` 方法
+  - [ ] 遍历 `osmd.GraphicSheet.MeasureList`
+  - [ ] 提取每个小节的拍号
+  - [ ] 提取每个小节的调号
+  - [ ] 提取小节号
+- [ ] 实现 `parseNotes()` 方法
+  - [ ] 使用 `osmd.cursor.Iterator` 遍历音符
+  - [ ] 提取音符音高(转换为1-7)
+  - [ ] 提取音符时值
+  - [ ] 提取八度信息
+  - [ ] 提取修饰符(升降号、附点)
+  - [ ] 识别休止符
+  - [ ] 保存OSMD原始数据引用
+- [ ] 实现辅助方法
+  - [ ] `getPitchNumber()` - 音名转简谱数字
+  - [ ] `getOctaveOffset()` - 计算八度偏移
+  - [ ] `getKeyString()` - 获取调号字符串
+
+**验收标准:**
+- [ ] 能正确解析小节数量和拍号
+- [ ] 能正确解析音符音高(1-7)
+- [ ] 能正确解析音符时值
+- [ ] 能正确识别休止符
+- [ ] 能正确识别升降号和附点
+- [ ] 保留了OSMD原始数据引用
+- [ ] 单元测试通过(至少10个测试用例)
+
+**测试用例:**
+```
+✓ 应该正确解析4/4拍小节
+✓ 应该正确解析3/4拍小节
+✓ 应该正确解析音符音高 do re mi fa sol la si
+✓ 应该正确识别休止符
+✓ 应该正确解析附点音符
+✓ 应该正确解析升降号
+✓ 应该正确解析多声部
+✓ 应该正确解析不同时值的音符
+✓ 应该保留OSMD数据引用
+✓ 应该正确解析调号变化
+```
+
+**预计时间:** 3天
+
+---
+
+### 任务1.2:实现时间计算器 ⏸️ 待开始
+- [ ] 实现 `calculateTimes()` 主方法
+  - [ ] 根据BPM计算四分音符时长
+  - [ ] 按小节和时间戳排序音符
+  - [ ] 计算每个音符的绝对开始时间
+  - [ ] 计算每个音符的绝对结束时间
+- [ ] 支持变速
+  - [ ] 识别小节内的速度变化
+  - [ ] 正确计算变速后的时间
+- [ ] 处理反复记号
+  - [ ] 识别反复记号(暂时可以简化处理)
+
+**验收标准:**
+- [ ] 时间计算准确(误差<1ms)
+- [ ] 支持不同速度(60-240 BPM)
+- [ ] 支持变速
+- [ ] 单元测试通过
+
+**测试用例:**
+```
+✓ 120 BPM时四分音符应该是0.5秒
+✓ 60 BPM时四分音符应该是1秒
+✓ 应该正确计算连续音符的时间
+✓ 应该正确处理变速
+✓ 时间计算误差应该小于1ms
+```
+
+**预计时间:** 1天
+
+---
+
+### 任务1.3:集成测试解析器 ⏸️ 待开始
+- [ ] 使用真实的MusicXML文件测试
+- [ ] 验证解析结果的完整性
+- [ ] 修复发现的bug
+- [ ] 优化性能
+
+**预计时间:** 0.5天
+
+---
+
+## 📐 阶段2:布局引擎(第3-4周)
+
+### 任务2.1:实现小节布局计算器 ⏸️ 待开始
+- [ ] 实现固定时间比例算法
+  - [ ] 根据拍号计算小节总时值
+  - [ ] 计算小节内容宽度
+  - [ ] 计算小节总宽度(包含padding)
+- [ ] 实现 `calculateNotePositions()` 方法
+  - [ ] 计算每个音符的X坐标
+  - [ ] 应用固定时间比例公式
+  - [ ] 计算音符宽度
+  - [ ] 应用最小间距限制
+- [ ] 实现 `layoutMeasures()` 批量布局方法
+  - [ ] 遍历所有小节
+  - [ ] 累计X坐标
+
+**核心算法:**
+```typescript
+// 小节宽度 = (拍数 / 单位拍 × 4) × 四分音符间距 + 左右padding
+measureWidth = (beats / beatType × 4) × quarterNoteSpacing + padding × 2
+
+// 音符X坐标 = 小节起始X + 左padding + 时间戳 × 单位间距
+noteX = measureX + measurePadding + timestamp × (beatType / 4) × quarterNoteSpacing
+```
+
+**验收标准:**
+- [ ] 同一拍号的小节宽度完全一致
+- [ ] 音符间距严格按时值比例分配
+- [ ] 左右padding正确应用
+- [ ] 单元测试通过
+
+**测试用例:**
+```
+✓ 4/4拍所有小节宽度应该相同
+✓ 3/4拍所有小节宽度应该相同
+✓ 4/4拍宽度应该是3/4拍的4/3倍
+✓ 二分音符间距应该是四分音符的2倍
+✓ 八分音符间距应该是四分音符的0.5倍
+✓ padding应该正确应用
+```
+
+**预计时间:** 3天
+
+---
+
+### 任务2.2:实现多声部对齐 ⏸️ 待开始
+- [ ] 实现 `MultiVoiceAligner.alignVoices()` 方法
+  - [ ] 收集所有时间戳
+  - [ ] 为每个时间戳分配统一的X坐标
+  - [ ] 更新所有声部音符的X坐标
+- [ ] 处理边界情况
+  - [ ] 单声部不需要对齐
+  - [ ] 休止符的对齐
+  - [ ] 不同时值音符的对齐
+
+**验收标准:**
+- [ ] 相同时间戳的音符X坐标完全相同
+- [ ] 不影响单声部的布局
+- [ ] 多声部总谱测试通过
+
+**测试用例:**
+```
+✓ 相同时间点的音符应该X坐标相同
+✓ 单声部不应该被影响
+✓ 休止符应该正确对齐
+✓ 不同时值的音符应该正确对齐
+```
+
+**预计时间:** 2天
+
+---
+
+### 任务2.3:实现行布局和自动换行 ⏸️ 待开始
+- [ ] 实现 `SystemLayoutEngine.layoutSystems()` 方法
+  - [ ] 设置行宽度
+  - [ ] 遍历小节,计算是否需要换行
+  - [ ] 创建新行
+  - [ ] 分配小节到行
+- [ ] 实现Y坐标计算
+  - [ ] 计算行间距
+  - [ ] 计算每行的Y坐标
+- [ ] 优化行分配
+  - [ ] 避免小节被拆分
+  - [ ] 优化最后一行
+
+**验收标准:**
+- [ ] 自动换行功能正常
+- [ ] 行间距正确
+- [ ] 不会出现空行
+- [ ] 小节不会被拆分
+- [ ] 测试通过
+
+**预计时间:** 2天
+
+---
+
+### 任务2.4:实现音符Y坐标计算 ⏸️ 待开始
+- [ ] 实现 `NotePositionCalculator.calculateNoteY()` 方法
+  - [ ] 根据声部数量计算间距
+  - [ ] 为每个声部分配Y坐标
+  - [ ] 确保声部之间有足够间距
+
+**验收标准:**
+- [ ] 音符Y坐标正确
+- [ ] 声部间距合理
+- [ ] 多声部不重叠
+
+**预计时间:** 1天
+
+---
+
+## 🎨 阶段3:绘制引擎(第5-6周)
+
+### 任务3.1:实现基础音符绘制 ⏸️ 待开始
+- [ ] 实现 `NoteDrawer.drawNote()` 主方法
+  - [ ] 创建SVG group容器
+  - [ ] 设置正确的ID (`vf-{noteId}`)
+  - [ ] 添加CSS类名 (`vf-note`)
+- [ ] 实现简谱数字绘制
+  - [ ] 创建text元素
+  - [ ] 设置字体大小和样式
+  - [ ] 添加CSS类名 (`vf-numbered-note-head`)
+  - [ ] 设置文本内容(1-7或0)
+- [ ] 实现高低音点绘制
+  - [ ] 计算高低音点数量
+  - [ ] 计算高低音点位置
+  - [ ] 创建circle元素
+  - [ ] 高音点在上方,低音点在下方
+- [ ] 实现附点绘制
+  - [ ] 计算附点数量
+  - [ ] 计算附点位置(音符右侧)
+  - [ ] 创建circle元素
+- [ ] 实现升降号绘制
+  - [ ] 创建text元素
+  - [ ] 设置符号(#、♭、♮)
+  - [ ] 定位在音符左侧
+- [ ] 实现休止符绘制
+  - [ ] 绘制数字"0"
+  - [ ] 添加rest类名
+
+**验收标准:**
+- [ ] 能正确绘制数字1-7
+- [ ] 能正确绘制休止符0
+- [ ] 高低音点位置正确,数量正确
+- [ ] 附点位置正确
+- [ ] 升降号显示正确
+- [ ] DOM结构符合VexFlow规范
+- [ ] CSS类名正确
+- [ ] 视觉效果符合简谱规范
+
+**预计时间:** 3天
+
+---
+
+### 任务3.2:实现线条绘制(增时线、减时线) ⏸️ 待开始
+- [ ] 实现 `LineDrawer.drawDurationLines()` 主方法
+  - [ ] 判断是长音符还是短音符
+  - [ ] 调用对应的绘制方法
+- [ ] 实现增时线绘制 `drawExtensionLines()`
+  - [ ] 计算需要绘制几条增时线
+  - [ ] 计算每条增时线的位置
+  - [ ] 增时线按固定时间比例分布
+  - [ ] 创建rect元素绘制横线
+  - [ ] 设置正确的ID (`vf-{noteId}-lines`)
+- [ ] 实现减时线绘制 `drawUnderlines()`
+  - [ ] 计算需要绘制几条减时线
+  - [ ] 创建rect元素绘制下划线
+  - [ ] 设置间距(3px)
+  - [ ] 定位在音符下方
+- [ ] 实现小节线绘制
+  - [ ] 创建line元素
+  - [ ] 设置位置和高度
+  - [ ] 添加CSS类名
+
+**核心算法:**
+```typescript
+// 增时线数量 = Math.floor(时值) - 1
+// 二分音符(2.0):1条
+// 全音符(4.0):3条
+
+// 减时线数量 = Math.log2(4 / 时值)
+// 八分音符(0.5):1条
+// 十六分音符(0.25):2条
+// 三十二分音符(0.125):3条
+```
+
+**验收标准:**
+- [ ] 增时线长度和位置符合简谱规范
+- [ ] 增时线按时值均匀分布
+- [ ] 减时线数量正确
+- [ ] 减时线间距正确
+- [ ] 小节线显示正确
+- [ ] 元素ID符合命名规则
+- [ ] 视觉效果正确
+
+**预计时间:** 2天
+
+---
+
+### 任务3.3:实现歌词绘制 ⏸️ 待开始
+- [ ] 实现 `LyricDrawer.drawLyric()` 方法
+  - [ ] 创建text元素
+  - [ ] 设置正确的CSS类名 (`vf-lyric`, `lyric{noteId}`)
+  - [ ] 设置 `lyricIndex` 属性(用于多遍歌词)
+  - [ ] 定位在音符下方
+  - [ ] 设置字体大小和样式
+- [ ] 支持多遍歌词
+  - [ ] 正确设置lyricIndex
+  - [ ] 确保选择器规则正确
+
+**验收标准:**
+- [ ] 歌词位置正确(在音符下方)
+- [ ] CSS类名符合规则
+- [ ] lyricIndex属性正确设置
+- [ ] 支持多遍歌词
+- [ ] 中英文歌词都正常显示
+
+**预计时间:** 1天
+
+---
+
+### 任务3.4:实现修饰符绘制 ⏸️ 待开始
+- [ ] 实现装饰音绘制
+- [ ] 实现连音符标记绘制
+- [ ] 实现其他修饰符
+
+**预计时间:** 1天
+
+---
+
+### 任务3.5:集成测试绘制引擎 ⏸️ 待开始
+- [ ] 渲染测试曲谱
+- [ ] 对比视觉效果
+- [ ] 修复视觉问题
+- [ ] 优化渲染性能
+
+**预计时间:** 1天
+
+---
+
+## 🔗 阶段4:兼容层实现(第7周)
+
+### 任务4.1:实现OSMDCompatibilityAdapter ⏸️ 待开始
+- [ ] 实现 `generateTimesArray()` 方法
+  - [ ] 遍历所有音符
+  - [ ] 生成兼容的数据结构
+  - [ ] 包含所有必需字段:
+    - [ ] `i`, `noteId`, `id`
+    - [ ] `time`, `endtime`
+    - [ ] `MeasureNumberXML`, `measureListIndex`
+    - [ ] `svgElement` (with attrs.id)
+    - [ ] `noteElement`
+    - [ ] `bbox`
+    - [ ] `halfTone`, `frequency`
+    - [ ] `isRestFlag`, `realValue`
+    - [ ] 其他业务字段
+- [ ] 实现 `createCursorAdapter()` 方法
+  - [ ] 实现Iterator接口
+  - [ ] 实现reset()方法
+  - [ ] 实现next()方法
+  - [ ] 实现moveToNext()方法
+  - [ ] 提供CurrentVoiceEntries
+- [ ] 实现 `createGraphicSheetAdapter()` 方法
+  - [ ] 生成MeasureList结构
+  - [ ] 包含parentSourceMeasure信息
+  - [ ] 提供必要的属性
+
+**验收标准:**
+- [ ] `state.times`数组结构100%兼容
+- [ ] 所有必需字段都存在
+- [ ] 数据类型正确
+- [ ] `cursor`接口功能完整
+- [ ] `GraphicSheet`接口可用
+- [ ] 业务代码可以正常使用
+
+**预计时间:** 3天
+
+---
+
+### 任务4.2:集成到业务层 ⏸️ 待开始
+- [ ] 修改 `src/view/music-score/index.tsx`
+  - [ ] 添加引擎选择逻辑
+  - [ ] 支持URL参数切换 (`useNewEngine=1`)
+  - [ ] 保持API兼容性
+- [ ] 测试切换功能
+  - [ ] 测试新引擎渲染
+  - [ ] 测试旧引擎渲染
+  - [ ] 测试切换是否无缝
+
+**验收标准:**
+- [ ] 可以通过URL参数切换引擎
+- [ ] 两种引擎切换无缝
+- [ ] 不影响其他功能
+
+**预计时间:** 1天
+
+---
+
+### 任务4.3:业务功能兼容性测试 ⏸️ 待开始
+- [ ] 测试播放音符高亮
+  - [ ] 音符能正确高亮
+  - [ ] CSS类名生效
+  - [ ] 高亮位置准确
+- [ ] 测试播放歌词高亮
+  - [ ] 歌词能正确高亮
+  - [ ] 多遍歌词正确匹配
+- [ ] 测试点击跳转播放
+  - [ ] 点击音符能跳转
+  - [ ] 播放时间正确
+- [ ] 测试选区功能
+  - [ ] 可以选择小节
+  - [ ] 选区位置准确
+  - [ ] bbox计算正确
+- [ ] 测试光标跟随
+  - [ ] 光标能正确移动
+  - [ ] cursor接口正常
+- [ ] 测试评测报告着色
+  - [ ] 背景色能正确设置
+  - [ ] `.vf-custom-bg`元素存在
+
+**验收标准:**
+- [ ] 所有业务功能测试通过
+- [ ] 无功能退化
+- [ ] 性能正常
+
+**预计时间:** 2天
+
+---
+
+### 任务4.4:修复兼容性问题 ⏸️ 待开始
+- [ ] 根据测试结果修复问题
+- [ ] 优化兼容层代码
+- [ ] 补充缺失的字段
+- [ ] 完善文档
+
+**预计时间:** 1天
+
+---
+
+## 🧪 阶段5:测试与优化(第8周)
+
+### 任务5.1:功能完整性测试 ⏸️ 待开始
+- [ ] 基础渲染测试
+  - [ ] 测试基本音符显示
+  - [ ] 测试休止符显示
+  - [ ] 测试高低音点
+  - [ ] 测试附点
+  - [ ] 测试升降号
+- [ ] 时值线测试
+  - [ ] 测试增时线
+  - [ ] 测试减时线
+  - [ ] 测试增时线位置
+- [ ] 布局测试
+  - [ ] 测试小节宽度一致性
+  - [ ] 测试固定时间比例
+  - [ ] 测试自动换行
+  - [ ] 测试多声部对齐
+- [ ] 歌词测试
+  - [ ] 测试单行歌词
+  - [ ] 测试多遍歌词
+  - [ ] 测试中英文混合
+- [ ] 特殊记号测试
+  - [ ] 测试装饰音
+  - [ ] 测试连音符
+  - [ ] 测试力度记号
+
+**预计时间:** 2天
+
+---
+
+### 任务5.2:兼容性测试 ⏸️ 待开始
+- [ ] 不同浏览器测试
+  - [ ] Chrome
+  - [ ] Edge
+  - [ ] Safari(如果需要)
+- [ ] 不同曲谱测试
+  - [ ] 简单曲谱
+  - [ ] 复杂曲谱
+  - [ ] 长曲谱(100+小节)
+  - [ ] 多声部曲谱
+- [ ] 边界情况测试
+  - [ ] 极短音符(三十二分音符)
+  - [ ] 极长音符(全音符)
+  - [ ] 极端速度(30BPM、300BPM)
+  - [ ] 变拍曲谱
+
+**预计时间:** 2天
+
+---
+
+### 任务5.3:性能优化 ⏸️ 待开始
+- [ ] 性能测试
+  - [ ] 测试渲染时间
+  - [ ] 测试内存使用
+  - [ ] 找出性能瓶颈
+- [ ] 优化措施
+  - [ ] 实现Canvas离屏渲染
+  - [ ] 实现增量渲染
+  - [ ] 优化算法复杂度
+  - [ ] 减少DOM操作
+- [ ] 验证优化效果
+
+**预计时间:** 2天
+
+---
+
+### 任务5.4:文档完善 ⏸️ 待开始
+- [ ] 完善API文档
+- [ ] 编写使用指南
+- [ ] 编写开发指南
+- [ ] 添加示例代码
+- [ ] 更新README
+
+**预计时间:** 1天
+
+---
+
+### 任务5.5:代码审查和清理 ⏸️ 待开始
+- [ ] 代码审查
+  - [ ] 检查代码规范
+  - [ ] 检查注释完整性
+  - [ ] 检查错误处理
+- [ ] 清理工作
+  - [ ] 删除调试代码
+  - [ ] 删除TODO注释(或转为issue)
+  - [ ] 优化代码结构
+- [ ] 最终验收
+  - [ ] 运行所有测试
+  - [ ] 确认所有功能正常
+  - [ ] 准备发布
+
+**预计时间:** 1天
+
+---
+
+## 📊 进度跟踪
+
+### 当前状态
+- **当前阶段:** 阶段0 - 准备工作
+- **当前任务:** 任务0.3 - 创建测试数据和环境
+- **完成度:** 25%
+- **开始日期:** 2026-01-29
+- **预计完成:** 2026-03-26(8周后)
+
+### 里程碑
+- [ ] **Checkpoint 1** - 阶段0完成(第1周结束)
+- [ ] **Checkpoint 2** - 阶段1完成(第2周结束)
+- [ ] **Checkpoint 3** - 阶段2完成(第4周结束)
+- [ ] **Checkpoint 4** - 阶段3完成(第6周结束)
+- [ ] **Checkpoint 5** - 阶段4完成(第7周结束)
+- [ ] **Checkpoint 6** - 项目完成(第8周结束)
+
+---
+
+## 📝 使用说明
+
+### 每次开发时:
+1. 打开本文档,查看当前任务
+2. 按照任务清单逐项完成
+3. 完成后在任务前打勾 `[x]`
+4. 更新进度跟踪部分
+5. 提交代码时引用任务编号
+
+### 任务命名规则:
+- 阶段:`阶段N` (0-5)
+- 任务:`任务N.M` (如任务1.2)
+- Git提交:`[任务N.M] 完成XXX功能`
+
+### 状态标记:
+- ✅ 已完成
+- 🚧 进行中
+- ⏸️ 待开始
+- ⚠️ 遇到问题
+- 🔄 需要重做
+
+---
+
+## 🎯 下一步行动
+
+**当前优先级:** 完成阶段0
+
+**下一个任务:** 任务0.3 - 创建测试数据和环境
+
+**需要做的事情:**
+1. 准备5个测试XML文件
+2. 创建对比测试页面
+3. 配置测试框架
+4. 编写开发文档
+
+**预计时间:** 0.5天
+
+---
+
+**文档版本:** v1.0  
+**最后更新:** 2026-01-29  
+**维护者:** 开发团队

+ 263 - 0
docs/jianpu-renderer/02-PROGRESS.md

@@ -0,0 +1,263 @@
+# 简谱渲染引擎重写 - 开发进度追踪
+
+> **最后更新:** 2026-01-29  
+> **当前阶段:** 阶段0 - 准备工作  
+> **整体进度:** 10% ██░░░░░░░░░░░░░░░░░░
+
+---
+
+## 📊 当前状态
+
+### 正在进行
+- **阶段:** 阶段0 - 准备工作(第1周)
+- **任务:** 任务0.3 - 创建测试数据和环境
+- **进度:** 已完成任务0.1、0.2、0.4,正在进行任务0.3
+
+### 下一步行动
+1. 完成任务0.3:准备测试XML文件和对比页面
+2. 进入阶段0.5:编写规范文档(新增阶段)
+3. 创建MusicXML映射规范
+4. 创建简谱渲染规范
+5. 创建VexFlow兼容性规范
+
+---
+
+## ✅ 已完成任务
+
+### 阶段0:准备工作(进行中 - 75%完成)
+
+#### ✅ 任务0.1:创建项目结构
+**完成日期:** 2026-01-29  
+**用时:** 0.5小时
+
+**完成内容:**
+- [x] 创建主目录 `src/jianpu-renderer`
+- [x] 创建完整的子目录结构(8个目录)
+- [x] 创建主入口文件
+
+---
+
+#### ✅ 任务0.2:定义核心数据模型
+**完成日期:** 2026-01-29  
+**用时:** 1小时
+
+**完成内容:**
+- [x] 定义所有数据模型(5个文件,300+行)
+- [x] 所有模型都有详细注释
+- [x] 包含OSMD兼容数据字段
+
+---
+
+#### ✅ 任务0.4:创建核心模块空白模板
+**完成日期:** 2026-01-29  
+**用时:** 1小时
+
+**完成内容:**
+- [x] 创建所有模块框架(15个文件)
+- [x] 每个类都有基本结构和TODO注释
+
+---
+
+#### ✅ 文档体系建立
+**完成日期:** 2026-01-29  
+**用时:** 2小时
+
+**完成内容:**
+- [x] 创建统一的文档目录 `docs/jianpu-renderer/`
+- [x] 创建完整任务清单文档(700+行)
+- [x] 创建进度追踪文档
+- [x] 创建MusicXML核心知识文档(300+行)
+- [x] 创建文档导航页
+
+**文档清单:**
+```
+docs/jianpu-renderer/
+├── 00-START_HERE.md          ✅ 起始页
+├── 01-TASKS_CHECKLIST.md     ✅ 任务清单(已更新为10周计划)
+├── 02-PROGRESS.md            ✅ 进度追踪
+├── 03-MUSICXML_KNOWLEDGE.md  ✅ MusicXML知识
+├── 04-MUSICXML_MAPPING.md    ⏸️ 待完成(阶段0.5)
+├── 05-VEXFLOW_COMPAT.md      ⏸️ 待完成(阶段0.5)
+├── 06-RENDER_SPEC.md         ⏸️ 待完成(阶段0.5)
+└── README.md                 ✅ 文档中心
+```
+
+---
+
+## 📋 进行中任务
+
+### 任务0.3:创建测试数据和环境
+**开始日期:** 2026-01-29  
+**预计用时:** 0.5天
+
+**进度:** 0%
+
+**待完成:**
+- [ ] 从 `osmd-extended/test/data/` 中选择5个测试XML文件
+- [ ] 复制到项目测试目录
+- [ ] 创建对比测试HTML页面
+- [ ] 配置测试框架
+
+---
+
+## 📅 里程碑进度
+
+### Checkpoint 0:阶段0完成(第1周结束)
+**预期日期:** 2026-02-05  
+**状态:** 🚧 进行中(75%完成)
+
+**检查项:**
+- [x] 项目结构创建完成
+- [x] 数据模型定义完成
+- [x] 文档体系建立完成
+- [ ] 测试环境准备完成 ← 当前任务
+
+---
+
+### Checkpoint 0.5:阶段0.5完成(第2周结束)⭐ 新增
+**预期日期:** 2026-02-12  
+**状态:** ⏸️ 未开始
+
+**检查项:**
+- [ ] MusicXML映射规范文档完成
+- [ ] 简谱渲染规范文档完成
+- [ ] VexFlow兼容性规范文档完成
+- [ ] 测试基准数据准备完成
+- [ ] 可以开始编码
+
+---
+
+### Checkpoint 1:阶段1完成(第3周结束)
+**预期日期:** 2026-02-19  
+**状态:** ⏸️ 未开始
+
+---
+
+### Checkpoint 2:阶段2完成(第5周结束)
+**预期日期:** 2026-03-05  
+**状态:** ⏸️ 未开始
+
+---
+
+### Checkpoint 3:阶段3完成(第7周结束)
+**预期日期:** 2026-03-19  
+**状态:** ⏸️ 未开始
+
+---
+
+### Checkpoint 4:阶段4完成(第8周结束)
+**预期日期:** 2026-03-26  
+**状态:** ⏸️ 未开始
+
+---
+
+### Checkpoint 5:项目完成(第10周结束)
+**预期日期:** 2026-04-09  
+**状态:** ⏸️ 未开始
+
+---
+
+## 📈 进度统计
+
+### 整体进度
+- **总任务数:** 60+个任务(已调整)
+- **已完成:** 4个任务
+- **进行中:** 1个任务
+- **待开始:** 55+个任务
+- **完成度:** 10%
+
+### 阶段进度
+| 阶段 | 进度 | 状态 | 预计时间 |
+|------|------|------|---------|
+| 阶段0:准备工作 | 75% | 🚧 进行中 | 第1周 |
+| 阶段0.5:规范文档 | 0% | ⏸️ 未开始 | 第2周 ⭐新增 |
+| 阶段1:核心解析器 | 0% | ⏸️ 未开始 | 第3周 |
+| 阶段2:布局引擎 | 0% | ⏸️ 未开始 | 第4-5周 |
+| 阶段3:绘制引擎 | 0% | ⏸️ 未开始 | 第6-7周 |
+| 阶段4:兼容层 | 0% | ⏸️ 未开始 | 第8周 |
+| 阶段5:测试优化 | 0% | ⏸️ 未开始 | 第9-10周 |
+
+### 代码统计
+- **文件总数:** 29个代码文件 + 8个文档文件
+- **代码行数:** ~1500行(框架代码)
+- **文档行数:** ~1500行
+- **测试文件:** 1个
+
+---
+
+## 🎯 关键决策记录
+
+### 决策1:采用10周开发计划(调整)
+**日期:** 2026-01-29  
+**原因:**
+- 新增阶段0.5:规范文档编写(1周)
+- 细化关键任务(divisions、增时线、兼容层)
+- 增加测试和优化时间
+- 更加稳妥,降低返工风险
+
+### 决策2:创建统一的文档目录
+**日期:** 2026-01-29  
+**原因:**
+- 文档是开发的重要资产
+- 统一管理便于查找和维护
+- 文档编号便于引用
+
+### 决策3:文档优先策略
+**日期:** 2026-01-29  
+**原因:**
+- 先定义清楚规范再写代码
+- 减少开发中的反复讨论
+- 规范可作为代码审查标准
+- 提高代码质量,降低bug率
+
+---
+
+## 📝 开发日志
+
+### 2026-01-29(第1天)
+- ✅ 创建项目结构(29个代码文件)
+- ✅ 定义所有数据模型
+- ✅ 创建所有模块框架
+- ✅ 建立完整的文档体系(8个文档)
+- ✅ 深入学习MusicXML和VexFlow知识
+- ✅ 调整开发计划为10周
+- ✅ 新增阶段0.5:规范文档编写
+- 📝 下一步:完成任务0.3,准备测试环境
+
+---
+
+## 🔗 文档导航
+
+- **[从这里开始](./00-START_HERE.md)** - 新手入门指南
+- **[任务清单](./01-TASKS_CHECKLIST.md)** - 所有任务详情
+- **[当前文档](./02-PROGRESS.md)** - 进度追踪
+- **[知识文档](./03-MUSICXML_KNOWLEDGE.md)** - MusicXML和VexFlow知识
+- **[文档中心](./README.md)** - 所有文档的导航
+
+---
+
+## 📞 下次开发时的检查清单
+
+**开始前:**
+- [ ] 阅读本进度文档
+- [ ] 查看任务清单文档
+- [ ] 拉取最新代码
+- [ ] 确认当前任务
+
+**开发中:**
+- [ ] 按照任务清单逐项完成
+- [ ] 频繁提交代码
+- [ ] 更新任务状态
+- [ ] 运行测试
+
+**结束时:**
+- [ ] 更新本文档的进度
+- [ ] 更新任务清单的完成状态
+- [ ] 提交所有更改
+- [ ] 记录下次开始点
+
+---
+
+**最后更新:** 2026-01-29 20:00  
+**更新人:** 开发团队  
+**版本:** v1.1(已调整为10周计划)

+ 818 - 0
docs/jianpu-renderer/03-MUSICXML_KNOWLEDGE.md

@@ -0,0 +1,818 @@
+# MusicXML与简谱渲染 - 核心知识文档
+
+> **创建日期:** 2026-01-29  
+> **目的:** 为简谱渲染引擎开发提供理论基础和参考规范  
+> **参考:** [MusicXML 4.0官方文档](https://www.w3.org/2021/06/musicxml40/) | [VexFlow官网](https://www.vexflow.com/)
+
+---
+
+## 📚 第一部分:MusicXML核心概念
+
+### 1.1 文档结构
+
+**MusicXML的两种格式:**
+- `<score-partwise>`: 以声部(Part)为主组织结构(最常用)
+- `<score-timewise>`: 以时间(Measure)为主组织结构
+
+**基本层级结构:**
+```xml
+<score-partwise>
+  <part-list>         <!-- 声部列表定义 -->
+    <score-part id="P1">
+      <part-name>Piano</part-name>
+    </score-part>
+  </part-list>
+  
+  <part id="P1">      <!-- 具体声部内容 -->
+    <measure number="1">  <!-- 小节 -->
+      <attributes>    <!-- 小节属性:拍号、调号、divisions等 -->
+      </attributes>
+      <note>          <!-- 音符 -->
+      </note>
+    </measure>
+  </part>
+</score-partwise>
+```
+
+### 1.2 Divisions(时值基准单位)⭐⭐⭐
+
+**核心概念:**
+- `<divisions>`: 定义每个四分音符包含多少个"division"单位
+- 所有音符的 `<duration>` 都以这个单位计算
+- **关键:不同的MusicXML文件可能使用不同的divisions值**
+
+**示例(divisions=256):**
+```xml
+<attributes>
+  <divisions>256</divisions>  <!-- 1个四分音符 = 256 divisions -->
+</attributes>
+
+<!-- 全音符 = 4个四分音符 = 1024 divisions -->
+<note>
+  <duration>1024</duration>
+  <type>whole</type>
+</note>
+
+<!-- 二分音符 = 2个四分音符 = 512 divisions -->
+<note>
+  <duration>512</duration>
+  <type>half</type>
+</note>
+
+<!-- 四分音符 = 1个四分音符 = 256 divisions -->
+<note>
+  <duration>256</duration>
+  <type>quarter</type>
+</note>
+
+<!-- 八分音符 = 0.5个四分音符 = 128 divisions -->
+<note>
+  <duration>128</duration>
+  <type>eighth</type>
+</note>
+
+<!-- 附点四分音符 = 1.5个四分音符 = 384 divisions -->
+<note>
+  <duration>384</duration>
+  <type>quarter</type>
+  <dot/>
+</note>
+```
+
+**转换公式:**
+```
+实际时值(以四分音符为单位)= duration / divisions
+实际时长(秒)= 实际时值 × (60 / BPM)
+```
+
+### 1.3 音符(Note)元素 ⭐⭐⭐⭐⭐
+
+**音符的完整结构:**
+```xml
+<note default-x="76" default-y="10" color="#000000">
+  <!-- 音高信息(五线谱) -->
+  <pitch>
+    <step>C</step>          <!-- 音名:C D E F G A B -->
+    <alter>1</alter>        <!-- 升降:1=升,-1=降,0或不存在=还原 -->
+    <octave>4</octave>      <!-- 八度:4是中央C所在的八度 -->
+  </pitch>
+  
+  <!-- 或者休止符 -->
+  <rest/>
+  
+  <!-- 时值 -->
+  <duration>256</duration>  <!-- 以divisions为单位 -->
+  <type>quarter</type>      <!-- 音符类型:whole, half, quarter, eighth, 16th, 32nd, 64th, 128th -->
+  <dot/>                    <!-- 附点(可多次出现,表示多附点) -->
+  
+  <!-- 声部和谱表 -->
+  <voice>1</voice>          <!-- 声部编号 -->
+  <staff>1</staff>          <!-- 谱表编号(钢琴有2个谱表) -->
+  
+  <!-- 符干方向 -->
+  <stem>up</stem>           <!-- up或down -->
+  
+  <!-- 连线信息 -->
+  <beam number="1">begin</beam>  <!-- begin, continue, end -->
+  
+  <!-- 延音线 -->
+  <tie type="start"/>       <!-- start或stop -->
+  <tied type="start"/>      <!-- 渲染信息 -->
+  
+  <!-- 装饰音标记 -->
+  <notations>
+    <articulations>
+      <staccato/>           <!-- 顿音 -->
+      <accent/>             <!-- 重音 -->
+    </articulations>
+    <ornaments>
+      <trill-mark/>         <!-- 颤音 -->
+    </ornaments>
+  </notations>
+  
+  <!-- 歌词 -->
+  <lyric number="1">
+    <syllabic>single</syllabic>  <!-- single, begin, middle, end -->
+    <text>啊</text>
+  </lyric>
+</note>
+```
+
+**关键字段说明:**
+
+| 字段 | 含义 | 取值范围 | 渲染要求 |
+|------|------|---------|---------|
+| `<step>` | 音名 | C, D, E, F, G, A, B | 转换为简谱数字1-7 |
+| `<octave>` | 八度 | 通常0-9 | 转换为高低音点 |
+| `<alter>` | 升降 | -2,-1,0,1,2 | 渲染为#、♭符号 |
+| `<duration>` | 时值 | 正整数 | 需除以divisions转换 |
+| `<type>` | 类型 | whole, half, quarter等 | 决定增时线/减时线 |
+| `<dot>` | 附点 | 可多个 | 每个附点增加50%时值 |
+
+### 1.4 拍号(Time Signature)
+
+```xml
+<attributes>
+  <time>
+    <beats>4</beats>        <!-- 分子:每小节4拍 -->
+    <beat-type>4</beat-type>  <!-- 分母:以四分音符为一拍 -->
+  </time>
+</attributes>
+```
+
+**常见拍号:**
+- 4/4拍:每小节4个四分音符
+- 3/4拍:每小节3个四分音符
+- 6/8拍:每小节6个八分音符(= 3个四分音符)
+- 2/2拍:每小节2个二分音符(= 4个四分音符)
+
+**用于布局计算:**
+```
+小节总时值 = beats / (beat-type / 4)  # 以四分音符为单位
+小节宽度 = 小节总时值 × 四分音符间距 + 左右padding
+```
+
+### 1.5 调号(Key Signature)
+
+```xml
+<attributes>
+  <key>
+    <fifths>0</fifths>    <!-- 五度圈位置:0=C大调,1=G大调,-1=F大调 -->
+    <mode>major</mode>    <!-- major或minor -->
+  </key>
+</attributes>
+```
+
+**简谱渲染注意:**
+- 简谱中调号决定了1=?(如1=C、1=G)
+- 固定调模式:需要显示升降号
+- 首调模式:不显示升降号,音符自动调整
+
+---
+
+## 🎵 第二部分:简谱记谱规范
+
+### 2.1 简谱基本元素
+
+**音符表示:**
+| 简谱 | 唱名 | 五线谱音名 | 说明 |
+|------|------|-----------|------|
+| 1 | do | C | 基本音级 |
+| 2 | re | D | |
+| 3 | mi | E | |
+| 4 | fa | F | |
+| 5 | sol | G | |
+| 6 | la | A | |
+| 7 | si | B | |
+| 0 | - | rest | 休止符 |
+
+**高低音点:**
+```
+高音:  ·         ··         ···
+       1         1          1
+     (高八度)  (高两八度)  (高三八度)
+
+中音:  1      (无点)
+
+低音:  1         1          1
+       ·         ··         ···
+     (低八度)  (低两八度)  (低三八度)
+```
+
+### 2.2 时值表示(增时线和减时线)⭐⭐⭐⭐⭐
+
+**核心规则:四分音符为基准**
+
+**长音符(≥四分音符)- 使用增时线:**
+```
+四分音符:    5        (无线,基准)
+附点四分音符: 5·       (附点)
+二分音符:    5 —      (1条增时线)
+附点二分音符: 5· —     (附点+增时线)
+全音符:      5 — — —  (3条增时线)
+```
+
+**增时线规则:**
+- 每条增时线代表**1个四分音符的时值**
+- 增时线应该**按时值均匀分布**在小节中(这是核心!)
+- 二分音符(2拍):音符本身占1拍,1条增时线占第2拍
+- 全音符(4拍):音符本身占1拍,3条增时线分别占第2、3、4拍
+
+**短音符(<四分音符)- 使用减时线(下划线):**
+```
+八分音符:     5        (1条下划线)
+               _
+
+十六分音符:   5        (2条下划线)
+              ==
+
+三十二分音符: 5        (3条下划线)
+              ≡≡
+```
+
+**减时线规则:**
+- 紧贴在音符下方
+- 每条减时线间距约2-3像素
+- 连续的相同时值音符,减时线应该连在一起(连音符)
+
+### 2.3 附点规则
+
+**单附点:**增加原时值的50%
+- 附点四分音符 = 1.5 × 四分音符
+- 附点二分音符 = 1.5 × 二分音符 = 3个四分音符
+
+**双附点:**增加原时值的75%
+- 双附点四分音符 = 1.75 × 四分音符
+
+**简谱中的附点:**
+```
+附点四分音符:  5·
+附点二分音符:  5· —
+附点八分音符:  5·
+                _
+```
+
+### 2.4 升降号
+
+**简谱中的升降号:**
+```
+升号:  #1  #5  (# 在音符左上方)
+降号:  ♭3  ♭7  (♭ 在音符左上方)
+还原号: ♮4      (♮ 在音符左上方)
+```
+
+**固定调 vs 首调:**
+- **首调**:1永远是do,升降号较少
+- **固定调**:1=C(固定),需要显示调号的所有升降号
+
+---
+
+## 🔧 第三部分:VexFlow底层技术
+
+### 3.1 VexFlow的Ticks系统 ⭐⭐⭐⭐⭐
+
+**核心常量:**
+```javascript
+Flow.RESOLUTION = 16384;  // VexFlow的基准分辨率
+TICKS_PER_QUARTER = 4096; // 1个四分音符 = 4096 ticks
+```
+
+**音符时值映射表:**
+| 音符类型 | VexFlow Ticks | 相对比例 | 简谱表示 |
+|---------|--------------|---------|---------|
+| 全音符 (whole) | 16384 | 4.0 | 5 — — — |
+| 二分音符 (half) | 8192 | 2.0 | 5 — |
+| 四分音符 (quarter) | 4096 | 1.0 | 5 |
+| 八分音符 (eighth) | 2048 | 0.5 | 5<br>_ |
+| 十六分音符 (16th) | 1024 | 0.25 | 5<br>== |
+| 三十二分音符 (32nd) | 512 | 0.125 | 5<br>≡≡ |
+
+**转换公式:**
+```javascript
+// MusicXML duration → VexFlow ticks
+vexflowTicks = (duration / divisions) × 4096
+
+// 示例(divisions=256):
+// 四分音符:256 / 256 × 4096 = 4096 ticks
+// 二分音符:512 / 256 × 4096 = 8192 ticks
+```
+
+### 3.2 VexFlow的音高系统
+
+**音符键(keys)格式:** `"note/octave"`
+```javascript
+// 示例
+"C/4"   // 中央C(C4)
+"A/4"   // A4
+"D/5"   // 高音D
+"F#/3"  // 升F,低八度
+"Bb/4"  // 降B
+```
+
+**转换为简谱:**
+```javascript
+const NOTE_TO_JIANPU = {
+  'C': 1,  // do
+  'D': 2,  // re
+  'E': 3,  // mi
+  'F': 4,  // fa
+  'G': 5,  // sol
+  'A': 6,  // la
+  'B': 7,  // si
+};
+
+// 八度偏移 = octave - 4
+// octave=4: 中音(无点)
+// octave=5: 高音(上点)
+// octave=3: 低音(下点)
+```
+
+### 3.3 VexFlow的Formatter工作原理
+
+**核心职责:**
+1. 收集小节中所有音符的时间戳(timestamp)
+2. 计算每个音符的最小所需宽度
+3. 根据小节总宽度,分配音符位置
+
+**关键方法:**
+```javascript
+// 1. preFormat(): 计算最小宽度
+formatter.preFormat();
+
+// 2. formatToStave(): 根据小节宽度分配位置
+formatter.formatToStave(voices, stave, { justifyWidth });
+
+// 3. 核心算法:
+// - Pass 1: 计算所有音符的最小宽度之和
+// - Pass 2: 将剩余空间按tick比例分配给音符
+```
+
+**我们的改进(固定时间比例):**
+```javascript
+// Pass 2改为:
+const pxPerTick = (justifyWidth - 2×padding) / totalTicks;
+音符X坐标 = measureStartX + padding + (音符timestamp × pxPerTick);
+```
+
+---
+
+## 📐 第四部分:MusicXML到简谱的映射规则
+
+### 4.1 音高转换 ⭐⭐⭐⭐⭐
+
+**步骤1:从MusicXML提取音高**
+```xml
+<pitch>
+  <step>A</step>     <!-- A音 -->
+  <alter>0</alter>   <!-- 无升降 -->
+  <octave>4</octave> <!-- 第4八度 -->
+</pitch>
+```
+
+**步骤2:转换为简谱**
+```javascript
+// 1. 确定调号(从<key>元素)
+const key = 'C';  // 1=C
+
+// 2. 音名转数字(在C大调中)
+const pitchNumber = NOTE_TO_JIANPU[step];  // A → 6
+
+// 3. 计算八度偏移
+const octaveOffset = octave - 4;  // 4 - 4 = 0(中音)
+
+// 4. 处理升降号
+const accidental = alter === 1 ? '#' : 
+                   alter === -1 ? '♭' : null;
+```
+
+**首调 vs 固定调:**
+```javascript
+// 首调:需要根据调号转调
+if (mode === 'shou') {
+  // 如果调号是G大调(1=G),需要转调
+  // G大调的A音实际是简谱的2(re)
+  pitchNumber = convertWithKey(step, key);
+}
+
+// 固定调:直接映射,但需要显示调号的升降号
+if (mode === 'fixed') {
+  // 保持C=1, D=2的固定映射
+  // 但需要显示调号规定的升降号
+}
+```
+
+### 4.2 时值转换 ⭐⭐⭐⭐⭐
+
+**从MusicXML计算实际时值:**
+```javascript
+// 从XML提取数据
+const duration = 384;     // MusicXML的duration
+const divisions = 256;    // 当前小节的divisions
+const hasDot = true;      // 是否有附点
+
+// 计算实际时值(以四分音符为单位)
+const realValue = duration / divisions;  // 384 / 256 = 1.5
+
+// 验证:附点四分音符应该是1.5
+// ✓ 正确!
+```
+
+**判断增时线/减时线:**
+```javascript
+if (realValue >= 1.0) {
+  // 长音符:绘制增时线
+  const numExtensionLines = Math.floor(realValue) - 1;
+  // 四分音符(1.0): 0条
+  // 二分音符(2.0): 1条
+  // 全音符(4.0): 3条
+  
+} else {
+  // 短音符:绘制减时线
+  const numUnderlines = Math.log2(4 / realValue);
+  // 四分音符(1.0): 0条
+  // 八分音符(0.5): 1条
+  // 十六分音符(0.25): 2条
+  // 三十二分音符(0.125): 3条
+}
+```
+
+**附点的影响:**
+```javascript
+// 附点四分音符
+realValue = 1.5;  // 基准是1.0,附点增加0.5
+numExtensionLines = Math.floor(1.5) - 1 = 0;  // 不画增时线!
+// 渲染为:5·  (数字+附点,无增时线)
+
+// 附点二分音符
+realValue = 3.0;  // 基准是2.0,附点增加1.0
+numExtensionLines = Math.floor(3.0) - 1 = 2;  // 2条增时线
+// 渲染为:5· — —
+```
+
+### 4.3 歌词转换
+
+```xml
+<lyric number="1">          <!-- 第1行歌词 -->
+  <syllabic>begin</syllabic> <!-- begin: 词首, middle: 词中, end: 词尾, single: 单字 -->
+  <text>美</text>
+</lyric>
+
+<lyric number="2">          <!-- 第2行歌词(多遍演唱) -->
+  <syllabic>single</syllabic>
+  <text>好</text>
+</lyric>
+```
+
+**简谱渲染要求:**
+- 歌词在音符正下方
+- 多行歌词垂直排列
+- 需要添加 `class="vf-lyric lyric{noteId}"` 用于高亮匹配
+- 需要添加 `lyricIndex` 属性用于多遍歌词匹配
+
+---
+
+## ⚠️ 第五部分:关键问题和陷阱
+
+### 5.1 Divisions陷阱 ⭐⭐⭐⭐⭐
+
+**问题:** 不同的MusicXML文件可能有不同的divisions值
+
+**示例:**
+```xml
+<!-- 文件A -->
+<divisions>256</divisions>
+<duration>256</duration>  <!-- 四分音符 -->
+
+<!-- 文件B -->
+<divisions>480</divisions>
+<duration>480</duration>  <!-- 同样是四分音符 -->
+
+<!-- 文件C -->
+<divisions>1</divisions>
+<duration>1</duration>    <!-- 还是四分音符! -->
+```
+
+**解决方案:**
+```javascript
+// ❌ 错误:直接使用duration
+if (duration === 256) { /* 四分音符 */ }  // 不可靠!
+
+// ✅ 正确:先转换为相对时值
+const realValue = duration / divisions;
+if (realValue === 1.0) { /* 四分音符 */ }  // 可靠!
+```
+
+### 5.2 音高转换陷阱
+
+**问题:** Step+Octave组合才能确定绝对音高
+
+**示例:**
+```xml
+<!-- 这两个是不同的音! -->
+<pitch>
+  <step>C</step>
+  <octave>4</octave>  <!-- 中央C -->
+</pitch>
+
+<pitch>
+  <step>C</step>
+  <octave>5</octave>  <!-- 高八度的C -->
+</pitch>
+```
+
+**转换为简谱:**
+```javascript
+// 都是"1",但八度不同
+pitch1 = { number: 1, octave: 0 };  // 中音 1
+pitch2 = { number: 1, octave: 1 };  // 高音 ·
+                                    //      1
+```
+
+### 5.3 休止符的时值
+
+**重要:** 休止符也有duration,占用时间!
+
+```xml
+<note>
+  <rest/>
+  <duration>512</duration>  <!-- 二分休止符 -->
+  <type>half</type>
+</note>
+```
+
+**简谱渲染:**
+- 休止符渲染为"0"
+- 占据相应的时值空间
+- **不绘制增时线**(即使是二分休止符)
+- 但需要在布局时占据相应的宽度
+
+### 5.4 连音符(Tuplet)
+
+```xml
+<note>
+  <pitch><step>C</step><octave>4</octave></pitch>
+  <duration>171</duration>  <!-- 不是标准时值! -->
+  <type>eighth</type>
+  <time-modification>
+    <actual-notes>3</actual-notes>  <!-- 3连音 -->
+    <normal-notes>2</normal-notes>  <!-- 占2个八分音符的时间 -->
+  </time-modification>
+</note>
+```
+
+**关键:**
+- duration已经调整过,可以直接使用
+- 需要在音符上方绘制"3"标记
+- 需要用弧线连接3个音符
+
+### 5.5 延音线(Tie)vs 连音线(Slur)
+
+**延音线(Tie):**
+- 连接**相同音高**的两个音符
+- 演奏时作为一个连续的音
+- MusicXML: `<tie type="start"/>` 和 `<tie type="stop"/>`
+
+**连音线(Slur):**
+- 连接**不同音高**的多个音符
+- 表示连贯演奏
+- MusicXML: `<slur type="start"/>` 和 `<slur type="stop"/>`
+
+**简谱渲染区别:**
+```
+延音线:  5  —  5    (两个5连在一起,演奏为一个长音)
+         └─────┘
+
+连音线:  5  3  1    (圆滑地演奏三个音)
+         ╰────╯
+```
+
+---
+
+## 🎯 第六部分:对任务清单的影响分析
+
+基于以上深入理解,我发现任务清单需要以下调整:
+
+### 需要新增的任务
+
+#### 📌 新增任务1.0:MusicXML元素解析规范文档
+**优先级:** P0(最高)  
+**位置:** 在任务1.1之前
+
+**内容:**
+- [ ] 创建MusicXML元素映射表
+  - [ ] divisions转换规则
+  - [ ] pitch元素的完整解析
+  - [ ] duration计算公式
+  - [ ] type类型映射
+  - [ ] 休止符识别
+  - [ ] 附点处理
+  - [ ] 升降号处理
+  - [ ] 连音符处理
+  - [ ] 延音线处理
+  - [ ] 歌词处理
+- [ ] 创建简谱渲染规则表
+  - [ ] 增时线数量计算公式
+  - [ ] 减时线数量计算公式
+  - [ ] 高低音点数量计算
+  - [ ] 附点位置规则
+
+**为什么需要:**
+- 没有这个文档,解析器开发会反复试错
+- 需要标准化各种edge case的处理方式
+- 为后续开发提供明确的规范
+
+---
+
+#### 📌 新增任务1.1.1:Divisions处理器
+**优先级:** P0  
+**位置:** 在任务1.1之中
+
+**原因:** divisions是时值计算的基础,必须单独处理
+
+**内容:**
+- [ ] 实现divisions缓存机制(每个小节可能不同)
+- [ ] 实现duration到实际时值的转换
+- [ ] 处理divisions为0或null的异常情况
+- [ ] 编写单元测试验证转换准确性
+
+---
+
+#### 📌 新增任务3.2.1:增时线精确布局算法
+**优先级:** P1  
+**位置:** 在任务3.2中细化
+
+**原因:** 增时线的渲染是简谱的核心难点
+
+**内容:**
+- [ ] 实现增时线位置计算(按四分音符时值分布)
+- [ ] 实现增时线与下一音符的间距计算
+- [ ] 处理附点+增时线的组合情况
+- [ ] 处理小节末尾增时线的特殊情况
+- [ ] 多声部场景下增时线的垂直对齐
+
+**算法要点:**
+```javascript
+// 二分音符(2拍)
+音符本身位置:x = measureX + padding + timestamp × pxPerQuarter
+第1条增时线位置:x1 = 音符位置 + 1 × pxPerQuarter
+增时线长度:pxPerQuarter × 0.7  // 占空间的70%
+
+// 全音符(4拍)
+第1条增时线:x1 = 音符位置 + 1 × pxPerQuarter
+第2条增时线:x2 = 音符位置 + 2 × pxPerQuarter
+第3条增时线:x3 = 音符位置 + 3 × pxPerQuarter
+```
+
+---
+
+#### 📌 新增任务4.1.1:state.times数组字段完整性检查
+**优先级:** P0  
+**位置:** 在任务4.1中
+
+**内容:**
+- [ ] 列出state.times的所有必需字段(从业务代码中提取)
+- [ ] 创建字段检查清单
+- [ ] 为每个字段编写生成逻辑
+- [ ] 编写验证测试
+
+**必需字段列表(至少30+个):**
+```typescript
+{
+  i, noteId, id,                    // 标识
+  time, endtime, relativeTime,      // 时间
+  MeasureNumberXML, measureListIndex, // 小节
+  svgElement, noteElement,          // 对象引用
+  bbox,                             // 位置
+  halfTone, frequency,              // 音高
+  isRestFlag, realValue,            // 属性
+  formatLyricsEntries,              // 歌词
+  // ... 还有20+个字段
+}
+```
+
+---
+
+### 需要调整的任务
+
+#### 🔄 调整任务2.1:增加边界情况处理
+
+**原任务:** 实现小节布局计算器  
+**调整为:**
+- [x] 实现固定时间比例算法
+- [ ] **新增:处理divisions为0或异常的情况**
+- [ ] **新增:处理变拍(中途改变拍号)的情况**
+- [ ] **新增:处理不完整小节(弱起小节)**
+- [ ] **新增:处理超长音符(跨小节)的情况**
+
+---
+
+#### 🔄 调整任务3.2:增加特殊情况
+
+**原任务:** 实现线条绘制  
+**调整为:**
+- [x] 实现增时线绘制
+- [x] 实现减时线绘制
+- [ ] **新增:处理附点+增时线组合**
+- [ ] **新增:处理增时线跨小节的情况(如果音符跨小节)**
+- [ ] **新增:处理连音符的减时线(不应该单独绘制)**
+- [ ] **新增:处理极短音符(64分、128分音符)的减时线**
+
+---
+
+### 需要补充的测试任务
+
+#### 📌 新增任务5.1.1:MusicXML边界情况测试
+
+**内容:**
+- [ ] 测试不同divisions值(1, 256, 480, 960等)
+- [ ] 测试极端时值(全音符、128分音符)
+- [ ] 测试附点音符(单附点、双附点)
+- [ ] 测试变拍(中途改变拍号)
+- [ ] 测试不完整小节
+- [ ] 测试跨小节音符
+- [ ] 测试零duration音符(装饰音)
+
+---
+
+## 📊 调整后的任务优先级
+
+### 必须优先完成(P0):
+1. **任务1.0**:MusicXML元素解析规范文档(新增)
+2. **任务1.1.1**:Divisions处理器(新增)
+3. **任务1.1**:OSMD数据解析器
+4. **任务3.2.1**:增时线精确布局算法(新增细化)
+5. **任务4.1.1**:state.times字段完整性检查(新增)
+
+### 高优先级(P1):
+- 所有原有任务
+
+### 可延后(P2):
+- 装饰音、颤音等高级记号
+- 导出功能
+- 性能优化(初期可接受较低性能)
+
+---
+
+## 🎯 建议的开发顺序调整
+
+**原计划:**
+阶段1(解析器)→ 阶段2(布局)→ 阶段3(绘制)→ 阶段4(兼容层)→ 阶段5(测试)
+
+**建议调整为:**
+
+1. **阶段0**:准备工作
+2. **阶段0.5(新增)**:创建规范文档
+   - 任务1.0:MusicXML解析规范
+   - 任务1.0.1:简谱渲染规范
+3. **阶段1**:核心解析器(调整)
+   - **先做**任务1.1.1:Divisions处理器
+   - 再做任务1.1:OSMD数据解析器
+   - 最后任务1.2:时间计算器
+4. **阶段2-5**:保持不变
+
+---
+
+## 📝 总结
+
+**核心发现:**
+
+1. ✅ **Divisions是时值计算的基础**,必须优先处理
+2. ✅ **增时线的精确布局**是简谱渲染的核心难点
+3. ✅ **state.times字段非常多**,需要完整的字段清单
+4. ✅ **需要处理大量边界情况**(变拍、不完整小节、跨小节等)
+
+**建议调整:**
+
+1. **新增阶段0.5**:创建规范文档(预计1-2天)
+2. **新增4个高优先级任务**
+3. **调整2个现有任务**,增加边界情况处理
+4. **总开发时间调整为9-11周**(增加1周用于规范文档和边界情况)
+
+**下一步行动:**
+
+您需要决定:
+1. 是否接受调整后的任务清单?
+2. 是否需要我立即创建"MusicXML解析规范文档"?
+3. 是否需要我更新 `JIANPU_TASKS_CHECKLIST.md`?
+
+等您确认后,我会更新任务清单,然后我们可以正式开始开发!

+ 33 - 0
docs/jianpu-renderer/04-MUSICXML_MAPPING.md

@@ -0,0 +1,33 @@
+# MusicXML元素映射规范
+
+> **文档状态:** ⏸️ 待完成(阶段0.5任务0.5.1的产出)  
+> **创建日期:** 2026-01-29  
+> **用途:** 定义MusicXML标签到简谱的完整映射规则
+
+---
+
+## 📋 待填充内容
+
+这个文档将在**阶段0.5 - 任务0.5.1**中完成,包含:
+
+1. **Divisions转换规则**
+   - divisions的定义和作用
+   - duration到实际时值的转换公式
+   - 不同divisions值的处理
+   
+2. **音高元素解析规则**
+   - step → 简谱数字映射
+   - octave → 高低音点计算
+   - alter → 升降号处理
+   
+3. **时值元素解析规则**
+   - type → 音符基准类型
+   - dot → 附点时值计算
+   - duration → 实际时值
+   
+4. **特殊元素处理规则**
+   - grace, rest, chord, tuplet, tie等
+
+---
+
+**下一步:** 完成任务0.3后,开始编写此文档

+ 32 - 0
docs/jianpu-renderer/05-VEXFLOW_COMPAT.md

@@ -0,0 +1,32 @@
+# VexFlow兼容性规范
+
+> **文档状态:** ⏸️ 待完成(阶段0.5任务0.5.3的产出)  
+> **创建日期:** 2026-01-29  
+> **用途:** 定义与VexFlow/OSMD的完整兼容规范
+
+---
+
+## 📋 待填充内容
+
+这个文档将在**阶段0.5 - 任务0.5.3**中完成,包含:
+
+1. **DOM结构规范**
+   - 元素ID命名规则
+   - CSS类名完整清单
+   - 元素层级结构
+   
+2. **state.times字段完整清单** (核心!)
+   - 从业务代码提取的所有字段(30-40个)
+   - 每个字段的类型、来源、用途
+   
+3. **cursor接口规范**
+   - Iterator对象必需方法
+   - 行为定义
+   
+4. **GraphicSheet接口规范**
+   - MeasureList结构
+   - 必需字段
+
+---
+
+**下一步:** 完成任务0.5.2后,开始编写此文档

+ 35 - 0
docs/jianpu-renderer/06-RENDER_SPEC.md

@@ -0,0 +1,35 @@
+# 简谱渲染细节规范
+
+> **文档状态:** ⏸️ 待完成(阶段0.5任务0.5.2的产出)  
+> **创建日期:** 2026-01-29  
+> **用途:** 定义简谱各元素的精确渲染规则
+
+---
+
+## 📋 待填充内容
+
+这个文档将在**阶段0.5 - 任务0.5.2**中完成,包含:
+
+1. **增时线渲染规范**(核心难点!)
+   - 数量计算公式
+   - 位置计算(按时值分配空间)
+   - 长度和样式
+   - 多声部对齐要求
+   
+2. **减时线渲染规范**
+   - 数量计算公式
+   - 位置和间距
+   - 连续音符处理
+   
+3. **高低音点规范**
+   - 位置和大小
+   - 多个点的排列
+   
+4. **附点、升降号等其他元素规范**
+
+5. **视觉尺寸标准**
+   - 所有元素的标准尺寸
+
+---
+
+**下一步:** 完成任务0.5.1后,开始编写此文档

+ 140 - 0
docs/jianpu-renderer/HOW_TO_UPDATE_PROGRESS.md

@@ -0,0 +1,140 @@
+# 如何更新进度页面
+
+> **进度页面:** `progress.html`  
+> **更新频率:** 每完成一个任务后更新
+
+---
+
+## 📖 使用方法
+
+### 查看进度页面
+
+**方法1:直接在浏览器中打开**
+```bash
+# 在文件管理器中双击
+docs/jianpu-renderer/progress.html
+```
+
+**方法2:通过VSCode打开**
+```bash
+# 右键文件 → Open with Live Server
+# 或者在VSCode中安装Live Server扩展后预览
+```
+
+---
+
+## 🔧 如何更新进度
+
+### 方法1:手动编辑HTML(简单)
+
+打开 `progress.html`,找到对应的任务,修改状态:
+
+**修改任务状态:**
+```html
+<!-- 将待开始改为进行中 -->
+<div class="task pending">  ← 改为 in-progress
+    ...
+    <div class="task-status pending">待开始</div>  ← 改为 in-progress 和 进行中
+</div>
+
+<!-- 将进行中改为已完成 -->
+<div class="task in-progress">  ← 改为 completed
+    ...
+    <div class="task-status in-progress">进行中</div>  ← 改为 completed 和 已完成
+</div>
+```
+
+**修改阶段进度:**
+```html
+<div class="stage-progress">0%</div>  ← 改为实际百分比,如 50%
+```
+
+**修改整体进度:**
+```html
+<!-- 在统计卡片中 -->
+<div class="stat-value">10%</div>  ← 改为新的百分比
+
+<!-- 在进度条中 -->
+<div class="progress-bar" style="width: 10%;">10%</div>  ← 改为新的百分比
+
+<!-- 在已完成任务数中 -->
+<div class="stat-value">6/60+</div>  ← 改为新的数量
+```
+
+---
+
+### 方法2:使用JavaScript更新(高级)
+
+在浏览器控制台中运行:
+
+```javascript
+// 更新阶段进度
+updateProgress(0, 75);  // 阶段0进度设为75%
+updateProgress(1, 25);  // 阶段0.5进度设为25%
+
+// 刷新页面查看效果
+```
+
+---
+
+## 📊 进度计算公式
+
+### 阶段进度
+```
+阶段进度 = (已完成任务数 / 该阶段总任务数) × 100%
+
+示例:
+阶段0有5个任务,完成了4个
+阶段0进度 = (4 / 5) × 100% = 80%
+```
+
+### 整体进度
+```
+整体进度 = (已完成总任务数 / 所有任务总数) × 100%
+
+当前:
+整体进度 = (6 / 60) × 100% ≈ 10%
+```
+
+---
+
+## 🎨 页面功能
+
+### 交互功能
+- ✅ 点击阶段标题可展开/折叠任务列表
+- ✅ 按 `E` 键展开所有阶段
+- ✅ 按 `C` 键折叠所有阶段
+- ✅ 鼠标悬停卡片有动画效果
+
+### 状态标识
+- 🚧 进行中(黄色)
+- ✅ 已完成(绿色)
+- ⏸️ 待开始(灰色)
+
+---
+
+## 📝 更新检查清单
+
+**每次完成任务后:**
+- [ ] 打开 `progress.html`
+- [ ] 找到对应的任务
+- [ ] 修改状态为"completed"
+- [ ] 更新阶段进度百分比
+- [ ] 更新整体进度
+- [ ] 保存文件
+- [ ] 刷新浏览器查看效果
+
+---
+
+## 💡 提示
+
+1. **及时更新**:每完成一个任务就更新,保持进度同步
+2. **截图记录**:定期截图保存进度状态
+3. **版本控制**:进度页面也要提交到Git
+4. **团队共享**:部署到内网服务器供团队查看
+
+---
+
+**快捷键:**
+- `E` - Expand(展开所有阶段)
+- `C` - Collapse(折叠所有阶段)

+ 230 - 0
docs/jianpu-renderer/PROJECT_SUMMARY.md

@@ -0,0 +1,230 @@
+# 🎼 简谱渲染引擎重写项目 - 总结文档
+
+> **项目启动:** 2026-01-29  
+> **当前状态:** 准备阶段完成75%  
+> **下一步:** 完成测试环境准备,进入规范文档编写阶段
+
+---
+
+## ✅ 第一天完成的工作(2026-01-29)
+
+### 1. 建立完整的项目结构
+**代码文件:** 29个
+```
+src/jianpu-renderer/
+├── models/           (5个文件) - 数据模型定义
+├── core/
+│   ├── parser/       (3个文件) - 解析器框架
+│   ├── layout/       (5个文件) - 布局引擎框架
+│   ├── drawer/       (5个文件) - 绘制引擎框架
+│   └── config/       (2个文件) - 配置管理
+├── adapters/         (3个文件) - 兼容层框架
+├── utils/            (4个文件) - 工具函数
+└── __tests__/        (1个文件) - 测试框架
+```
+
+### 2. 建立完整的文档体系
+**文档文件:** 8个(~2000行)
+```
+docs/jianpu-renderer/
+├── 00-START_HERE.md          ✅ 新手入门指南
+├── 01-TASKS_CHECKLIST.md     ✅ 完整任务清单(60+任务)
+├── 02-PROGRESS.md            ✅ 开发进度追踪
+├── 03-MUSICXML_KNOWLEDGE.md  ✅ MusicXML核心知识
+├── 04-MUSICXML_MAPPING.md    ⏸️ 待完成(阶段0.5)
+├── 05-VEXFLOW_COMPAT.md      ⏸️ 待完成(阶段0.5)
+├── 06-RENDER_SPEC.md         ⏸️ 待完成(阶段0.5)
+└── README.md                 ✅ 文档中心导航
+```
+
+### 3. 深入学习并整理核心知识
+**知识文档:** `03-MUSICXML_KNOWLEDGE.md`(300+行)
+- ✅ MusicXML元素和属性详解
+- ✅ Divisions转换机制
+- ✅ VexFlow Ticks系统
+- ✅ 简谱增时线/减时线规范
+- ✅ 音高转换规则
+- ✅ 常见陷阱和解决方案
+
+### 4. 调整并优化开发计划
+- ✅ 从8周调整为10周(更稳妥)
+- ✅ 新增阶段0.5:规范文档编写
+- ✅ 细化关键任务(20+个新增子任务)
+- ✅ 明确每个任务的验收标准
+
+---
+
+## 🎯 核心成果
+
+### 成果1:清晰的开发路线图
+**10周开发计划:**
+1. 第1周:阶段0 - 准备工作(75%完成)
+2. 第2周:阶段0.5 - 规范文档编写(新增)⭐
+3. 第3周:阶段1 - 核心解析器
+4. 第4-5周:阶段2 - 布局引擎
+5. 第6-7周:阶段3 - 绘制引擎
+6. 第8周:阶段4 - 兼容层实现
+7. 第9-10周:阶段5 - 测试与优化
+
+### 成果2:完整的任务分解
+- **总任务数:** 60+个任务
+- **每个任务都有:**
+  - 明确的目标和步骤
+  - 验收标准和测试用例
+  - 预计时间和优先级
+  - 代码模板和算法示例
+
+### 成果3:知识库建立
+- MusicXML核心概念
+- VexFlow技术要点
+- 简谱渲染规范
+- 业务功能依赖分析
+- 兼容性策略
+
+### 成果4:可持续的开发流程
+- 每次开发都有明确的起点和终点
+- 进度可追踪,任务可验收
+- 即使间断多次也能无缝继续
+- 文档驱动,减少重复讨论
+
+---
+
+## 📊 项目现状
+
+### 整体进度:10%
+```
+████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
+已完成    │  进行中  │              待完成(约9周工作量)
+```
+
+### 已完成的里程碑
+- ✅ Checkpoint 0.1:项目结构搭建
+- ✅ Checkpoint 0.2:数据模型定义
+- ✅ Checkpoint 0.3:文档体系建立
+- ✅ Checkpoint 0.4:知识学习完成
+
+### 下一个里程碑
+- 🎯 Checkpoint 0.5:阶段0完成(第1周结束)
+  - 剩余任务:任务0.3(测试环境准备)
+  - 预计完成时间:0.5天
+
+---
+
+## 🔑 关键发现
+
+### 发现1:Divisions是时值计算的基石
+- MusicXML使用divisions定义时值单位
+- 必须优先实现divisions处理器
+- 影响所有音符的时值计算
+
+### 发现2:增时线渲染是简谱核心难点
+- 不是简单的视觉元素
+- 必须按时值占据空间
+- 多声部场景下需要垂直对齐
+- 需要专门的算法(任务3.2.1)
+
+### 发现3:业务兼容层工作量被低估
+- state.times包含30-40个字段
+- 需要完整的字段清单和映射逻辑
+- DOM结构必须完全兼容VexFlow
+- 需要新增任务4.0(字段完整性文档)
+
+### 发现4:规范文档是成功的关键
+- 先定义规范再写代码,可大幅减少返工
+- 规范可作为代码审查和测试的标准
+- 新增阶段0.5专门用于编写规范
+
+---
+
+## 💡 采纳的建议和决策
+
+### 决策1:接受10周开发计划
+- **原计划:** 8周
+- **调整为:** 10周
+- **理由:** 更稳妥,预留规范编写和测试优化时间
+
+### 决策2:新增阶段0.5
+- **内容:** 规范文档编写
+- **时间:** 1周
+- **价值:** 为后续8周开发提供明确标准
+
+### 决策3:创建统一文档目录
+- **位置:** `docs/jianpu-renderer/`
+- **编号:** 00-10的文档编号体系
+- **价值:** 便于管理和查找
+
+### 决策4:文档优先策略
+- 先写规范,再写代码
+- 规范驱动开发
+- 降低返工风险
+
+---
+
+## 🚀 下一步行动
+
+### 立即行动(今天或明天)
+1. **完成任务0.3** - 创建测试数据和环境(0.5天)
+   - 从osmd-extended/test/data/选择5个XML文件
+   - 创建对比测试页面
+   - 配置测试框架
+
+2. **完成阶段0** - 第1周结束时验收
+   - 确认所有准备工作完成
+   - 准备进入阶段0.5
+
+### 下周行动(第2周)
+3. **阶段0.5:编写规范文档**
+   - 任务0.5.1:MusicXML映射规范(2天)
+   - 任务0.5.2:简谱渲染规范(1天)
+   - 任务0.5.3:VexFlow兼容性规范(1.5天)
+   - 任务0.5.4:创建测试基准(1.5天)
+
+### 第3周开始编码
+4. **阶段1:核心解析器**
+   - 有了规范文档,开发效率会大幅提升
+   - 减少讨论和返工
+   - 代码质量更高
+
+---
+
+## 📖 使用指南
+
+### 每次开发时
+1. 打开 `docs/jianpu-renderer/00-START_HERE.md`
+2. 按照标准流程进行开发
+3. 更新进度和任务状态
+4. 提交代码和文档
+
+### 遇到问题时
+1. 查阅 `03-MUSICXML_KNOWLEDGE.md`
+2. 查阅对应的规范文档(04-06)
+3. 查看任务清单的详细说明
+
+### 需要了解进度时
+1. 打开 `02-PROGRESS.md`
+2. 查看当前状态和下一步
+3. 查看里程碑进度
+
+---
+
+## 🎉 总结
+
+**今天完成了什么:**
+- ✅ 搭建了完整的代码框架(29个文件)
+- ✅ 建立了完善的文档体系(8个文档)
+- ✅ 深入学习了MusicXML和VexFlow
+- ✅ 调整了开发计划为10周
+- ✅ 细化了所有关键任务
+
+**项目现状:**
+- 📊 整体进度:10%
+- 🎯 当前任务:任务0.3 - 创建测试数据和环境
+- ⏰ 预计完成:2026-04-09(10周后)
+
+**成功的基础已经打好!** 🚀
+
+---
+
+**文档版本:** v1.0  
+**最后更新:** 2026-01-29  
+**维护者:** 开发团队

+ 136 - 0
docs/jianpu-renderer/README.md

@@ -0,0 +1,136 @@
+# 简谱渲染引擎重写 - 文档中心
+
+> **项目:** 简谱渲染引擎完全重写  
+> **开始日期:** 2026-01-29  
+> **预计完成:** 10周  
+> **状态:** 🚧 进行中 - 阶段0
+
+---
+
+## 📚 文档导航
+
+### 核心文档(必读)
+
+1. **[01-TASKS_CHECKLIST.md](./01-TASKS_CHECKLIST.md)** - 📋 完整任务清单
+   - 所有开发任务的详细分解
+   - 每个任务的验收标准
+   - 预计时间和优先级
+   - **每次开发必看!**
+
+2. **[02-PROGRESS.md](./02-PROGRESS.md)** - 📊 开发进度追踪
+   - 当前进度和状态
+   - 已完成任务记录
+   - 里程碑进度
+   - 开发日志
+   - **每次开发前后都要更新!**
+
+3. **[03-MUSICXML_KNOWLEDGE.md](./03-MUSICXML_KNOWLEDGE.md)** - 📚 MusicXML核心知识
+   - MusicXML元素和属性详解
+   - 简谱渲染规范
+   - VexFlow技术要点
+   - 常见陷阱和解决方案
+   - **开发前必读,遇到问题随时查阅!**
+
+### 规范文档(开发中创建)
+
+4. **[04-MUSICXML_MAPPING.md](./04-MUSICXML_MAPPING.md)** - 🗺️ MusicXML元素映射表
+   - MusicXML标签到简谱的映射规则
+   - 完整的转换算法
+   - 边界情况处理
+   - **阶段0.5任务0.5.1的产出**
+
+5. **[05-VEXFLOW_COMPAT.md](./05-VEXFLOW_COMPAT.md)** - 🔗 VexFlow兼容性规范
+   - DOM结构规范
+   - CSS类名规范
+   - state.times字段完整清单
+   - cursor接口规范
+   - **阶段0.5任务0.5.3的产出**
+
+6. **[06-RENDER_SPEC.md](./06-RENDER_SPEC.md)** - 🎨 简谱渲染细节规范
+   - 增时线精确布局算法
+   - 减时线绘制规范
+   - 多声部对齐规则
+   - 视觉效果标准
+   - **阶段0.5任务0.5.2的产出**
+
+### API和设计文档(开发中完善)
+
+7. **[07-API_REFERENCE.md](./07-API_REFERENCE.md)** - 📖 API参考文档
+   - JianpuRenderer API
+   - 各个模块的公共接口
+   - 使用示例
+
+8. **[08-ARCHITECTURE.md](./08-ARCHITECTURE.md)** - 🏗️ 架构设计文档
+   - 模块划分
+   - 数据流
+   - 接口设计
+
+### 测试和发布文档
+
+9. **[09-TESTING_GUIDE.md](./09-TESTING_GUIDE.md)** - 🧪 测试指南
+   - 测试策略
+   - 测试用例列表
+   - 测试工具使用
+
+10. **[10-CHANGELOG.md](./10-CHANGELOG.md)** - 📝 变更日志
+    - 版本历史
+    - 功能更新记录
+    - Bug修复记录
+
+---
+
+## 🎯 文档使用指南
+
+### 每次开发时的标准流程
+
+**开始前(5分钟):**
+1. 打开 `02-PROGRESS.md` 查看当前状态
+2. 打开 `01-TASKS_CHECKLIST.md` 找到下一个任务
+3. 如有疑问,查阅 `03-MUSICXML_KNOWLEDGE.md`
+
+**开发中:**
+1. 参考规范文档(04-06)进行开发
+2. 遇到问题查阅知识文档
+3. 频繁提交代码
+
+**结束时(5分钟):**
+1. 更新 `01-TASKS_CHECKLIST.md` 标记完成的任务
+2. 更新 `02-PROGRESS.md` 记录进度和日志
+3. 提交所有更改
+
+---
+
+## 📊 文档状态
+
+| 文档 | 状态 | 负责人 | 最后更新 |
+|------|------|--------|---------|
+| 01-TASKS_CHECKLIST.md | ✅ 已完成 | AI | 2026-01-29 |
+| 02-PROGRESS.md | ✅ 已完成 | AI | 2026-01-29 |
+| 03-MUSICXML_KNOWLEDGE.md | ✅ 已完成 | AI | 2026-01-29 |
+| 04-MUSICXML_MAPPING.md | ⏸️ 待创建 | - | - |
+| 05-VEXFLOW_COMPAT.md | ⏸️ 待创建 | - | - |
+| 06-RENDER_SPEC.md | ⏸️ 待创建 | - | - |
+| 07-API_REFERENCE.md | ⏸️ 待创建 | - | - |
+| 08-ARCHITECTURE.md | ⏸️ 待创建 | - | - |
+| 09-TESTING_GUIDE.md | ⏸️ 待创建 | - | - |
+| 10-CHANGELOG.md | ⏸️ 待创建 | - | - |
+
+---
+
+## 🔗 相关链接
+
+### 项目代码
+- [简谱渲染器代码](../../src/jianpu-renderer/)
+- [业务代码](../../src/view/music-score/)
+- [OSMD扩展](../../osmd-extended/)
+
+### 外部资源
+- [MusicXML 4.0官方文档](https://www.w3.org/2021/06/musicxml40/)
+- [VexFlow官网](https://www.vexflow.com/)
+- [OpenSheetMusicDisplay](https://opensheetmusicdisplay.github.io/)
+
+---
+
+**文档版本:** v1.0  
+**维护者:** 开发团队  
+**最后更新:** 2026-01-29

+ 922 - 0
docs/jianpu-renderer/progress.html

@@ -0,0 +1,922 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>简谱渲染引擎重写 - 开发进度仪表板</title>
+    <style>
+        * {
+            margin: 0;
+            padding: 0;
+            box-sizing: border-box;
+        }
+
+        body {
+            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
+            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+            min-height: 100vh;
+            padding: 20px;
+        }
+
+        .container {
+            max-width: 1400px;
+            margin: 0 auto;
+            background: white;
+            border-radius: 20px;
+            box-shadow: 0 20px 60px rgba(0,0,0,0.3);
+            overflow: hidden;
+        }
+
+        .header {
+            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+            color: white;
+            padding: 40px;
+            text-align: center;
+        }
+
+        .header h1 {
+            font-size: 2.5em;
+            margin-bottom: 10px;
+            font-weight: 700;
+        }
+
+        .header p {
+            font-size: 1.2em;
+            opacity: 0.9;
+        }
+
+        .stats {
+            display: grid;
+            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+            gap: 20px;
+            padding: 30px;
+            background: #f8f9fa;
+        }
+
+        .stat-card {
+            background: white;
+            padding: 20px;
+            border-radius: 12px;
+            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+            text-align: center;
+            transition: transform 0.3s ease;
+        }
+
+        .stat-card:hover {
+            transform: translateY(-5px);
+            box-shadow: 0 8px 12px rgba(0,0,0,0.15);
+        }
+
+        .stat-value {
+            font-size: 2.5em;
+            font-weight: 700;
+            color: #667eea;
+            margin-bottom: 5px;
+        }
+
+        .stat-label {
+            color: #666;
+            font-size: 0.9em;
+        }
+
+        .main-progress {
+            padding: 40px;
+            background: white;
+        }
+
+        .progress-header {
+            margin-bottom: 20px;
+        }
+
+        .progress-title {
+            font-size: 1.5em;
+            color: #333;
+            margin-bottom: 10px;
+        }
+
+        .progress-bar-container {
+            background: #e9ecef;
+            height: 40px;
+            border-radius: 20px;
+            overflow: hidden;
+            position: relative;
+            margin-bottom: 10px;
+        }
+
+        .progress-bar {
+            height: 100%;
+            background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
+            transition: width 0.5s ease;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            color: white;
+            font-weight: 600;
+            font-size: 1.1em;
+        }
+
+        .progress-details {
+            display: flex;
+            justify-content: space-between;
+            color: #666;
+            font-size: 0.9em;
+            margin-top: 5px;
+        }
+
+        .stages {
+            padding: 40px;
+            background: white;
+        }
+
+        .stage {
+            margin-bottom: 30px;
+            border: 2px solid #e9ecef;
+            border-radius: 12px;
+            overflow: hidden;
+            transition: all 0.3s ease;
+        }
+
+        .stage:hover {
+            border-color: #667eea;
+            box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
+        }
+
+        .stage-header {
+            background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
+            padding: 20px;
+            cursor: pointer;
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+        }
+
+        .stage-header.completed {
+            background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%);
+        }
+
+        .stage-header.in-progress {
+            background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%);
+        }
+
+        .stage-info {
+            display: flex;
+            align-items: center;
+            gap: 15px;
+        }
+
+        .stage-status {
+            width: 30px;
+            height: 30px;
+            border-radius: 50%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            font-weight: bold;
+            color: white;
+        }
+
+        .status-pending {
+            background: #6c757d;
+        }
+
+        .status-progress {
+            background: #ffc107;
+        }
+
+        .status-completed {
+            background: #28a745;
+        }
+
+        .stage-title {
+            font-size: 1.3em;
+            font-weight: 600;
+            color: #333;
+        }
+
+        .stage-meta {
+            display: flex;
+            gap: 20px;
+            align-items: center;
+        }
+
+        .stage-progress {
+            font-size: 1.2em;
+            font-weight: 600;
+            color: #667eea;
+        }
+
+        .stage-time {
+            color: #666;
+            font-size: 0.9em;
+        }
+
+        .stage-body {
+            padding: 20px;
+            display: none;
+        }
+
+        .stage.expanded .stage-body {
+            display: block;
+        }
+
+        .task {
+            padding: 15px;
+            margin-bottom: 10px;
+            background: #f8f9fa;
+            border-radius: 8px;
+            border-left: 4px solid #e9ecef;
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            transition: all 0.3s ease;
+        }
+
+        .task:hover {
+            background: #e9ecef;
+            transform: translateX(5px);
+        }
+
+        .task.completed {
+            border-left-color: #28a745;
+            background: #d4edda;
+        }
+
+        .task.in-progress {
+            border-left-color: #ffc107;
+            background: #fff3cd;
+        }
+
+        .task-info {
+            flex: 1;
+        }
+
+        .task-title {
+            font-weight: 600;
+            color: #333;
+            margin-bottom: 5px;
+        }
+
+        .task-meta {
+            font-size: 0.85em;
+            color: #666;
+        }
+
+        .task-status {
+            padding: 6px 12px;
+            border-radius: 20px;
+            font-size: 0.85em;
+            font-weight: 600;
+            white-space: nowrap;
+        }
+
+        .task-status.completed {
+            background: #28a745;
+            color: white;
+        }
+
+        .task-status.in-progress {
+            background: #ffc107;
+            color: #333;
+        }
+
+        .task-status.pending {
+            background: #6c757d;
+            color: white;
+        }
+
+        .timeline {
+            padding: 40px;
+            background: #f8f9fa;
+        }
+
+        .timeline-title {
+            font-size: 1.5em;
+            color: #333;
+            margin-bottom: 30px;
+            text-align: center;
+        }
+
+        .timeline-container {
+            position: relative;
+            padding-left: 40px;
+        }
+
+        .timeline-line {
+            position: absolute;
+            left: 15px;
+            top: 0;
+            bottom: 0;
+            width: 4px;
+            background: #dee2e6;
+        }
+
+        .timeline-item {
+            position: relative;
+            margin-bottom: 30px;
+            padding-left: 30px;
+        }
+
+        .timeline-dot {
+            position: absolute;
+            left: -33px;
+            top: 5px;
+            width: 20px;
+            height: 20px;
+            border-radius: 50%;
+            background: white;
+            border: 4px solid #6c757d;
+        }
+
+        .timeline-dot.completed {
+            border-color: #28a745;
+            background: #28a745;
+        }
+
+        .timeline-dot.in-progress {
+            border-color: #ffc107;
+            background: #ffc107;
+        }
+
+        .timeline-content {
+            background: white;
+            padding: 20px;
+            border-radius: 8px;
+            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+        }
+
+        .timeline-date {
+            color: #667eea;
+            font-weight: 600;
+            margin-bottom: 5px;
+        }
+
+        .timeline-text {
+            color: #333;
+        }
+
+        .footer {
+            background: #2c3e50;
+            color: white;
+            padding: 30px;
+            text-align: center;
+        }
+
+        .footer-links {
+            display: flex;
+            justify-content: center;
+            gap: 30px;
+            margin-top: 15px;
+        }
+
+        .footer-link {
+            color: #3498db;
+            text-decoration: none;
+            transition: color 0.3s;
+        }
+
+        .footer-link:hover {
+            color: #5dade2;
+        }
+
+        @media (max-width: 768px) {
+            .stats {
+                grid-template-columns: repeat(2, 1fr);
+            }
+            
+            .header h1 {
+                font-size: 1.8em;
+            }
+            
+            .stage-meta {
+                flex-direction: column;
+                align-items: flex-start;
+                gap: 5px;
+            }
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <!-- 头部 -->
+        <div class="header">
+            <h1>🎼 简谱渲染引擎重写</h1>
+            <p>开发进度仪表板 - 实时追踪项目状态</p>
+        </div>
+
+        <!-- 统计卡片 -->
+        <div class="stats">
+            <div class="stat-card">
+                <div class="stat-value">10%</div>
+                <div class="stat-label">总体进度</div>
+            </div>
+            <div class="stat-card">
+                <div class="stat-value">6/60+</div>
+                <div class="stat-label">已完成任务</div>
+            </div>
+            <div class="stat-card">
+                <div class="stat-value">10周</div>
+                <div class="stat-label">预计周期</div>
+            </div>
+            <div class="stat-card">
+                <div class="stat-value">29</div>
+                <div class="stat-label">代码文件</div>
+            </div>
+            <div class="stat-card">
+                <div class="stat-value">9</div>
+                <div class="stat-label">文档文件</div>
+            </div>
+            <div class="stat-card">
+                <div class="stat-value">阶段0</div>
+                <div class="stat-label">当前阶段</div>
+            </div>
+        </div>
+
+        <!-- 主进度条 -->
+        <div class="main-progress">
+            <div class="progress-header">
+                <div class="progress-title">📊 整体进度</div>
+            </div>
+            <div class="progress-bar-container">
+                <div class="progress-bar" style="width: 10%;">10%</div>
+            </div>
+            <div class="progress-details">
+                <span>开始日期:2026-01-29</span>
+                <span>预计完成:2026-04-09(10周后)</span>
+            </div>
+        </div>
+
+        <!-- 各阶段详情 -->
+        <div class="stages">
+            <h2 style="margin-bottom: 30px; color: #333; text-align: center;">📋 开发阶段详情</h2>
+
+            <!-- 阶段0 -->
+            <div class="stage expanded">
+                <div class="stage-header in-progress" onclick="toggleStage(this)">
+                    <div class="stage-info">
+                        <div class="stage-status status-progress">🚧</div>
+                        <div class="stage-title">阶段0:准备工作</div>
+                    </div>
+                    <div class="stage-meta">
+                        <div class="stage-progress">75%</div>
+                        <div class="stage-time">第1周</div>
+                    </div>
+                </div>
+                <div class="stage-body">
+                    <div class="task completed">
+                        <div class="task-info">
+                            <div class="task-title">✅ 任务0.1:创建项目目录结构</div>
+                            <div class="task-meta">完成时间:0.5天 | 29个代码文件</div>
+                        </div>
+                        <div class="task-status completed">已完成</div>
+                    </div>
+                    <div class="task completed">
+                        <div class="task-info">
+                            <div class="task-title">✅ 任务0.2:定义核心数据模型</div>
+                            <div class="task-meta">完成时间:1天 | 5个模型文件,300+行</div>
+                        </div>
+                        <div class="task-status completed">已完成</div>
+                    </div>
+                    <div class="task in-progress">
+                        <div class="task-info">
+                            <div class="task-title">🚧 任务0.3:创建测试数据和环境</div>
+                            <div class="task-meta">预计时间:0.5天 | 准备5个测试XML文件</div>
+                        </div>
+                        <div class="task-status in-progress">进行中</div>
+                    </div>
+                    <div class="task completed">
+                        <div class="task-info">
+                            <div class="task-title">✅ 任务0.4:创建核心模块框架</div>
+                            <div class="task-meta">完成时间:1天 | 15个模块文件</div>
+                        </div>
+                        <div class="task-status completed">已完成</div>
+                    </div>
+                    <div class="task completed">
+                        <div class="task-info">
+                            <div class="task-title">✅ 文档体系建立</div>
+                            <div class="task-meta">完成时间:2天 | 9个文档,2000+行</div>
+                        </div>
+                        <div class="task-status completed">已完成</div>
+                    </div>
+                </div>
+            </div>
+
+            <!-- 阶段0.5 -->
+            <div class="stage">
+                <div class="stage-header" onclick="toggleStage(this)">
+                    <div class="stage-info">
+                        <div class="stage-status status-pending">⏸️</div>
+                        <div class="stage-title">阶段0.5:规范文档编写 ⭐新增</div>
+                    </div>
+                    <div class="stage-meta">
+                        <div class="stage-progress">0%</div>
+                        <div class="stage-time">第2周</div>
+                    </div>
+                </div>
+                <div class="stage-body">
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">📝 任务0.5.1:MusicXML元素映射规范</div>
+                            <div class="task-meta">预计时间:2天 | divisions、音高、时值转换规则</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">📝 任务0.5.2:简谱渲染规范</div>
+                            <div class="task-meta">预计时间:1天 | 增时线、减时线详细规则</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">📝 任务0.5.3:VexFlow兼容性规范</div>
+                            <div class="task-meta">预计时间:1.5天 | DOM结构、state.times字段清单</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">📝 任务0.5.4:创建测试基准数据</div>
+                            <div class="task-meta">预计时间:1.5天 | 使用旧引擎生成对比基准</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                </div>
+            </div>
+
+            <!-- 阶段1 -->
+            <div class="stage">
+                <div class="stage-header" onclick="toggleStage(this)">
+                    <div class="stage-info">
+                        <div class="stage-status status-pending">⏸️</div>
+                        <div class="stage-title">阶段1:核心解析器</div>
+                    </div>
+                    <div class="stage-meta">
+                        <div class="stage-progress">0%</div>
+                        <div class="stage-time">第3周</div>
+                    </div>
+                </div>
+                <div class="stage-body">
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">🔧 任务1.0:Divisions处理器 ⭐新增</div>
+                            <div class="task-meta">预计时间:0.5天 | MusicXML时值基础</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">🔧 任务1.1:OSMD数据解析器</div>
+                            <div class="task-meta">预计时间:3天 | 解析小节、音符、歌词</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">🔧 任务1.2:时间计算器</div>
+                            <div class="task-meta">预计时间:1天 | 计算音符播放时间</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                </div>
+            </div>
+
+            <!-- 阶段2 -->
+            <div class="stage">
+                <div class="stage-header" onclick="toggleStage(this)">
+                    <div class="stage-info">
+                        <div class="stage-status status-pending">⏸️</div>
+                        <div class="stage-title">阶段2:布局引擎</div>
+                    </div>
+                    <div class="stage-meta">
+                        <div class="stage-progress">0%</div>
+                        <div class="stage-time">第4-5周</div>
+                    </div>
+                </div>
+                <div class="stage-body">
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">📐 任务2.1:小节布局计算器</div>
+                            <div class="task-meta">预计时间:3天 | 固定时间比例算法</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">📐 任务2.2:多声部对齐</div>
+                            <div class="task-meta">预计时间:2天 | 垂直对齐算法</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">📐 任务2.3:行布局和自动换行</div>
+                            <div class="task-meta">预计时间:2天 | 系统布局</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                </div>
+            </div>
+
+            <!-- 阶段3 -->
+            <div class="stage">
+                <div class="stage-header" onclick="toggleStage(this)">
+                    <div class="stage-info">
+                        <div class="stage-status status-pending">⏸️</div>
+                        <div class="stage-title">阶段3:绘制引擎</div>
+                    </div>
+                    <div class="stage-meta">
+                        <div class="stage-progress">0%</div>
+                        <div class="stage-time">第6-7周</div>
+                    </div>
+                </div>
+                <div class="stage-body">
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">🎨 任务3.1:基础音符绘制</div>
+                            <div class="task-meta">预计时间:3天 | 数字、高低音点、附点、升降号</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">🎨 任务3.2:线条绘制 ⭐已细化</div>
+                            <div class="task-meta">预计时间:2.5天 | 增时线、减时线算法</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">🎨 任务3.3:歌词绘制</div>
+                            <div class="task-meta">预计时间:1天 | 歌词位置和样式</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                </div>
+            </div>
+
+            <!-- 阶段4 -->
+            <div class="stage">
+                <div class="stage-header" onclick="toggleStage(this)">
+                    <div class="stage-info">
+                        <div class="stage-status status-pending">⏸️</div>
+                        <div class="stage-title">阶段4:兼容层实现</div>
+                    </div>
+                    <div class="stage-meta">
+                        <div class="stage-progress">0%</div>
+                        <div class="stage-time">第8周</div>
+                    </div>
+                </div>
+                <div class="stage-body">
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">🔗 任务4.0:state.times字段文档 ⭐新增</div>
+                            <div class="task-meta">预计时间:1天 | 30-40个字段完整清单</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">🔗 任务4.1:OSMD兼容适配器</div>
+                            <div class="task-meta">预计时间:3天 | 数据和接口适配</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">🔗 任务4.2:业务层集成</div>
+                            <div class="task-meta">预计时间:1天 | 引擎切换逻辑</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                </div>
+            </div>
+
+            <!-- 阶段5 -->
+            <div class="stage">
+                <div class="stage-header" onclick="toggleStage(this)">
+                    <div class="stage-info">
+                        <div class="stage-status status-pending">⏸️</div>
+                        <div class="stage-title">阶段5:测试与优化</div>
+                    </div>
+                    <div class="stage-meta">
+                        <div class="stage-progress">0%</div>
+                        <div class="stage-time">第9-10周</div>
+                    </div>
+                </div>
+                <div class="stage-body">
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">🧪 任务5.1:功能完整性测试</div>
+                            <div class="task-meta">预计时间:2天 | 所有功能测试</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">🧪 任务5.2:兼容性测试</div>
+                            <div class="task-meta">预计时间:2天 | 多浏览器、多场景</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                    <div class="task pending">
+                        <div class="task-info">
+                            <div class="task-title">🚀 任务5.3:性能优化</div>
+                            <div class="task-meta">预计时间:2天 | 渲染性能提升</div>
+                        </div>
+                        <div class="task-status pending">待开始</div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <!-- 时间线 -->
+        <div class="timeline">
+            <h2 class="timeline-title">⏱️ 开发时间线</h2>
+            <div class="timeline-container">
+                <div class="timeline-line"></div>
+                
+                <div class="timeline-item">
+                    <div class="timeline-dot completed"></div>
+                    <div class="timeline-content">
+                        <div class="timeline-date">2026-01-29 ✅</div>
+                        <div class="timeline-text">
+                            <strong>项目启动</strong><br>
+                            创建项目结构、数据模型、文档体系
+                        </div>
+                    </div>
+                </div>
+
+                <div class="timeline-item">
+                    <div class="timeline-dot in-progress"></div>
+                    <div class="timeline-content">
+                        <div class="timeline-date">2026-02-05 🚧</div>
+                        <div class="timeline-text">
+                            <strong>Checkpoint 0:阶段0完成</strong><br>
+                            测试环境准备完成,进入规范文档编写
+                        </div>
+                    </div>
+                </div>
+
+                <div class="timeline-item">
+                    <div class="timeline-dot"></div>
+                    <div class="timeline-content">
+                        <div class="timeline-date">2026-02-12 ⏸️</div>
+                        <div class="timeline-text">
+                            <strong>Checkpoint 0.5:规范文档完成</strong><br>
+                            MusicXML映射、渲染规范、兼容性规范全部完成
+                        </div>
+                    </div>
+                </div>
+
+                <div class="timeline-item">
+                    <div class="timeline-dot"></div>
+                    <div class="timeline-content">
+                        <div class="timeline-date">2026-02-19 ⏸️</div>
+                        <div class="timeline-text">
+                            <strong>Checkpoint 1:核心解析器完成</strong><br>
+                            OSMD数据解析、时间计算全部完成
+                        </div>
+                    </div>
+                </div>
+
+                <div class="timeline-item">
+                    <div class="timeline-dot"></div>
+                    <div class="timeline-content">
+                        <div class="timeline-date">2026-03-05 ⏸️</div>
+                        <div class="timeline-text">
+                            <strong>Checkpoint 2:布局引擎完成</strong><br>
+                            小节布局、多声部对齐、自动换行全部完成
+                        </div>
+                    </div>
+                </div>
+
+                <div class="timeline-item">
+                    <div class="timeline-dot"></div>
+                    <div class="timeline-content">
+                        <div class="timeline-date">2026-03-19 ⏸️</div>
+                        <div class="timeline-text">
+                            <strong>Checkpoint 3:绘制引擎完成</strong><br>
+                            音符、线条、歌词绘制全部完成
+                        </div>
+                    </div>
+                </div>
+
+                <div class="timeline-item">
+                    <div class="timeline-dot"></div>
+                    <div class="timeline-content">
+                        <div class="timeline-date">2026-03-26 ⏸️</div>
+                        <div class="timeline-text">
+                            <strong>Checkpoint 4:兼容层完成</strong><br>
+                            业务功能全部兼容,可以切换使用
+                        </div>
+                    </div>
+                </div>
+
+                <div class="timeline-item">
+                    <div class="timeline-dot"></div>
+                    <div class="timeline-content">
+                        <div class="timeline-date">2026-04-09 🎯</div>
+                        <div class="timeline-text">
+                            <strong>Checkpoint 5:项目完成!</strong><br>
+                            测试通过,性能优化完成,准备上线
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <!-- 页脚 -->
+        <div class="footer">
+            <p>简谱渲染引擎重写项目 © 2026</p>
+            <div class="footer-links">
+                <a href="./00-START_HERE.md" class="footer-link">📖 开始指南</a>
+                <a href="./01-TASKS_CHECKLIST.md" class="footer-link">📋 任务清单</a>
+                <a href="./02-PROGRESS.md" class="footer-link">📊 进度追踪</a>
+                <a href="./03-MUSICXML_KNOWLEDGE.md" class="footer-link">📚 知识文档</a>
+            </div>
+            <p style="margin-top: 15px; font-size: 0.9em; opacity: 0.8;">
+                最后更新:2026-01-29 | 下次更新:完成任务0.3后
+            </p>
+        </div>
+    </div>
+
+    <script>
+        // 切换阶段展开/折叠
+        function toggleStage(header) {
+            const stage = header.parentElement;
+            stage.classList.toggle('expanded');
+        }
+
+        // 默认展开当前阶段
+        document.addEventListener('DOMContentLoaded', function() {
+            // 展开第一个阶段(阶段0)
+            const firstStage = document.querySelector('.stage');
+            if (firstStage) {
+                firstStage.classList.add('expanded');
+            }
+        });
+
+        // 更新进度百分比(可以通过JavaScript动态更新)
+        function updateProgress(stageIndex, progress) {
+            const stages = document.querySelectorAll('.stage');
+            if (stages[stageIndex]) {
+                const progressEl = stages[stageIndex].querySelector('.stage-progress');
+                progressEl.textContent = progress + '%';
+                
+                // 更新状态图标
+                const statusEl = stages[stageIndex].querySelector('.stage-status');
+                const headerEl = stages[stageIndex].querySelector('.stage-header');
+                
+                if (progress === 100) {
+                    statusEl.textContent = '✅';
+                    statusEl.className = 'stage-status status-completed';
+                    headerEl.classList.remove('in-progress');
+                    headerEl.classList.add('completed');
+                } else if (progress > 0) {
+                    statusEl.textContent = '🚧';
+                    statusEl.className = 'stage-status status-progress';
+                    headerEl.classList.add('in-progress');
+                } else {
+                    statusEl.textContent = '⏸️';
+                    statusEl.className = 'stage-status status-pending';
+                }
+            }
+            
+            // 更新主进度条
+            updateMainProgress();
+        }
+
+        // 更新主进度条
+        function updateMainProgress() {
+            const totalTasks = 60;  // 总任务数
+            const completedTasks = 6;  // 已完成任务数
+            const progress = Math.round((completedTasks / totalTasks) * 100);
+            
+            const progressBar = document.querySelector('.progress-bar');
+            progressBar.style.width = progress + '%';
+            progressBar.textContent = progress + '%';
+            
+            document.querySelector('.stat-value').textContent = progress + '%';
+        }
+
+        // 添加快捷键支持
+        document.addEventListener('keydown', function(e) {
+            // 按'e'键展开所有阶段
+            if (e.key === 'e') {
+                document.querySelectorAll('.stage').forEach(stage => {
+                    stage.classList.add('expanded');
+                });
+            }
+            // 按'c'键折叠所有阶段
+            if (e.key === 'c') {
+                document.querySelectorAll('.stage').forEach(stage => {
+                    stage.classList.remove('expanded');
+                });
+            }
+        });
+
+        console.log('💡 提示:按 E 键展开所有阶段,按 C 键折叠所有阶段');
+    </script>
+</body>
+</html>

+ 154 - 0
src/jianpu-renderer/JianpuRenderer.ts

@@ -0,0 +1,154 @@
+/**
+ * 简谱渲染器主类
+ * 
+ * @description 简谱渲染引擎的核心类,负责协调解析、布局、绘制等模块
+ */
+
+import { OSMDDataParser } from './core/parser/OSMDDataParser';
+import { MeasureLayoutEngine } from './core/layout/MeasureLayoutEngine';
+import { NoteDrawer } from './core/drawer/NoteDrawer';
+import { OSMDCompatibilityAdapter } from './adapters/OSMDCompatibilityAdapter';
+import { JianpuScore, JianpuMeasure, JianpuNote } from './models';
+import { RenderConfig } from './core/config';
+
+export interface JianpuRendererOptions {
+  // 渲染配置
+  quarterNoteSpacing?: number;
+  measurePadding?: number;
+  
+  // 显示配置
+  drawPartNames?: boolean;
+  drawLyrics?: boolean;
+  musicColor?: string;
+}
+
+export class JianpuRenderer {
+  private container: HTMLElement;
+  private options: JianpuRendererOptions;
+  private score: JianpuScore | null = null;
+  private svgElement: SVGSVGElement | null = null;
+  
+  // 子模块
+  private parser: OSMDDataParser;
+  private layoutEngine: MeasureLayoutEngine;
+  private drawer: NoteDrawer;
+  private compatAdapter: OSMDCompatibilityAdapter;
+  
+  constructor(container: HTMLElement | string, options: JianpuRendererOptions = {}) {
+    // 获取容器元素
+    if (typeof container === 'string') {
+      const el = document.getElementById(container);
+      if (!el) throw new Error(`Container element not found: ${container}`);
+      this.container = el;
+    } else {
+      this.container = container;
+    }
+    
+    this.options = {
+      quarterNoteSpacing: 50,
+      measurePadding: 20,
+      ...options,
+    };
+    
+    // 初始化子模块
+    this.parser = new OSMDDataParser();
+    this.layoutEngine = new MeasureLayoutEngine({
+      quarterNoteSpacing: this.options.quarterNoteSpacing!,
+      measurePadding: this.options.measurePadding!,
+    });
+    this.drawer = new NoteDrawer('svg');
+    this.compatAdapter = new OSMDCompatibilityAdapter(this);
+    
+    console.log('[JianpuRenderer] 初始化完成');
+  }
+  
+  /**
+   * 加载MusicXML或OSMD对象
+   */
+  async load(source: string | any): Promise<void> {
+    console.log('[JianpuRenderer] 开始加载数据');
+    
+    // TODO: 实现加载逻辑
+    // 如果source是OSMD对象,调用parser解析
+    // 如果source是XML字符串,先创建OSMD对象再解析
+    
+    throw new Error('load() method not implemented yet');
+  }
+  
+  /**
+   * 渲染简谱
+   */
+  render(): void {
+    if (!this.score) {
+      throw new Error('No score loaded. Call load() first.');
+    }
+    
+    console.log('[JianpuRenderer] 开始渲染');
+    
+    // TODO: 实现渲染逻辑
+    // 1. 创建SVG容器
+    // 2. 调用布局引擎计算位置
+    // 3. 调用绘制引擎绘制元素
+    
+    throw new Error('render() method not implemented yet');
+  }
+  
+  /**
+   * 获取所有音符(用于兼容层)
+   */
+  getAllNotes(): JianpuNote[] {
+    if (!this.score) return [];
+    // TODO: 从score中提取所有音符
+    return [];
+  }
+  
+  /**
+   * 获取所有小节(用于兼容层)
+   */
+  getAllMeasures(): JianpuMeasure[] {
+    if (!this.score) return [];
+    // TODO: 从score中提取所有小节
+    return [];
+  }
+  
+  /**
+   * 获取速度(用于兼容层)
+   */
+  getTempo(): number {
+    return this.score?.tempo || 120;
+  }
+  
+  // ===== OSMD兼容接口 =====
+  
+  /**
+   * 兼容OSMD的cursor接口
+   */
+  get cursor(): any {
+    return this.compatAdapter.createCursorAdapter();
+  }
+  
+  /**
+   * 兼容OSMD的GraphicSheet接口
+   */
+  get GraphicSheet(): any {
+    return this.compatAdapter.createGraphicSheetAdapter();
+  }
+  
+  /**
+   * 兼容OSMD的Sheet接口
+   */
+  get Sheet(): any {
+    return {
+      userStartTempoInBPM: this.getTempo(),
+    };
+  }
+  
+  /**
+   * 兼容OSMD的EngravingRules接口
+   */
+  get EngravingRules(): any {
+    return {
+      DYMusicScoreType: 'jianpu',
+    };
+  }
+}

+ 101 - 0
src/jianpu-renderer/README.md

@@ -0,0 +1,101 @@
+# 简谱渲染引擎
+
+一个专为简谱设计的渲染引擎,支持固定时间比例渲染,完全兼容OpenSheetMusicDisplay的业务接口。
+
+## 🎯 项目目标
+
+- ✅ 完美渲染简谱(数字1-7、高低音点、增时线、减时线)
+- ✅ 固定时间比例布局(同拍号小节宽度一致)
+- ✅ 多声部垂直对齐
+- ✅ 100%兼容现有业务功能(播放高亮、歌词高亮、选区等)
+
+## 📁 项目结构
+
+```
+src/jianpu-renderer/
+├── index.ts                    # 主入口
+├── JianpuRenderer.ts           # 主渲染器类
+├── models/                     # 数据模型
+│   ├── JianpuNote.ts          # 音符模型
+│   ├── JianpuMeasure.ts       # 小节模型
+│   ├── JianpuSystem.ts        # 行模型
+│   └── JianpuScore.ts         # 总谱模型
+├── core/
+│   ├── parser/                # 解析器
+│   │   ├── OSMDDataParser.ts  # OSMD数据解析
+│   │   └── TimeCalculator.ts  # 时间计算
+│   ├── layout/                # 布局引擎
+│   │   ├── MeasureLayoutEngine.ts  # 小节布局
+│   │   ├── SystemLayoutEngine.ts   # 行布局
+│   │   └── MultiVoiceAligner.ts    # 多声部对齐
+│   ├── drawer/                # 绘制引擎
+│   │   ├── NoteDrawer.ts      # 音符绘制
+│   │   ├── LineDrawer.ts      # 线条绘制
+│   │   └── LyricDrawer.ts     # 歌词绘制
+│   └── config/                # 配置
+│       └── RenderConfig.ts    # 渲染配置
+├── adapters/                   # 兼容层
+│   ├── OSMDCompatibilityAdapter.ts  # OSMD兼容适配
+│   └── DOMAdapter.ts          # DOM适配
+└── utils/                      # 工具函数
+    ├── SVGHelper.ts           # SVG工具
+    ├── MathHelper.ts          # 数学工具
+    └── Constants.ts           # 常量定义
+```
+
+## 🚀 使用方法
+
+```typescript
+import { JianpuRenderer } from '@/jianpu-renderer';
+
+// 创建渲染器实例
+const renderer = new JianpuRenderer(container, {
+  quarterNoteSpacing: 50,  // 四分音符间距
+  measurePadding: 20,      // 小节padding
+});
+
+// 加载MusicXML或OSMD对象
+await renderer.load(xmlString);
+
+// 渲染
+renderer.render();
+
+// 兼容OSMD接口
+const times = renderer.generateTimesArray();  // 生成state.times
+const cursor = renderer.cursor;               // 光标接口
+```
+
+## 📝 开发状态
+
+### ✅ 已完成
+- [x] 项目结构搭建
+- [x] 数据模型定义
+- [x] 核心模块框架
+
+### 🚧 进行中
+- [ ] 阶段1:核心解析器
+- [ ] 阶段2:布局引擎
+- [ ] 阶段3:绘制引擎
+- [ ] 阶段4:兼容层
+- [ ] 阶段5:测试与优化
+
+## 📅 开发计划
+
+- 第1周:准备工作 ✅
+- 第2周:核心解析器
+- 第3-4周:布局引擎
+- 第5-6周:绘制引擎
+- 第7周:兼容层实现
+- 第8周:测试与优化
+
+## 🔧 技术栈
+
+- TypeScript
+- SVG (主要渲染方式)
+- Canvas (可选)
+
+## 📖 相关文档
+
+- [完整工作流程](../../JIANPU_DEVELOPMENT_WORKFLOW.md)
+- [进度跟踪](../../JIANPU_DEVELOPMENT_PROGRESS.md)
+- [API文档](./docs/API.md) (待完成)

+ 18 - 0
src/jianpu-renderer/__tests__/setup.test.ts

@@ -0,0 +1,18 @@
+/**
+ * 测试环境设置
+ */
+
+describe('简谱渲染引擎 - 环境测试', () => {
+  it('应该能够导入主模块', () => {
+    expect(() => {
+      require('../index');
+    }).not.toThrow();
+  });
+  
+  it('应该能够创建JianpuRenderer实例', () => {
+    const { JianpuRenderer } = require('../JianpuRenderer');
+    const container = document.createElement('div');
+    const renderer = new JianpuRenderer(container);
+    expect(renderer).toBeDefined();
+  });
+});

+ 17 - 0
src/jianpu-renderer/adapters/DOMAdapter.ts

@@ -0,0 +1,17 @@
+/**
+ * DOM适配器
+ * 
+ * @description 确保生成的DOM结构符合VexFlow规范
+ */
+
+export class DOMAdapter {
+  /**
+   * 在渲染后调整DOM结构,确保兼容性
+   */
+  adaptDOM(container: HTMLElement): void {
+    console.log('[DOMAdapter] 适配DOM结构');
+    
+    // TODO: 实现DOM适配逻辑
+    // 例如:添加VexFlow兼容的ID和类名
+  }
+}

+ 51 - 0
src/jianpu-renderer/adapters/OSMDCompatibilityAdapter.ts

@@ -0,0 +1,51 @@
+/**
+ * OSMD兼容适配器
+ * 
+ * @description 让新引擎完全兼容OSMD的API和数据结构
+ */
+
+import { JianpuRenderer } from '../JianpuRenderer';
+
+export class OSMDCompatibilityAdapter {
+  constructor(private renderer: JianpuRenderer) {}
+  
+  /**
+   * 生成兼容的state.times数组
+   */
+  generateTimesArray(): any[] {
+    console.log('[OSMDCompat] 生成times数组');
+    
+    const times: any[] = [];
+    const notes = this.renderer.getAllNotes();
+    
+    // TODO: 实现times数组生成逻辑
+    
+    return times;
+  }
+  
+  /**
+   * 提供兼容的cursor接口
+   */
+  createCursorAdapter(): any {
+    console.log('[OSMDCompat] 创建cursor适配器');
+    
+    // TODO: 实现cursor接口
+    return {
+      Iterator: {},
+      reset: () => {},
+      next: () => {},
+    };
+  }
+  
+  /**
+   * 提供兼容的GraphicSheet接口
+   */
+  createGraphicSheetAdapter(): any {
+    console.log('[OSMDCompat] 创建GraphicSheet适配器');
+    
+    // TODO: 实现GraphicSheet接口
+    return {
+      MeasureList: [],
+    };
+  }
+}

+ 2 - 0
src/jianpu-renderer/adapters/index.ts

@@ -0,0 +1,2 @@
+export * from './OSMDCompatibilityAdapter';
+export * from './DOMAdapter';

+ 84 - 0
src/jianpu-renderer/core/config/RenderConfig.ts

@@ -0,0 +1,84 @@
+/**
+ * 渲染配置
+ * 
+ * @description 定义简谱渲染的所有配置选项
+ */
+
+export interface RenderConfig {
+  // ===== 布局配置 =====
+  /** 四分音符的基准间距(像素) */
+  quarterNoteSpacing: number;
+  
+  /** 小节左右padding(像素) */
+  measurePadding: number;
+  
+  /** 最小音符间距(像素,防止音符重叠) */
+  minNoteSpacing: number;
+  
+  /** 行宽度(像素) */
+  systemWidth: number;
+  
+  /** 行高度(像素) */
+  systemHeight: number;
+  
+  /** 行间距(像素) */
+  systemSpacing: number;
+  
+  // ===== 字体配置 =====
+  /** 音符字体大小(像素) */
+  noteFontSize: number;
+  
+  /** 歌词字体大小(像素) */
+  lyricFontSize: number;
+  
+  /** 字体族 */
+  fontFamily: string;
+  
+  // ===== 颜色配置 =====
+  /** 音符颜色 */
+  noteColor: string;
+  
+  /** 歌词颜色 */
+  lyricColor: string;
+  
+  /** 线条颜色 */
+  lineColor: string;
+  
+  // ===== 显示配置 =====
+  /** 是否显示歌词 */
+  showLyrics: boolean;
+  
+  /** 是否显示小节号 */
+  showMeasureNumbers: boolean;
+  
+  /** 是否显示声部名称 */
+  showPartNames: boolean;
+}
+
+/**
+ * 默认配置
+ */
+export const DEFAULT_RENDER_CONFIG: RenderConfig = {
+  // 布局
+  quarterNoteSpacing: 50,
+  measurePadding: 20,
+  minNoteSpacing: 10,
+  systemWidth: 800,
+  systemHeight: 150,
+  systemSpacing: 50,
+  
+  // 字体
+  noteFontSize: 20,
+  lyricFontSize: 14,
+  fontFamily: 'Arial, sans-serif',
+  
+  // 颜色
+  noteColor: '#000000',
+  lyricColor: '#000000',
+  lineColor: '#000000',
+  
+  // 显示
+  showLyrics: true,
+  showMeasureNumbers: false,
+  showPartNames: false,
+};

+ 1 - 0
src/jianpu-renderer/core/config/index.ts

@@ -0,0 +1 @@
+export * from './RenderConfig';

+ 35 - 0
src/jianpu-renderer/core/drawer/LineDrawer.ts

@@ -0,0 +1,35 @@
+/**
+ * 线条绘制器
+ * 
+ * @description 绘制增时线、减时线、小节线等
+ */
+
+import { JianpuNote } from '../../models';
+
+export class LineDrawer {
+  /**
+   * 绘制音符的时值线(增时线或减时线)
+   */
+  drawDurationLines(note: JianpuNote, quarterNoteSpacing: number): SVGGElement {
+    console.log(`[LineDrawer] 绘制时值线`);
+    
+    // TODO: 实现线条绘制逻辑
+    const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
+    g.id = `vf-${note.id}-lines`;
+    return g;
+  }
+  
+  /**
+   * 绘制小节线
+   */
+  drawBarline(x: number, y: number, height: number): SVGLineElement {
+    const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
+    line.setAttribute('x1', x.toString());
+    line.setAttribute('y1', y.toString());
+    line.setAttribute('x2', x.toString());
+    line.setAttribute('y2', (y + height).toString());
+    line.setAttribute('stroke', 'black');
+    line.setAttribute('stroke-width', '1');
+    return line;
+  }
+}

+ 29 - 0
src/jianpu-renderer/core/drawer/LyricDrawer.ts

@@ -0,0 +1,29 @@
+/**
+ * 歌词绘制器
+ */
+
+export class LyricDrawer {
+  /**
+   * 绘制歌词
+   */
+  drawLyric(
+    text: string,
+    x: number,
+    y: number,
+    noteId: string,
+    lyricIndex: number
+  ): SVGTextElement {
+    const lyric = document.createElementNS('http://www.w3.org/2000/svg', 'text');
+    
+    // ⭐ 保持VexFlow命名规则
+    lyric.classList.add('vf-lyric', `lyric${noteId}`);
+    lyric.setAttribute('lyricIndex', lyricIndex.toString());
+    
+    lyric.setAttribute('x', x.toString());
+    lyric.setAttribute('y', (y + 35).toString());
+    lyric.setAttribute('font-size', '14');
+    lyric.textContent = text;
+    
+    return lyric;
+  }
+}

+ 23 - 0
src/jianpu-renderer/core/drawer/ModifierDrawer.ts

@@ -0,0 +1,23 @@
+/**
+ * 修饰符绘制器
+ * 
+ * @description 绘制装饰音、连音符标记等
+ */
+
+export class ModifierDrawer {
+  /**
+   * 绘制装饰音
+   */
+  drawGraceNote(x: number, y: number): SVGGElement {
+    // TODO: 实现
+    return document.createElementNS('http://www.w3.org/2000/svg', 'g');
+  }
+  
+  /**
+   * 绘制连音符标记
+   */
+  drawTuplet(x: number, y: number, width: number, number: number): SVGGElement {
+    // TODO: 实现
+    return document.createElementNS('http://www.w3.org/2000/svg', 'g');
+  }
+}

+ 27 - 0
src/jianpu-renderer/core/drawer/NoteDrawer.ts

@@ -0,0 +1,27 @@
+/**
+ * 音符绘制器
+ * 
+ * @description 绘制简谱数字、高低音点、附点、升降号等
+ */
+
+import { JianpuNote } from '../../models';
+
+export class NoteDrawer {
+  private renderMode: 'canvas' | 'svg';
+  
+  constructor(renderMode: 'canvas' | 'svg' = 'svg') {
+    this.renderMode = renderMode;
+  }
+  
+  /**
+   * 绘制单个音符
+   */
+  drawNote(note: JianpuNote): SVGGElement {
+    console.log(`[NoteDrawer] 绘制音符 ${note.pitch}`);
+    
+    // TODO: 实现音符绘制逻辑
+    const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
+    g.id = `vf-${note.id}`;
+    return g;
+  }
+}

+ 4 - 0
src/jianpu-renderer/core/drawer/index.ts

@@ -0,0 +1,4 @@
+export * from './NoteDrawer';
+export * from './LineDrawer';
+export * from './LyricDrawer';
+export * from './ModifierDrawer';

+ 42 - 0
src/jianpu-renderer/core/layout/MeasureLayoutEngine.ts

@@ -0,0 +1,42 @@
+/**
+ * 小节布局引擎
+ * 
+ * @description 计算小节宽度和音符X坐标(固定时间比例算法)
+ */
+
+import { JianpuMeasure } from '../../models';
+
+export interface MeasureLayoutConfig {
+  quarterNoteSpacing: number;
+  measurePadding: number;
+  minNoteSpacing: number;
+}
+
+export class MeasureLayoutEngine {
+  private config: MeasureLayoutConfig;
+  
+  constructor(config: Partial<MeasureLayoutConfig> = {}) {
+    this.config = {
+      quarterNoteSpacing: 50,
+      measurePadding: 20,
+      minNoteSpacing: 10,
+      ...config,
+    };
+  }
+  
+  /**
+   * 计算单个小节的布局
+   */
+  layoutMeasure(measure: JianpuMeasure): void {
+    console.log(`[MeasureLayout] 布局小节 #${measure.measureNumber}`);
+    
+    // TODO: 实现布局计算逻辑
+  }
+  
+  /**
+   * 批量布局多个小节
+   */
+  layoutMeasures(measures: JianpuMeasure[]): void {
+    measures.forEach(measure => this.layoutMeasure(measure));
+  }
+}

+ 20 - 0
src/jianpu-renderer/core/layout/MultiVoiceAligner.ts

@@ -0,0 +1,20 @@
+/**
+ * 多声部对齐器
+ * 
+ * @description 确保多声部在相同时间点垂直对齐
+ */
+
+import { JianpuMeasure } from '../../models';
+
+export class MultiVoiceAligner {
+  /**
+   * 对齐多声部音符
+   */
+  alignVoices(measure: JianpuMeasure): void {
+    if (measure.notes.length <= 1) return;
+    
+    console.log(`[MultiVoiceAlign] 对齐小节 #${measure.measureNumber} 的多声部`);
+    
+    // TODO: 实现多声部对齐逻辑
+  }
+}

+ 15 - 0
src/jianpu-renderer/core/layout/NotePositionCalculator.ts

@@ -0,0 +1,15 @@
+/**
+ * 音符位置计算器
+ */
+
+import { JianpuNote } from '../../models';
+
+export class NotePositionCalculator {
+  /**
+   * 计算音符Y坐标(根据声部)
+   */
+  calculateNoteY(note: JianpuNote, voiceCount: number): number {
+    // TODO: 实现
+    return 0;
+  }
+}

+ 19 - 0
src/jianpu-renderer/core/layout/SystemLayoutEngine.ts

@@ -0,0 +1,19 @@
+/**
+ * 行布局引擎
+ * 
+ * @description 将小节分配到不同的行,支持自动换行
+ */
+
+import { JianpuMeasure, JianpuSystem } from '../../models';
+
+export class SystemLayoutEngine {
+  /**
+   * 将小节分配到行
+   */
+  layoutSystems(measures: JianpuMeasure[]): JianpuSystem[] {
+    console.log('[SystemLayout] 开始行布局');
+    
+    // TODO: 实现行布局逻辑
+    return [];
+  }
+}

+ 4 - 0
src/jianpu-renderer/core/layout/index.ts

@@ -0,0 +1,4 @@
+export * from './MeasureLayoutEngine';
+export * from './SystemLayoutEngine';
+export * from './NotePositionCalculator';
+export * from './MultiVoiceAligner';

+ 54 - 0
src/jianpu-renderer/core/parser/OSMDDataParser.ts

@@ -0,0 +1,54 @@
+/**
+ * OSMD数据解析器
+ * 
+ * @description 从OpenSheetMusicDisplay对象中解析简谱所需的数据
+ */
+
+import { JianpuScore, JianpuMeasure, JianpuNote } from '../../models';
+
+export class OSMDDataParser {
+  /**
+   * 从OSMD对象解析简谱数据
+   * 
+   * @param osmd OpenSheetMusicDisplay实例
+   * @returns 简谱数据结构
+   */
+  parse(osmd: any): JianpuScore {
+    console.log('[OSMDDataParser] 开始解析OSMD数据');
+    
+    // TODO: 实现解析逻辑
+    throw new Error('OSMDDataParser.parse() not implemented yet');
+  }
+  
+  /**
+   * 解析小节信息
+   */
+  private parseMeasures(osmd: any): JianpuMeasure[] {
+    // TODO: 实现
+    return [];
+  }
+  
+  /**
+   * 解析音符信息
+   */
+  private parseNotes(osmd: any, measures: JianpuMeasure[]): JianpuNote[] {
+    // TODO: 实现
+    return [];
+  }
+  
+  /**
+   * 获取简谱音高(1-7)
+   */
+  private getPitchNumber(note: any): number {
+    // TODO: 实现
+    return 1;
+  }
+  
+  /**
+   * 获取八度偏移
+   */
+  private getOctaveOffset(note: any): number {
+    // TODO: 实现
+    return 0;
+  }
+}

+ 22 - 0
src/jianpu-renderer/core/parser/TimeCalculator.ts

@@ -0,0 +1,22 @@
+/**
+ * 时间计算器
+ * 
+ * @description 计算每个音符的绝对播放时间
+ */
+
+import { JianpuNote, JianpuMeasure } from '../../models';
+
+export class TimeCalculator {
+  /**
+   * 计算所有音符的绝对时间
+   */
+  calculateTimes(
+    notes: JianpuNote[],
+    tempo: number,
+    measures: JianpuMeasure[]
+  ): void {
+    console.log('[TimeCalculator] 开始计算音符时间');
+    
+    // TODO: 实现时间计算逻辑
+  }
+}

+ 2 - 0
src/jianpu-renderer/core/parser/index.ts

@@ -0,0 +1,2 @@
+export * from './OSMDDataParser';
+export * from './TimeCalculator';

+ 21 - 0
src/jianpu-renderer/index.ts

@@ -0,0 +1,21 @@
+/**
+ * 简谱渲染引擎 - 主入口文件
+ * 
+ * @description 这是简谱渲染引擎的主入口,导出所有公共API
+ * @author AI Assistant
+ * @date 2026-01-29
+ */
+
+export { JianpuRenderer } from './JianpuRenderer';
+export { OSMDCompatibilityAdapter } from './adapters/OSMDCompatibilityAdapter';
+
+// 导出数据模型
+export type {
+  JianpuNote,
+  JianpuMeasure,
+  JianpuSystem,
+  JianpuScore,
+} from './models';
+
+// 导出配置
+export type { RenderConfig } from './core/config';

+ 76 - 0
src/jianpu-renderer/models/JianpuMeasure.ts

@@ -0,0 +1,76 @@
+/**
+ * 简谱小节数据模型
+ */
+
+import { JianpuNote } from './JianpuNote';
+
+export interface JianpuMeasure {
+  // ===== 基础信息 =====
+  /** 小节索引(0开始,用于内部索引) */
+  index: number;
+  
+  /** 小节号(从1开始,对应XML中的小节号) */
+  measureNumber: number;
+  
+  // ===== 拍号信息 =====
+  timeSignature: {
+    /** 拍数(如4/4中的4) */
+    beats: number;
+    
+    /** 单位拍(如4/4中的4,表示以四分音符为一拍) */
+    beatType: number;
+  };
+  
+  // ===== 调号信息 =====
+  keySignature: {
+    /** 调号(C, G, D, A, E, B, F, Bb, Eb, Ab, Db, Gb, Cb等) */
+    key: string;
+    
+    /** 大小调 */
+    mode: 'major' | 'minor';
+    
+    /** 升降号数量(正数表示升号,负数表示降号) */
+    alterations?: number;
+  };
+  
+  // ===== 音符数据 =====
+  /** 
+   * 所有声部的音符
+   * 二维数组:notes[voiceIndex][noteIndex]
+   * 例如:notes[0] 是第一声部的所有音符
+   */
+  notes: JianpuNote[][];
+  
+  // ===== 布局信息(由布局引擎计算) =====
+  /** 小节起始X坐标(像素) */
+  x: number;
+  
+  /** 小节起始Y坐标(像素) */
+  y: number;
+  
+  /** 小节总宽度(像素,包含padding) */
+  width: number;
+  
+  /** 小节高度(像素) */
+  height: number;
+  
+  // ===== 小节线配置 =====
+  /** 是否显示小节线 */
+  hasBarline: boolean;
+  
+  /** 小节线类型 */
+  barlineType: 'single' | 'double' | 'repeat-start' | 'repeat-end' | 'final';
+  
+  // ===== 其他标记 =====
+  /** 是否显示拍号(通常只在第一小节或拍号变化时显示) */
+  showTimeSignature?: boolean;
+  
+  /** 是否显示调号 */
+  showKeySignature?: boolean;
+  
+  /** 速度标记(BPM) */
+  tempo?: number;
+  
+  /** 速度变化标记(如 Allegro, Andante 等) */
+  tempoText?: string;
+}

+ 91 - 0
src/jianpu-renderer/models/JianpuNote.ts

@@ -0,0 +1,91 @@
+/**
+ * 简谱音符数据模型
+ * 
+ * @description 定义简谱渲染所需的音符数据结构
+ */
+
+export interface JianpuNote {
+  // ===== 基础属性 =====
+  /** 音符唯一ID(用于生成DOM id: vf-{id}) */
+  id: string;
+  
+  /** 音高:1-7表示do re mi fa sol la si,0表示休止符 */
+  pitch: number;
+  
+  /** 八度偏移:0=中音,1=高音,-1=低音,可累加(2=高高音) */
+  octave: number;
+  
+  /** 时值(以四分音符为单位,如0.5=八分音符,1.0=四分音符,2.0=二分音符) */
+  duration: number;
+  
+  // ===== 修饰符 =====
+  /** 升降号 */
+  accidental?: 'sharp' | 'flat' | 'natural';
+  
+  /** 附点数量(0, 1, 2) */
+  dots: number;
+  
+  // ===== 时间信息 =====
+  /** 在小节中的相对时间戳(以四分音符为单位,从0开始) */
+  timestamp: number;
+  
+  /** 绝对开始时间(秒) */
+  startTime: number;
+  
+  /** 绝对结束时间(秒) */
+  endTime: number;
+  
+  // ===== 位置信息(由布局引擎计算) =====
+  /** X坐标(像素) */
+  x: number;
+  
+  /** Y坐标(像素) */
+  y: number;
+  
+  /** 音符占据的宽度(像素) */
+  width: number;
+  
+  /** 音符高度(像素) */
+  height: number;
+  
+  // ===== 声部和小节信息 =====
+  /** 所在声部索引(0开始) */
+  voiceIndex: number;
+  
+  /** 所在小节索引(0开始) */
+  measureIndex: number;
+  
+  // ===== 渲染标记 =====
+  /** 是否是休止符 */
+  isRest: boolean;
+  
+  /** 是否是装饰音 */
+  isGraceNote: boolean;
+  
+  /** 是否是顿音(staccato) */
+  isStaccato: boolean;
+  
+  /** 是否属于连音符组(tuplet) */
+  isPartOfTuplet?: boolean;
+  
+  // ===== OSMD兼容数据(用于业务功能) =====
+  osmdCompatible: {
+    /** 原始OSMD Note对象引用 */
+    noteElement: any;
+    
+    /** SVG元素引用信息 */
+    svgElement: {
+      attrs: { id: string };
+      modifiers?: any[];
+    };
+    
+    /** 半音值(用于MIDI和评测) */
+    halfTone: number;
+    
+    /** 音频频率(Hz,用于评测) */
+    frequency: number;
+    
+    /** 实际音高(用于指法显示) */
+    realKey?: number;
+  };
+}

+ 55 - 0
src/jianpu-renderer/models/JianpuScore.ts

@@ -0,0 +1,55 @@
+/**
+ * 简谱总谱数据模型
+ * 
+ * @description 代表整个简谱曲目的数据
+ */
+
+import { JianpuSystem } from './JianpuSystem';
+import { RenderConfig } from '../core/config/RenderConfig';
+
+export interface JianpuScore {
+  // ===== 基础信息 =====
+  /** 曲目标题 */
+  title?: string;
+  
+  /** 副标题 */
+  subtitle?: string;
+  
+  /** 作曲家 */
+  composer?: string;
+  
+  /** 作词家 */
+  lyricist?: string;
+  
+  // ===== 所有行 =====
+  systems: JianpuSystem[];
+  
+  // ===== 音乐配置 =====
+  /** 速度(BPM) */
+  tempo: number;
+  
+  /** 初始拍号 */
+  initialTimeSignature?: {
+    beats: number;
+    beatType: number;
+  };
+  
+  /** 初始调号 */
+  initialKeySignature?: {
+    key: string;
+    mode: 'major' | 'minor';
+  };
+  
+  // ===== 渲染配置 =====
+  config: RenderConfig;
+  
+  // ===== 元数据 =====
+  /** 总小节数 */
+  totalMeasures?: number;
+  
+  /** 总时长(秒) */
+  duration?: number;
+  
+  /** 声部数量 */
+  voiceCount?: number;
+}

+ 36 - 0
src/jianpu-renderer/models/JianpuSystem.ts

@@ -0,0 +1,36 @@
+/**
+ * 简谱行(System)数据模型
+ * 
+ * @description 一个System代表曲谱的一行,包含多个小节
+ */
+
+import { JianpuMeasure } from './JianpuMeasure';
+
+export interface JianpuSystem {
+  // ===== 基础信息 =====
+  /** 行索引(0开始) */
+  index: number;
+  
+  /** 该行包含的所有小节 */
+  measures: JianpuMeasure[];
+  
+  // ===== 布局信息 =====
+  /** 行起始X坐标(通常为0或页边距) */
+  x: number;
+  
+  /** 行起始Y坐标 */
+  y: number;
+  
+  /** 行宽度 */
+  width: number;
+  
+  /** 行高度 */
+  height: number;
+  
+  // ===== 显示配置 =====
+  /** 是否显示声部名称(通常只在第一行显示) */
+  showPartNames?: boolean;
+  
+  /** 是否显示谱号(通常只在第一行显示) */
+  showClef?: boolean;
+}

+ 8 - 0
src/jianpu-renderer/models/index.ts

@@ -0,0 +1,8 @@
+/**
+ * 数据模型导出文件
+ */
+
+export * from './JianpuNote';
+export * from './JianpuMeasure';
+export * from './JianpuSystem';
+export * from './JianpuScore';

+ 46 - 0
src/jianpu-renderer/utils/Constants.ts

@@ -0,0 +1,46 @@
+/**
+ * 常量定义
+ */
+
+export const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
+
+/**
+ * VexFlow兼容的CSS类名
+ */
+export const VEXFLOW_CSS_CLASSES = {
+  NOTE: 'vf-note',
+  NUMBERED_NOTE_HEAD: 'vf-numbered-note-head',
+  LYRIC: 'vf-lyric',
+  BEAM: 'vf-beam',
+  CUSTOM_BG: 'vf-custom-bg',
+  BARLINE: 'vf-barline',
+  DURATION_LINE: 'vf-duration-line',
+  REST: 'vf-rest',
+  STEM: 'vf-stem',
+} as const;
+
+/**
+ * 音高映射(简谱数字到音名)
+ */
+export const PITCH_MAP: { [key: number]: string } = {
+  1: 'C',
+  2: 'D',
+  3: 'E',
+  4: 'F',
+  5: 'G',
+  6: 'A',
+  7: 'B',
+};
+
+/**
+ * 音名到简谱数字映射
+ */
+export const NOTE_NAME_TO_PITCH: { [key: string]: number } = {
+  'C': 1,
+  'D': 2,
+  'E': 3,
+  'F': 4,
+  'G': 5,
+  'A': 6,
+  'B': 7,
+};

+ 27 - 0
src/jianpu-renderer/utils/MathHelper.ts

@@ -0,0 +1,27 @@
+/**
+ * 数学辅助函数
+ */
+
+export class MathHelper {
+  /**
+   * 保留指定小数位数
+   */
+  static round(value: number, decimals: number = 2): number {
+    const factor = Math.pow(10, decimals);
+    return Math.round(value * factor) / factor;
+  }
+  
+  /**
+   * 线性插值
+   */
+  static lerp(start: number, end: number, t: number): number {
+    return start + (end - start) * t;
+  }
+  
+  /**
+   * 限制值在范围内
+   */
+  static clamp(value: number, min: number, max: number): number {
+    return Math.min(Math.max(value, min), max);
+  }
+}

+ 41 - 0
src/jianpu-renderer/utils/SVGHelper.ts

@@ -0,0 +1,41 @@
+/**
+ * SVG辅助函数
+ */
+
+export class SVGHelper {
+  private static readonly SVG_NS = 'http://www.w3.org/2000/svg';
+  
+  /**
+   * 创建SVG元素
+   */
+  static createElement(tag: string): SVGElement {
+    return document.createElementNS(SVGHelper.SVG_NS, tag);
+  }
+  
+  /**
+   * 创建SVG组
+   */
+  static createGroup(id?: string, classes?: string[]): SVGGElement {
+    const g = SVGHelper.createElement('g') as SVGGElement;
+    if (id) g.id = id;
+    if (classes) classes.forEach(cls => g.classList.add(cls));
+    return g;
+  }
+  
+  /**
+   * 创建SVG文本
+   */
+  static createText(
+    x: number,
+    y: number,
+    content: string,
+    fontSize: number = 14
+  ): SVGTextElement {
+    const text = SVGHelper.createElement('text') as SVGTextElement;
+    text.setAttribute('x', x.toString());
+    text.setAttribute('y', y.toString());
+    text.setAttribute('font-size', fontSize.toString());
+    text.textContent = content;
+    return text;
+  }
+}

+ 3 - 0
src/jianpu-renderer/utils/index.ts

@@ -0,0 +1,3 @@
+export * from './SVGHelper';
+export * from './MathHelper';
+export * from './Constants';